Merge pull request #3896 from tanhauhau/tanhauhau/loop-protect

add loopGuardTimeout options
pull/3901/head
Rich Harris 5 years ago committed by GitHub
commit 4a3147ff33
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -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`
@ -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.

@ -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] = {
@ -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') {
@ -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

@ -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) {
@ -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 = {}) {

@ -121,6 +121,7 @@ export interface CompileOptions {
customElement?: boolean;
tag?: string;
css?: boolean;
loopGuardTimeout?: number;
preserveComments?: boolean;
preserveWhitespace?: boolean;

@ -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`);
}
};

@ -1,5 +1,6 @@
export default {
options: {
dev: true
}
};
dev: true,
loopGuardTimeout: 100,
},
};

@ -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();
@ -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();
@ -81,7 +81,7 @@ function instance($$self) {
}
$: {
const guard_5 = loop_guard();
const guard_5 = loop_guard(100);
do {
foo();

@ -2,5 +2,6 @@ export default {
error: 'Infinite loop detected',
compileOptions: {
dev: true,
loopGuardTimeout: 100,
}
};

Loading…
Cancel
Save