Skip to content

Commit 32ed9d5

Browse files
committed
add to server, add stack overflow guard
1 parent 1021f0d commit 32ed9d5

File tree

5 files changed

+245
-204
lines changed

5 files changed

+245
-204
lines changed

packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/element.js

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,8 @@ import { is_ignored } from '../../../../../state.js';
66
import { is_event_attribute } from '../../../../../utils/ast.js';
77
import * as b from '../../../../../utils/builders.js';
88
import { build_getter } from '../../utils.js';
9-
import {
10-
build_template_chunk,
11-
get_expression_id,
12-
DYNAMIC,
13-
evaluate_static_expression
14-
} from './utils.js';
9+
import { build_template_chunk, get_expression_id } from './utils.js';
10+
import { evaluate_static_expression, DYNAMIC } from '../../../shared/static-evaluation.js';
1511

1612
/**
1713
* @param {Array<AST.Attribute | AST.SpreadAttribute>} attributes

packages/svelte/src/compiler/phases/3-transform/client/visitors/shared/utils.js

Lines changed: 2 additions & 196 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/** @import { Expression, ExpressionStatement, Identifier, MemberExpression, SequenceExpression, Statement, Super, Node, UnaryExpression, TemplateLiteral, BinaryExpression, LogicalExpression, ConditionalExpression } from 'estree' */
1+
/** @import { Expression, ExpressionStatement, Identifier, MemberExpression, SequenceExpression, Statement, Super } from 'estree' */
22
/** @import { AST, ExpressionMetadata } from '#compiler' */
33
/** @import { ComponentClientTransformState } from '../../types' */
44
import { walk } from 'zimmerframe';
@@ -9,6 +9,7 @@ import { regex_is_valid_identifier } from '../../../../patterns.js';
99
import is_reference from 'is-reference';
1010
import { locator } from '../../../../../state.js';
1111
import { create_derived } from '../../utils.js';
12+
import { evaluate_static_expression, DYNAMIC } from '../../../shared/static-evaluation.js';
1213

