bail out pure expressions

pull/16073/head
7nik 4 months ago
parent 1f51993b93
commit bcb6b72af2

@ -1,4 +1,4 @@
/** @import { AssignmentExpression, Expression, ExpressionStatement, Identifier, MemberExpression, SequenceExpression, Literal, Super, UpdateExpression } from 'estree' */
/** @import { AssignmentExpression, Expression, ExpressionStatement, Identifier, MemberExpression, SequenceExpression, Literal, Super, UpdateExpression, Pattern } from 'estree' */
/** @import { AST, ExpressionMetadata } from '#compiler' */
/** @import { ComponentClientTransformState, ComponentContext, Context } from '../../types' */
import { walk } from 'zimmerframe';
@ -361,6 +361,54 @@ export function validate_mutation(node, context, expression) {
);
}
/**
* Checks whether the expression contains assignments, function calls, or member accesses
* @param {Expression|Pattern} expression
* @returns {boolean}
*/
function is_pure_expression(expression) {
// It's supposed that values do not have custom @@toPrimitive() or toString(),
// which may be implicitly called in expressions like `a + b`, `a & b`, `+a`, `str: ${a}`
switch (expression.type) {
case "ArrayExpression": return expression.elements.every((element) => element == null || (element.type === "SpreadElement" ? false : is_pure_expression(element)));
case "BinaryExpression": return expression.left.type !== "PrivateIdentifier" && is_pure_expression(expression.left) && is_pure_expression(expression.right);
case "ConditionalExpression": return is_pure_expression(expression.test) && is_pure_expression(expression.consequent) && is_pure_expression(expression.alternate);
case "Identifier": return true;
case "Literal": return true;
case "LogicalExpression": return is_pure_expression(expression.left) && is_pure_expression(expression.right);
case "MetaProperty": return true; // new.target
case "ObjectExpression": return expression.properties.every((property) =>
property.type !== "SpreadElement"
&& property.key.type !== "PrivateIdentifier"
&& is_pure_expression(property.key)
&& is_pure_expression(property.value)
);
case "SequenceExpression": return expression.expressions.every(is_pure_expression);
case "TemplateLiteral": return expression.expressions.every(is_pure_expression);
case "ThisExpression": return true;
case "UnaryExpression": return is_pure_expression(expression.argument);
case "YieldExpression": return expression.argument == null || is_pure_expression(expression.argument);
case "ArrayPattern":
case "ArrowFunctionExpression":
case "AssignmentExpression":
case "AssignmentPattern":
case "AwaitExpression":
case "CallExpression":
case "ChainExpression":
case "ClassExpression":
case "FunctionExpression":
case "ImportExpression":
case "MemberExpression":
case "NewExpression":
case "ObjectPattern":
case "RestElement":
case "TaggedTemplateExpression":
case "UpdateExpression":
return false;
}
}
/**
* Serializes an expression with reactivity like in Svelte 4
* @param {Expression} expression
@ -370,7 +418,7 @@ export function build_legacy_expression(expression, context) {
// To recreate Svelte 4 behaviour, we track the dependencies
// the compiler can 'see', but we untrack the effect itself
const serialized_expression = /** @type {Expression} */ (context.visit(expression));
if (expression.type === "Identifier") return serialized_expression;
if (is_pure_expression(expression)) return serialized_expression;
/** @type {Expression[]} */
const sequence = [];

@ -8,7 +8,7 @@ export default function Each_index_non_null($$anchor) {
var fragment = $.comment();
var node = $.first_child(fragment);
$.each(node, 0, () => Array(10), $.index, ($$anchor, $$item, i) => {
$.each(node, 0, () => [,,,,,], $.index, ($$anchor, $$item, i) => {
var p = root_1();
p.textContent = `index: ${i}`;

@ -1,7 +1,7 @@
import * as $ from 'svelte/internal/server';
export default function Each_index_non_null($$payload) {
const each_array = $.ensure_array_like(Array(10));
const each_array = $.ensure_array_like([,,,,,]);
$$payload.out += `<!--[-->`;

@ -1,3 +1,3 @@
{#each Array(10), i}
{#each [,,,,,], i}
<p>index: {i}</p>
{/each}

Loading…
Cancel
Save