Skip to content

add loopGuardTimeout options #3896

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Nov 11, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions site/content/docs/04-compile-time.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ The following options can be passed to the compiler. None are required:
| `tag` | string | null
| `accessors` | boolean | `false`
| `css` | boolean | `true`
| `loopGuardTimeout` | number | 0
| `preserveComments` | boolean | `false`
| `preserveWhitespace` | boolean | `false`
| `outputFilename` | string | `null`
Expand All @@ -73,6 +74,7 @@ The following options can be passed to the compiler. None are required:
| `customElement` | `false` | If `true`, tells the compiler to generate a custom element constructor instead of a regular Svelte component.
| `tag` | `null` | A `string` that tells Svelte what tag name to register the custom element with. It must be a lowercase alphanumeric string with at least one hyphen, e.g. `"my-element"`.
| `css` | `true` | If `true`, styles will be included in the JavaScript class and injected at runtime. It's recommended that you set this to `false` and use the CSS that is statically generated, as it will result in smaller JavaScript bundles and better performance.
| `loopGuardTimeout` | 0 | A `number` that tells Svelte to break the loop if it blocks the thread for more than `loopGuardTimeout` ms. This is useful to prevent infinite loops. **Only available when `dev: true`**
| `preserveComments` | `false` | If `true`, your HTML comments will be preserved during server-side rendering. By default, they are stripped out.
| `preserveWhitespace` | `false` | If `true`, whitespace inside and between elements is kept as you typed it, rather than optimised by Svelte.
| `outputFilename` | `null` | A `string` used for your JavaScript sourcemap.
Expand Down
8 changes: 4 additions & 4 deletions src/compiler/compile/Component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -750,8 +750,8 @@ export default class Component {

component.warn_on_undefined_store_value_references(node, parent, scope);

if (component.compile_options.dev) {
const to_insert_for_loop_protect = component.loop_protect(node, prop, index);
if (component.compile_options.dev && component.compile_options.loopGuardTimeout > 0) {
const to_insert_for_loop_protect = component.loop_protect(node, prop, index, component.compile_options.loopGuardTimeout);
if (to_insert_for_loop_protect) {
if (!Array.isArray(parent[prop])) {
parent[prop] = {
Expand Down Expand Up @@ -863,7 +863,7 @@ export default class Component {
}
}

loop_protect(node, prop, index) {
loop_protect(node, prop, index, timeout) {
if (node.type === 'WhileStatement' ||
node.type === 'ForStatement' ||
node.type === 'DoWhileStatement') {
Expand All @@ -873,7 +873,7 @@ export default class Component {
internal: true,
});

const before = b`const ${guard} = @loop_guard()`;
const before = b`const ${guard} = @loop_guard(${timeout})`;
const inside = b`${guard}();`;

// wrap expression statement with BlockStatement
Expand Down
13 changes: 12 additions & 1 deletion src/compiler/compile/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,13 @@ const valid_options = [
'customElement',
'tag',
'css',
'loopGuardTimeout',
'preserveComments',
'preserveWhitespace'
];

function validate_options(options: CompileOptions, warnings: Warning[]) {
const { name, filename } = options;
const { name, filename, loopGuardTimeout, dev } = options;

Object.keys(options).forEach(key => {
if (valid_options.indexOf(key) === -1) {
Expand All @@ -54,6 +55,16 @@ function validate_options(options: CompileOptions, warnings: Warning[]) {
toString: () => message,
});
}

if (loopGuardTimeout && !dev) {
const message = 'options.loopGuardTimeout is for options.dev = true only';
warnings.push({
code: `options-loop-guard-timeout`,
message,
filename,
toString: () => message,
});
}
}

export default function compile(source: string, options: CompileOptions = {}) {
Expand Down
1 change: 1 addition & 0 deletions src/compiler/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ export interface CompileOptions {
customElement?: boolean;
tag?: string;
css?: boolean;
loopGuardTimeout?: number;

preserveComments?: boolean;
preserveWhitespace?: boolean;
Expand Down
4 changes: 2 additions & 2 deletions src/runtime/internal/dev.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,10 +96,10 @@ export class SvelteComponentDev extends SvelteComponent {
}
}

export function loop_guard() {
export function loop_guard(timeout) {
const start = Date.now();
return () => {
if (Date.now() - start > 100) {
if (Date.now() - start > timeout) {
throw new Error(`Infinite loop detected`);
}
};
Expand Down
7 changes: 4 additions & 3 deletions test/js/samples/loop-protect/_config.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export default {
options: {
dev: true
}
};
dev: true,
loopGuardTimeout: 100,
},
};
14 changes: 7 additions & 7 deletions test/js/samples/loop-protect/expected.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,28 +35,28 @@ function create_fragment(ctx) {
}

function instance($$self) {
const guard = loop_guard();
const guard = loop_guard(100);

while (true) {
foo();
guard();
}

const guard_1 = loop_guard();
const guard_1 = loop_guard(100);

for (; ; ) {
foo();
guard_1();
}

const guard_2 = loop_guard();
const guard_2 = loop_guard(100);

while (true) {
foo();
guard_2();
}

const guard_4 = loop_guard();
const guard_4 = loop_guard(100);

do {
foo();
Expand All @@ -68,11 +68,11 @@ function instance($$self) {
};

$$self.$inject_state = $$props => {

};

$: {
const guard_3 = loop_guard();
const guard_3 = loop_guard(100);

while (true) {
foo();
Expand All @@ -81,7 +81,7 @@ function instance($$self) {
}

$: {
const guard_5 = loop_guard();
const guard_5 = loop_guard(100);

do {
foo();
Expand Down
1 change: 1 addition & 0 deletions test/runtime/samples/loop-protect/_config.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@ export default {
error: 'Infinite loop detected',
compileOptions: {
dev: true,
loopGuardTimeout: 100,
}
};