don't alias constant expressions

gh-17012
ComputerGuy 23 hours ago
parent 1e55c1f11e
commit 5b5e9efb92

@ -5,6 +5,7 @@ import * as b from '#compiler/builders';
import { dev } from '../../../../state.js';
import { get_parent } from '../../../../utils/ast.js';
import { get_name } from '../../../nodes.js';
import { is_constant } from '../../../scope.js';
/**
* @param {ClassBody} node
@ -61,6 +62,20 @@ export function ClassBody(node, context) {
);
continue;
}
if (is_constant(computed_key, context.state.scope)) {
body.push(
b.prop_def(field.key, null),
b.method('get', computed_key, [], [b.return(b.call('$.get', member))], true),
b.method(
'set',
computed_key,
[b.id('value')],
[b.stmt(b.call('$.set', member, b.id('value'), should_proxy && b.true))],
true
)
);
continue;
}
const key = context.state.scope.generate('key');
computed_field_declarations.push(b.let(key));
@ -163,6 +178,42 @@ export function ClassBody(node, context) {
b.literal(`${declaration.id?.name ?? '[class]'}[computed key]`)
);
}
const computed_key = /** @type {Expression} */ (context.visit(field.computed_key));
const evaluation = context.state.scope.evaluate(computed_key);
if (evaluation.is_known) {
body.push(
b.prop_def(field.key, call),
b.method(
'get',
b.literal(evaluation.value),
[],
[b.return(b.call('$.get', member))],
true
),
b.method(
'set',
b.literal(evaluation.value),
[b.id('value')],
[b.stmt(b.call('$.set', member, b.id('value'), should_proxy && b.true))],
true
)
);
continue;
}
if (is_constant(computed_key, context.state.scope)) {
body.push(
b.prop_def(field.key, call),
b.method('get', computed_key, [], [b.return(b.call('$.get', member))], true),
b.method(
'set',
computed_key,
[b.id('value')],
[b.stmt(b.call('$.set', member, b.id('value'), should_proxy && b.true))],
true
)
);
continue;
}
const key = context.state.scope.generate('key');
computed_field_declarations.push(b.let(key));
@ -170,11 +221,7 @@ export function ClassBody(node, context) {
b.prop_def(field.key, call),
b.method(
'get',
b.assignment(
'=',
b.id(key),
/** @type {Expression} */ (context.visit(field.computed_key))
),
b.assignment('=', b.id(key), computed_key),
[],
[b.return(b.call('$.get', member))],
true

@ -2,6 +2,7 @@
/** @import { Context } from '../types.js' */
import * as b from '#compiler/builders';
import { get_name } from '../../../nodes.js';
import { is_constant } from '../../../scope.js';
/**
* @param {ClassBody} node
@ -34,17 +35,43 @@ export function ClassBody(node, context) {
const member = b.member(b.this, field.key);
if (typeof name !== 'string' && field.computed_key) {
const computed_key = /** @type {Expression} */ (context.visit(field.computed_key));
const evaluation = context.state.scope.evaluate(computed_key);
if (evaluation.is_known) {
body.push(
b.prop_def(field.key, null),
b.method('get', b.literal(evaluation.value), [], [b.return(b.call(member))], true),
b.method(
'set',
b.literal(evaluation.value),
[b.id('$$value')],
[b.return(b.call(member, b.id('$$value')))],
true
)
);
continue;
}
if (is_constant(computed_key, context.state.scope)) {
body.push(
b.prop_def(field.key, null),
b.method('get', computed_key, [], [b.return(b.call(member))], true),
b.method(
'set',
computed_key,
[b.id('$$value')],
[b.return(b.call(member, b.id('$$value')))],
true
)
);
continue;
}
const key = context.state.scope.generate('key');
computed_field_declarations.push(b.let(key));
body.push(
b.prop_def(field.key, null),
b.method(
'get',
b.assignment(
'=',
b.id(key),
/** @type {Expression} */ (context.visit(field.computed_key))
),
b.assignment('=', b.id(key), computed_key),
[],
[b.return(b.call(member))],
true
@ -133,6 +160,23 @@ export function ClassBody(node, context) {
);
continue;
}
if (is_constant(computed_key, context.state.scope)) {
body.push(
b.prop_def(
field.key,
/** @type {CallExpression} */ (context.visit(field.value, child_state))
),
b.method('get', computed_key, [], [b.return(b.call(member))], true),
b.method(
'set',
computed_key,
[b.id('$$value')],
[b.return(b.call(member, b.id('$$value')))],
true
)
);
continue;
}
const key = context.state.scope.generate('key');
computed_field_declarations.push(b.let(key));
body.push(
@ -142,11 +186,7 @@ export function ClassBody(node, context) {
),
b.method(
'get',
b.assignment(
'=',
b.id(key),
/** @type {Expression} */ (context.visit(field.computed_key))
),
b.assignment('=', b.id(key), computed_key),
[],
[b.return(b.call(member))],
true

@ -1358,6 +1358,70 @@ export function get_rune(node, scope) {
return keypath;
}
/**
* @param {Expression} expression
* @param {Scope} scope
*/
export function is_constant(expression, scope) {
const evaluation = scope.evaluate(expression);
if (evaluation.is_known) {
return true;
}
let constant = true;
walk(/** @type {Node} */ (expression), null, {
Identifier(node, { path, stop }) {
if (is_reference(node, /** @type {Node} */ (path.at(-1)))) {
const binding = scope.get(node.name);
if (!binding || binding.reassigned) {
constant = false;
stop();
return;
}
}
},
ArrowFunctionExpression(_, { stop }) {
constant = false;
stop();
},
FunctionExpression(_, { stop }) {
constant = false;
stop();
},
ClassExpression(_, { stop }) {
constant = false;
stop();
},
MemberExpression(_, { stop }) {
constant = false;
stop();
},
CallExpression(node, { stop }) {
if (scope.evaluate(node).is_known) {
return;
}
constant = false;
stop();
},
UpdateExpression(_, { stop }) {
constant = false;
stop();
},
AssignmentExpression(_, { stop }) {
constant = false;
stop();
},
UnaryExpression(node, { next, stop }) {
if (node.operator === 'delete') {
constant = false;
stop();
return;
}
next();
}
});
return constant;
}
/**
* Returns the name of the rune if the given expression is a `CallExpression` using a rune.
* @param {Expression | Super} node

Loading…
Cancel
Save