1314
/**
1415
* @param {ComponentClientTransformState} state
@@ -74,201 +75,6 @@ function compare_expressions(a, b) {
7475

7576
return true;
7677
}
77-
export const DYNAMIC = Symbol('DYNAMIC');
78-
79-
/**
80-
* @param {Node} node
81-
* @param {ComponentClientTransformState} state
82-
* @returns {any}
83-
*/
84-
export function evaluate_static_expression(node, state) {
85-
if (node == undefined) return DYNAMIC;
86-
/**
87-
* @param {BinaryExpression | LogicalExpression} node
88-
*/
89-
function handle_left_right(node) {
90-
const left = evaluate_static_expression(node?.left, state);
91-
const right = evaluate_static_expression(node?.right, state);
92-
if (left === DYNAMIC || right === DYNAMIC) {
93-
return DYNAMIC;
94-
}
95-
switch (node.operator) {
96-
case '+':
97-
return left + right;
98-
case '-':
99-
return left - right;
100-
case '&':
101-
return left & right;
102-
case '|':
103-
return left | right;
104-
case '<<':
105-
return left << right;
106-
case '>>':
107-
return left >> right;
108-
case '>':
109-
return left > right;
110-
case '<':
111-
return left < right;
112-
case '>=':
113-
return left >= right;
114-
case '<=':
115-
return left <= right;
116-
case '==':
117-
return left == right;
118-
case '===':
119-
return left === right;
120-
case '||':
121-
return left || right;
122-
case '??':
123-
return left ?? right;
124-
case '&&':
125-
return left && right;
126-
case '%':
127-
return left % right;
128-
case '>>>':
129-
return left >>> right;
130-
case '^':
131-
return left ^ right;
132-
case '**':
133-
return left ** right;
134-
case '*':
135-
return left * right;
136-
case '/':
137-
return left / right;
138-
case '!=':
139-
return left != right;
140-
case '!==':
141-
return left !== right;
142-
default:
143-
return DYNAMIC;
144-
}
145-
}
146-
/**
147-
* @param {UnaryExpression} node
148-
*/
149-
function handle_unary(node) {
150-
const argument = evaluate_static_expression(node?.argument, state);
151-
if (argument === DYNAMIC) return DYNAMIC;
152-
/**
153-
* @param {Expression} argument
154-
*/
155-
function handle_void(argument) {
156-
//@ts-ignore
157-
const evaluated = evaluate_static_expression(argument);
158-
if (evaluated !== DYNAMIC) {
159-
return undefined;
160-
}
161-
return DYNAMIC;
162-
}
163-
switch (node.operator) {
164-
case '!':
165-
return !argument;
166-
case '-':
167-
return -argument;
168-
case 'typeof':
169-
return typeof argument;
170-
case '~':
171-
return ~argument;
172-
case '+':
173-
return +argument;
174-
case 'void':
175-
return handle_void(argument);
176-
default:
177-
// `delete` is ignored, since it may have side effects
178-
return DYNAMIC;
179-
}
180-
}
181-
/**
182-
* @param {SequenceExpression} node
183-
*/
184-
function handle_sequence(node) {
185-
const is_static = node.expressions.reduce(
186-
(a, b) => a && evaluate_static_expression(b, state) !== DYNAMIC,
187-
true
188-
);
189-
if (is_static) {
190-
//@ts-ignore
191-
return evaluate_static_expression(node.expressions.at(-1), state);
192-
}
193-
return DYNAMIC;
194-
}
195-
/**
196-
* @param {string} name
197-
*/
198-
function handle_ident(name) {
199-
const scope = state.scope.get(name);
200-
if (scope?.kind === 'normal' && scope?.declaration_kind !== 'import') {
201-
if (scope.initial && !scope.mutated && !scope.reassigned && !scope.updated) {
202-
//@ts-ignore
203-
let evaluated = evaluate_static_expression(scope.initial, state);
204-
return evaluated;
205-
}
206-
}
207-
return DYNAMIC;
208-
}
209-
/**
210-
* @param {TemplateLiteral} node
211-
*/
212-
function handle_template(node) {
213-
const expressions = node.expressions;
214-
const quasis = node.quasis;
215-
const is_static = expressions.reduce(
216-
(a, b) => a && evaluate_static_expression(b, state) !== DYNAMIC,
217-
true
218-
);
219-
if (is_static) {
220-
let res = '';
221-
let last_was_quasi = false;
222-
let expr_index = 0;
223-
let quasi_index = 0;
224-
for (let index = 0; index < quasis.length + expressions.length; index++) {
225-
if (last_was_quasi) {
226-
res += evaluate_static_expression(expressions[expr_index++], state);
227-
last_was_quasi = false;
228-
} else {
229-
res += quasis[quasi_index++].value.cooked;
230-
last_was_quasi = true;
231-
}
232-
}
233-
return res;
234-
}
235-
return DYNAMIC;
236-
}
237-
/**
238-
* @param {ConditionalExpression} node
239-
*/
240-
function handle_ternary(node) {
241-
const test = evaluate_static_expression(node.test, state);
242-
if (test !== DYNAMIC) {
243-
if (test) {
244-
return evaluate_static_expression(node.consequent, state);
245-
} else {
246-
return evaluate_static_expression(node.alternate, state);
247-
}
248-
}
249-
return DYNAMIC;
250-
}
251-
switch (node.type) {
252-
case 'Literal':
253-
return node.value;
254-
case 'BinaryExpression':
255-
return handle_left_right(node);
256-
case 'LogicalExpression':
257-
return handle_left_right(node);
258-
case 'UnaryExpression':
259-
return handle_unary(node);
260-
case 'Identifier':
261-
return handle_ident(node.name);
262-
case 'SequenceExpression':
263-
return handle_sequence(node);
264-
case 'TemplateLiteral':
265-
return handle_template(node);
266-
case 'ConditionalExpression':
267-
return handle_ternary(node);
268-
default:
269-
return DYNAMIC;
270-
}
271-
}
27278

27379
/**
27480
* @param {Array<AST.Text | AST.ExpressionTag>} values

packages/svelte/src/compiler/phases/3-transform/server/visitors/shared/utils.js

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {
1111
import * as b from '../../../../../utils/builders.js';
1212
import { sanitize_template_string } from '../../../../../utils/sanitize_template_string.js';
1313
import { regex_whitespaces_strict } from '../../../../patterns.js';
14+
import { evaluate_static_expression, DYNAMIC } from '../../../shared/static-evaluation.js';
1415

1516
/** Opens an if/each block, so that we can remove nodes in the case of a mismatch */
1617
export const block_open = b.literal(BLOCK_OPEN);
@@ -49,7 +50,15 @@ export function process_children(nodes, { visit, state }) {
4950
quasi.value.cooked += escape_html(node.expression.value + '');
5051
}
5152
} else {
52-
expressions.push(b.call('$.escape', /** @type {Expression} */ (visit(node.expression))));
53+
let evaluated = evaluate_static_expression(node.expression, state, true);
54+
if (evaluated === DYNAMIC) {
55+
expressions.push(b.call('$.escape', /** @type {Expression} */ (visit(node.expression))));
56+
} else {
57+
if (evaluated != null) {
58+
quasi.value.cooked += escape_html(evaluated + '');
59+
}
60+
continue;
61+
}
5362

5463
quasi = b.quasi('', i + 1 === sequence.length);
5564
quasis.push(quasi);

0 commit comments

Comments
 (0)