mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2024-12-18 20:25:45 +01:00
EDITOR: Improved infinite loop checking (#1276)
This commit is contained in:
parent
7ed64cbc9c
commit
08eb60d21b
@ -62,6 +62,21 @@ For example, if a script run with 1 thread is able to hack \$10,000, then runnin
|
||||
|
||||
When "multithreading" a script, the total [RAM](ram.md) cost can be calculated by simply multiplying the [RAM](ram.md) cost of a single instance of your script by the number of threads you will use. [See [`ns.getScriptRam()`](https://github.com/bitburner-official/bitburner-src/blob/bec737a25307be29c7efef147fc31effca65eedc/markdown/bitburner.ns.getscriptram.md) or the `mem` terminal command detailed below]
|
||||
|
||||
## Never-ending scripts
|
||||
|
||||
Sometimes it might be necessary for a script to never end and keep doing a particular task.
|
||||
In that case you would want to write your script in a never-ending loop, like `while (true)`.
|
||||
|
||||
However, if you are not careful, this can crash your game.
|
||||
If the code inside the loop doesn't `await` for some time, it will never give other scripts and the game itself time to process.
|
||||
|
||||
<br />
|
||||
|
||||
To help you find this potential bug, any `while (true)` loop without any `await` statement inside it will be marked.
|
||||
A red decoration will appear on the left side of the script editor, telling you about the issue.
|
||||
|
||||
If you are really sure that this is not an oversight, you can suppress the warning using the comment `// @ignore-infinite` directly above the loop.
|
||||
|
||||
## Working with Scripts in Terminal
|
||||
|
||||
Here are some [terminal](terminal.md) commands you will find useful when working with scripts:
|
||||
|
@ -199,16 +199,16 @@ function parseOnlyRamCalculate(otherScripts: Map<ScriptFilePath, Script>, code:
|
||||
return { cost: ram, entries: detailedCosts.filter((e) => e.cost > 0) };
|
||||
}
|
||||
|
||||
export function checkInfiniteLoop(code: string): number {
|
||||
export function checkInfiniteLoop(code: string): number[] {
|
||||
let ast: acorn.Node;
|
||||
try {
|
||||
ast = parse(code, { sourceType: "module", ecmaVersion: "latest" });
|
||||
} catch (e) {
|
||||
// If code cannot be parsed, do not provide infinite loop detection warning
|
||||
return -1;
|
||||
return [];
|
||||
}
|
||||
function nodeHasTrueTest(node: acorn.Node): boolean {
|
||||
return node.type === "Literal" && "raw" in node && node.raw === "true";
|
||||
return node.type === "Literal" && "raw" in node && (node.raw === "true" || node.raw === "1");
|
||||
}
|
||||
|
||||
function hasAwait(ast: acorn.Node): boolean {
|
||||
@ -225,14 +225,19 @@ export function checkInfiniteLoop(code: string): number {
|
||||
return hasAwait;
|
||||
}
|
||||
|
||||
let missingAwaitLine = -1;
|
||||
const possibleLines: number[] = [];
|
||||
walk.recursive(
|
||||
ast,
|
||||
{},
|
||||
{
|
||||
WhileStatement: (node: Node, st: unknown, walkDeeper: walk.WalkerCallback<any>) => {
|
||||
const previousLines = code.slice(0, node.start).trimEnd().split("\n");
|
||||
const lineNumber = previousLines.length + 1;
|
||||
if (previousLines[previousLines.length - 1].match(/^\s*\/\/\s*@ignore-infinite/)) {
|
||||
return;
|
||||
}
|
||||
if (nodeHasTrueTest(node.test) && !hasAwait(node)) {
|
||||
missingAwaitLine = (code.slice(0, node.start).match(/\n/g) || []).length + 1;
|
||||
possibleLines.push(lineNumber);
|
||||
} else {
|
||||
node.body && walkDeeper(node.body, st);
|
||||
}
|
||||
@ -240,7 +245,7 @@ export function checkInfiniteLoop(code: string): number {
|
||||
},
|
||||
);
|
||||
|
||||
return missingAwaitLine;
|
||||
return possibleLines;
|
||||
}
|
||||
|
||||
interface ParseDepsResult {
|
||||
|
@ -116,10 +116,10 @@ function Root(props: IProps): React.ReactElement {
|
||||
if (editorRef.current === null || currentScript === null) return;
|
||||
if (!decorations) decorations = editorRef.current.createDecorationsCollection();
|
||||
if (!currentScript.path.endsWith(".js")) return;
|
||||
const awaitWarning = checkInfiniteLoop(newCode);
|
||||
if (awaitWarning !== -1) {
|
||||
decorations.set([
|
||||
{
|
||||
const possibleLines = checkInfiniteLoop(newCode);
|
||||
if (possibleLines.length !== 0) {
|
||||
decorations.set(
|
||||
possibleLines.map((awaitWarning) => ({
|
||||
range: {
|
||||
startLineNumber: awaitWarning,
|
||||
startColumn: 1,
|
||||
@ -130,11 +130,12 @@ function Root(props: IProps): React.ReactElement {
|
||||
isWholeLine: true,
|
||||
glyphMarginClassName: "myGlyphMarginClass",
|
||||
glyphMarginHoverMessage: {
|
||||
value: "Possible infinite loop, await something.",
|
||||
value:
|
||||
"Possible infinite loop, await something. If this is a false-positive, use `// @ignore-infinite` to suppress.",
|
||||
},
|
||||
},
|
||||
},
|
||||
]);
|
||||
})),
|
||||
);
|
||||
} else decorations.clear();
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user