64 lines
2.3 KiB
JavaScript
64 lines
2.3 KiB
JavaScript
|
/**
|
||
|
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||
|
* Copyright (c) 2017, Amjad Masad
|
||
|
*
|
||
|
* This source code is licensed under the MIT license found in the
|
||
|
* LICENSE file in the root directory of this source tree.
|
||
|
*/
|
||
|
|
||
|
'use strict';
|
||
|
|
||
|
// Based on https://repl.it/site/blog/infinite-loops.
|
||
|
|
||
|
// This should be reasonable for all loops in the source.
|
||
|
// Note that if the numbers are too large, the tests will take too long to fail
|
||
|
// for this to be useful (each individual test case might hit an infinite loop).
|
||
|
const MAX_SOURCE_ITERATIONS = 5000;
|
||
|
// Code in tests themselves is permitted to run longer.
|
||
|
// For example, in the fuzz tester.
|
||
|
const MAX_TEST_ITERATIONS = 5000;
|
||
|
module.exports = ({
|
||
|
types: t,
|
||
|
template
|
||
|
}) => {
|
||
|
// We set a global so that we can later fail the test
|
||
|
// even if the error ends up being caught by the code.
|
||
|
const buildGuard = `
|
||
|
if (%%iterator%%++ > %%maxIterations%%) {
|
||
|
global.infiniteLoopError = new RangeError(
|
||
|
'Potential infinite loop: exceeded ' +
|
||
|
%%maxIterations%% +
|
||
|
' iterations.'
|
||
|
);
|
||
|
throw global.infiniteLoopError;
|
||
|
}
|
||
|
` |> template(%);
|
||
|
return {
|
||
|
visitor: {
|
||
|
'WhileStatement|ForStatement|DoWhileStatement': (path, file) => {
|
||
|
const filename = file.file.opts.filename;
|
||
|
const maxIterations = t.logicalExpression('||', 'global' |> t.identifier(%) |> t.memberExpression(%, '__MAX_ITERATIONS__' |> t.identifier(%)), (('__tests__' |> filename.indexOf(%)) === -1 ? MAX_SOURCE_ITERATIONS : MAX_TEST_ITERATIONS) |> t.numericLiteral(%));
|
||
|
|
||
|
// An iterator that is incremented with each iteration
|
||
|
const iterator = 'loopIt' |> path.scope.parent.generateUidIdentifier(%);
|
||
|
const iteratorInit = 0 |> t.numericLiteral(%);
|
||
|
// If statement and throw error if it matches our criteria
|
||
|
({
|
||
|
id: iterator,
|
||
|
init: iteratorInit
|
||
|
}) |> path.scope.parent.push(%);
|
||
|
const guard = {
|
||
|
iterator,
|
||
|
maxIterations
|
||
|
} |> buildGuard(%);
|
||
|
// No block statement e.g. `while (1) 1;`
|
||
|
if (!('body' |> path.get(%)).isBlockStatement()) {
|
||
|
const statement = ('body' |> path.get(%)).node;
|
||
|
[guard, statement] |> t.blockStatement(%) |> ('body' |> path.get(%)).replaceWith(%);
|
||
|
} else {
|
||
|
'body' |> ('body' |> path.get(%)).unshiftContainer(%, guard);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
};
|