Skip to content

Commit e66416b

Browse files
authored
feat: more efficient code generation when referencing globals (#12712)
* feat: more efficient code generation when referencing globals * update test
1 parent 93cfa6c commit e66416b

File tree

9 files changed

+83
-23
lines changed

9 files changed

+83
-23
lines changed

.changeset/red-kings-draw.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'svelte': patch
3+
---
4+
5+
feat: more efficient code generation when referencing globals

packages/svelte/src/compiler/phases/2-analyze/visitors/CallExpression.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
import { get_rune } from '../../scope.js';
55
import * as e from '../../../errors.js';
66
import { get_parent, unwrap_optional } from '../../../utils/ast.js';
7-
import { is_known_safe_call, is_safe_identifier } from './shared/utils.js';
7+
import { is_pure, is_safe_identifier } from './shared/utils.js';
88

99
/**
1010
* @param {CallExpression} node
@@ -150,7 +150,7 @@ export function CallExpression(node, context) {
150150
break;
151151
}
152152

153-
if (context.state.expression && !is_known_safe_call(node.callee, context)) {
153+
if (context.state.expression && !is_pure(node.callee, context)) {
154154
context.state.expression.has_call = true;
155155
context.state.expression.has_state = true;
156156
}

packages/svelte/src/compiler/phases/2-analyze/visitors/MemberExpression.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/** @import { MemberExpression } from 'estree' */
22
/** @import { Context } from '../types' */
33
import * as e from '../../../errors.js';
4-
import { is_safe_identifier } from './shared/utils.js';
4+
import { is_pure, is_safe_identifier } from './shared/utils.js';
55

66
/**
77
* @param {MemberExpression} node
@@ -15,7 +15,7 @@ export function MemberExpression(node, context) {
1515
}
1616
}
1717

18-
if (context.state.expression) {
18+
if (context.state.expression && !is_pure(node, context)) {
1919
context.state.expression.has_state = true;
2020
}
2121

packages/svelte/src/compiler/phases/2-analyze/visitors/TaggedTemplateExpression.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
/** @import { TaggedTemplateExpression, VariableDeclarator } from 'estree' */
22
/** @import { Context } from '../types' */
3-
import { is_known_safe_call } from './shared/utils.js';
3+
import { is_pure } from './shared/utils.js';
44

55
/**
66
* @param {TaggedTemplateExpression} node
77
* @param {Context} context
88
*/
99
export function TaggedTemplateExpression(node, context) {
10-
if (context.state.expression && !is_known_safe_call(node.tag, context)) {
10+
if (context.state.expression && !is_pure(node.tag, context)) {
1111
context.state.expression.has_call = true;
1212
context.state.expression.has_state = true;
1313
}

packages/svelte/src/compiler/phases/2-analyze/visitors/shared/utils.js

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
/** @import { Scope } from '../../../scope' */
55
/** @import { NodeLike } from '../../../../errors.js' */
66
import * as e from '../../../../errors.js';
7-
import { extract_identifiers } from '../../../../utils/ast.js';
7+
import { extract_identifiers, object } from '../../../../utils/ast.js';
88
import * as w from '../../../../warnings.js';
99

1010
/**
@@ -167,24 +167,23 @@ export function is_safe_identifier(expression, scope) {
167167
}
168168

169169
/**
170-
* @param {Expression | Super} callee
170+
* @param {Expression | Super} node
171171
* @param {Context} context
172172
* @returns {boolean}
173173
*/
174-
export function is_known_safe_call(callee, context) {
175-
// String / Number / BigInt / Boolean casting calls
176-
if (callee.type === 'Identifier') {
177-
const name = callee.name;
178-
const binding = context.state.scope.get(name);
179-
if (
180-
binding === null &&
181-
(name === 'BigInt' || name === 'String' || name === 'Number' || name === 'Boolean')
182-
) {
183-
return true;
184-
}
174+
export function is_pure(node, context) {
175+
if (node.type !== 'Identifier' && node.type !== 'MemberExpression') {
176+
return false;
185177
}
186178

187-
// TODO add more cases
179+
const left = object(node);
180+
if (!left) return false;
181+
182+
if (left.type === 'Identifier') {
183+
const binding = context.state.scope.get(left.name);
184+
if (binding === null) return true; // globals are assumed to be safe
185+
}
188186

187+
// TODO add more cases (safe Svelte imports, etc)
189188
return false;
190189
}
Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<script>
2-
import {untrack} from 'svelte';
2+
import { untrack } from 'svelte';
33
44
const foo = $effect.tracking();
55
let bar = $state(false);
@@ -10,5 +10,5 @@
1010

1111
<p>{foo}</p>
1212
<p>{bar}</p>
13-
<p>{$effect.tracking()}</p>
14-
<p>{untrack(() => $effect.tracking())}</p>
13+
<p>{(bar, $effect.tracking())}</p>
14+
<p>{untrack(() => (bar, $effect.tracking()))}</p>
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import "svelte/internal/disclose-version";
2+
import * as $ from "svelte/internal/client";
3+
4+
var root = $.template(`<p> </p> <p> </p> <!>`, 1);
5+
6+
export default function Purity($$anchor) {
7+
let min = 0;
8+
let max = 100;
9+
let number = 50;
10+
let value = 'hello';
11+
var fragment = root();
12+
var p = $.first_child(fragment);
13+
var text = $.child(p);
14+
15+
text.nodeValue = Math.max(min, Math.min(max, number));
16+
$.reset(p);
17+
18+
var p_1 = $.sibling($.sibling(p, true));
19+
var text_1 = $.child(p_1);
20+
21+
text_1.nodeValue = location.href;
22+
$.reset(p_1);
23+
24+
var node = $.sibling($.sibling(p_1, true));
25+
26+
Child(node, {
27+
prop: encodeURIComponent(value),
28+
$$legacy: true
29+
});
30+
31+
$.append($$anchor, fragment);
32+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import * as $ from "svelte/internal/server";
2+
3+
export default function Purity($$payload) {
4+
let min = 0;
5+
let max = 100;
6+
let number = 50;
7+
let value = 'hello';
8+
9+
$$payload.out += `<p>${$.escape(Math.max(min, Math.min(max, number)))}</p> <p>${$.escape(location.href)}</p> `;
10+
Child($$payload, { prop: encodeURIComponent(value) });
11+
$$payload.out += `<!---->`;
12+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<script>
2+
let min = 0;
3+
let max = 100;
4+
let number = 50;
5+
6+
let value = 'hello';
7+
</script>
8+
9+
<p>{Math.max(min, Math.min(max, number))}</p>
10+
<p>{location.href}</p>
11+
12+
<Child prop={encodeURIComponent(value)} />

0 commit comments

Comments
 (0)