Skip to content

Commit dda54a7

Browse files
dummdidummmilahuSimonbenmccann
authored andcommitted
[fix] prevent maximum call stack size exceeded error on large pages (sveltejs#7203)
Co-authored-by: milahu <[email protected]> Co-authored-by: Simon <[email protected]> Co-authored-by: Ben McCann <[email protected]>
1 parent bfa2a06 commit dda54a7

File tree

10 files changed

+39
-26
lines changed

10 files changed

+39
-26
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# Svelte changelog
22

3+
## Unreleased
4+
5+
* Avoid `maximum call stack size exceeded` errors on large components ([#4694](https://github.com/sveltejs/svelte/issues/4694))
6+
37
## 3.46.3
48

59
* Ignore whitespace in `{#each}` blocks when containing elements with `animate:` ([#5477](https://github.com/sveltejs/svelte/pull/5477))

package-lock.json

Lines changed: 7 additions & 8 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@
127127
"@typescript-eslint/parser": "^4.31.2",
128128
"acorn": "^8.4.1",
129129
"agadoo": "^1.1.0",
130-
"code-red": "^0.2.4",
130+
"code-red": "^0.2.5",
131131
"css-tree": "^1.1.2",
132132
"eslint": "^7.32.0",
133133
"eslint-plugin-import": "^2.24.2",

src/compiler/compile/css/Stylesheet.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { CssNode } from './interfaces';
88
import hash from '../utils/hash';
99
import compiler_warnings from '../compiler_warnings';
1010
import { extract_ignores_above_position } from '../../utils/extract_svelte_ignore';
11+
import { push_array } from '../../utils/push_array';
1112

1213
function remove_css_prefix(name: string): string {
1314
return name.replace(/^-((webkit)|(moz)|(o)|(ms))-/, '');
@@ -351,7 +352,7 @@ export default class Stylesheet {
351352
const at_rule_declarations = node.block.children
352353
.filter(node => node.type === 'Declaration')
353354
.map(node => new Declaration(node));
354-
atrule.declarations.push(...at_rule_declarations);
355+
push_array(atrule.declarations, at_rule_declarations);
355356
}
356357

357358
current_atrule = atrule;

src/compiler/compile/nodes/shared/map_children.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import Text from '../Text';
1818
import Title from '../Title';
1919
import Window from '../Window';
2020
import { TemplateNode } from '../../../interfaces';
21+
import { push_array } from '../../../utils/push_array';
2122

2223
export type Children = ReturnType<typeof map_children>;
2324

@@ -60,7 +61,7 @@ export default function map_children(component, parent, scope, children: Templat
6061
if (use_ignores) component.pop_ignores(), ignores = [];
6162

6263
if (node.type === 'Comment' && node.ignores.length) {
63-
ignores.push(...node.ignores);
64+
push_array(ignores, node.ignores);
6465
}
6566

6667
if (last) last.next = node;

src/compiler/compile/render_dom/index.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { apply_preprocessor_sourcemap } from '../../utils/mapped_code';
1111
import { RawSourceMap, DecodedSourceMap } from '@ampproject/remapping/dist/types/types';
1212
import { flatten } from '../../utils/flatten';
1313
import check_enable_sourcemap from '../utils/check_enable_sourcemap';
14+
import { push_array } from '../../utils/push_array';
1415

1516
export default function dom(
1617
component: Component,
@@ -67,7 +68,7 @@ export default function dom(
6768
// TODO the deconflicted names of blocks are reversed... should set them here
6869
const blocks = renderer.blocks.slice().reverse();
6970

70-
body.push(...blocks.map(block => {
71+
push_array(body, blocks.map(block => {
7172
// TODO this is a horrible mess — renderer.blocks
7273
// contains a mixture of Blocks and Nodes
7374
if ((block as Block).render) return (block as Block).render();
@@ -562,7 +563,7 @@ export default function dom(
562563
});
563564
}
564565

565-
declaration.body.body.push(...accessors);
566+
push_array(declaration.body.body, accessors);
566567

567568
body.push(declaration);
568569

@@ -599,7 +600,7 @@ export default function dom(
599600
}
600601
`[0] as ClassDeclaration;
601602

602-
declaration.body.body.push(...accessors);
603+
push_array(declaration.body.body, accessors);
603604

604605
body.push(declaration);
605606
}

src/compiler/compile/render_dom/wrappers/Element/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import Action from '../../../nodes/Action';
2626
import MustacheTagWrapper from '../MustacheTag';
2727
import RawMustacheTagWrapper from '../RawMustacheTag';
2828
import is_dynamic from '../shared/is_dynamic';
29+
import { push_array } from '../../../../utils/push_array';
2930

3031
interface BindingGroup {
3132
events: string[];
@@ -597,7 +598,7 @@ export default class ElementWrapper extends Wrapper {
597598
this.attributes.forEach((attribute) => {
598599
if (attribute.node.name === 'class') {
599600
const dependencies = attribute.node.get_dependencies();
600-
this.class_dependencies.push(...dependencies);
601+
push_array(this.class_dependencies, dependencies);
601602
}
602603
});
603604

src/compiler/compile/render_dom/wrappers/IfBlock.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { b, x } from 'code-red';
1010
import { walk } from 'estree-walker';
1111
import { is_head } from './shared/is_head';
1212
import { Identifier, Node } from 'estree';
13+
import { push_array } from '../../../utils/push_array';
1314

1415
function is_else_if(node: ElseBlock) {
1516
return (
@@ -166,7 +167,7 @@ export default class IfBlockWrapper extends Wrapper {
166167
block.has_outro_method = has_outros;
167168
});
168169

169-
renderer.blocks.push(...blocks);
170+
push_array(renderer.blocks, blocks);
170171
}
171172

172173
render(

src/compiler/utils/mapped_code.ts

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { DecodedSourceMap, RawSourceMap, SourceMapLoader } from '@ampproject/rem
22
import remapping from '@ampproject/remapping';
33
import { SourceMap } from 'magic-string';
44
import { Source, Processed } from '../preprocess/types';
5+
import { push_array } from './push_array';
56

67
export type SourceLocation = {
78
line: number;
@@ -60,14 +61,6 @@ function merge_tables<T>(this_table: T[], other_table: T[]): [T[], number[], boo
6061
return [new_table, idx_map, val_changed, idx_changed];
6162
}
6263

63-
function pushArray<T>(_this: T[], other: T[]) {
64-
// We use push to mutate in place for memory and perf reasons
65-
// We use the for loop instead of _this.push(...other) to avoid the JS engine's function argument limit (65,535 in JavascriptCore)
66-
for (let i = 0; i < other.length; i++) {
67-
_this.push(other[i]);
68-
}
69-
}
70-
7164
export class MappedCode {
7265
string: string;
7366
map: DecodedSourceMap;
@@ -159,10 +152,10 @@ export class MappedCode {
159152
}
160153

161154
// combine last line + first line
162-
pushArray(m1.mappings[m1.mappings.length - 1], m2.mappings.shift());
155+
push_array(m1.mappings[m1.mappings.length - 1], m2.mappings.shift());
163156

164157
// append other lines
165-
pushArray(m1.mappings, m2.mappings);
158+
push_array(m1.mappings, m2.mappings);
166159

167160
return this;
168161
}

src/compiler/utils/push_array.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
/**
2+
* Pushes all `items` into `array` using `push`, therefore mutating the array.
3+
* We do this for memory and perf reasons, and because `array.push(...items)` would
4+
* run into a "max call stack size exceeded" error with too many items (~65k).
5+
* @param array
6+
* @param items
7+
*/
8+
export function push_array<T>(array: T[], items: T[]): void {
9+
for (let i = 0; i < items.length; i++) {
10+
array.push(items[i]);
11+
}
12+
}

0 commit comments

Comments
 (0)