mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2026-05-03 22:37:06 +02:00
Unify error handling
This commit is contained in:
+42
-63
@@ -12,7 +12,7 @@ import { generateNextPid } from "./Netscript/Pid";
|
||||
import { CONSTANTS } from "./Constants";
|
||||
import { Interpreter } from "./ThirdParty/JSInterpreter";
|
||||
import { NetscriptFunctions } from "./NetscriptFunctions";
|
||||
import { executeJSScript, Node } from "./NetscriptJSEvaluator";
|
||||
import { compile, Node } from "./NetscriptJSEvaluator";
|
||||
import { IPort } from "./NetscriptPort";
|
||||
import { RunningScript } from "./Script/RunningScript";
|
||||
import { getRamUsageFromRunningScript } from "./Script/RunningScriptHelpers";
|
||||
@@ -31,7 +31,6 @@ import { roundToTwo } from "./utils/helpers/roundToTwo";
|
||||
import { parse } from "acorn";
|
||||
import { simple as walksimple } from "acorn-walk";
|
||||
import { areFilesEqual } from "./Terminal/DirectoryHelpers";
|
||||
import { Player } from "./Player";
|
||||
import { Terminal } from "./Terminal";
|
||||
import { ScriptArg } from "./Netscript/ScriptArg";
|
||||
|
||||
@@ -49,14 +48,26 @@ export function prestigeWorkerScripts(): void {
|
||||
workerScripts.clear();
|
||||
}
|
||||
|
||||
// JS script promises need a little massaging to have the same guarantees as netscript
|
||||
// promises. This does said massaging and kicks the script off. It returns a promise
|
||||
// that resolves or rejects when the corresponding worker script is done.
|
||||
const startNetscript2Script = (workerScript: WorkerScript): Promise<void> =>
|
||||
executeJSScript(Player, workerScript.getServer().scripts, workerScript);
|
||||
async function startNetscript2Script(workerScript: WorkerScript): Promise<void> {
|
||||
const scripts = workerScript.getServer().scripts;
|
||||
const script = workerScript.getScript();
|
||||
if (script === null) throw "workerScript had no associated script. This is a bug.";
|
||||
const loadedModule = await compile(script, scripts);
|
||||
workerScript.ramUsage = script.ramUsage;
|
||||
const ns = workerScript.env.vars;
|
||||
|
||||
function startNetscript1Script(workerScript: WorkerScript): Promise<void> {
|
||||
if (!loadedModule) throw `${script.filename} cannot be run because the script module won't load`;
|
||||
// TODO: Better error for "unexpected reserved word" when using await in non-async function?
|
||||
if (typeof loadedModule.main !== "function")
|
||||
throw `${script.filename} cannot be run because it does not have a main function.`;
|
||||
if (!ns) throw `${script.filename} cannot be run because the NS object hasn't been constructed properly.`;
|
||||
await loadedModule.main(ns);
|
||||
return;
|
||||
}
|
||||
|
||||
async function startNetscript1Script(workerScript: WorkerScript): Promise<void> {
|
||||
const code = workerScript.code;
|
||||
let errorToThrow: unknown;
|
||||
|
||||
//Process imports
|
||||
let codeWithImports, codeLineOffset;
|
||||
@@ -65,10 +76,7 @@ function startNetscript1Script(workerScript: WorkerScript): Promise<void> {
|
||||
codeWithImports = importProcessingRes.code;
|
||||
codeLineOffset = importProcessingRes.lineOffset;
|
||||
} catch (e: unknown) {
|
||||
dialogBoxCreate(`Error processing Imports in ${workerScript.name} on ${workerScript.hostname}:\n\n${e}`);
|
||||
workerScript.env.stopFlag = true;
|
||||
killWorkerScript(workerScript);
|
||||
return Promise.resolve();
|
||||
throw `Error processing Imports in ${workerScript.name}@${workerScript.hostname}:\n\n${e}`;
|
||||
}
|
||||
|
||||
interface BasicObject {
|
||||
@@ -77,20 +85,14 @@ function startNetscript1Script(workerScript: WorkerScript): Promise<void> {
|
||||
function wrapNS1Layer(int: Interpreter, intLayer: unknown, nsLayer = workerScript.env.vars as BasicObject) {
|
||||
for (const [name, entry] of Object.entries(nsLayer)) {
|
||||
if (typeof entry === "function") {
|
||||
// Async functions need to be wrapped. See JS-Interpreter documentation
|
||||
const wrapper = async (...args: unknown[]) => {
|
||||
// This async wrapper is sent a resolver function as an extra arg.
|
||||
// See JSInterpreter.js:3209
|
||||
try {
|
||||
// Sent a resolver function as an extra arg. See createAsyncFunction JSInterpreter.js:3209
|
||||
const callback = args.pop() as (value: unknown) => void;
|
||||
const result = await entry(...args.map((arg) => int.pseudoToNative(arg)));
|
||||
return callback(int.nativeToPseudo(result));
|
||||
} catch (e: unknown) {
|
||||
// NS1 interpreter doesn't cleanly handle throwing. Need to show dialog here.
|
||||
errorDialog(e, `RUNTIME ERROR:\n${workerScript.name}@${workerScript.hostname}\n\n`);
|
||||
workerScript.env.stopFlag = true;
|
||||
killWorkerScript(workerScript);
|
||||
return;
|
||||
errorToThrow = e;
|
||||
}
|
||||
};
|
||||
int.setProperty(intLayer, name, int.createAsyncFunction(wrapper));
|
||||
@@ -109,31 +111,16 @@ function startNetscript1Script(workerScript: WorkerScript): Promise<void> {
|
||||
try {
|
||||
interpreter = new Interpreter(codeWithImports, wrapNS1Layer, codeLineOffset);
|
||||
} catch (e: unknown) {
|
||||
dialogBoxCreate(`Syntax ERROR in ${workerScript.name} on ${workerScript.hostname}:\n\n${String(e)}`);
|
||||
workerScript.env.stopFlag = true;
|
||||
killWorkerScript(workerScript);
|
||||
return Promise.resolve();
|
||||
throw `Syntax ERROR in ${workerScript.name}@${workerScript.hostname}:\n\n${String(e)}`;
|
||||
}
|
||||
|
||||
return new Promise((resolve) => {
|
||||
function runInterpreter(): void {
|
||||
if (workerScript.env.stopFlag) resolve();
|
||||
|
||||
let more = true;
|
||||
let i = 0;
|
||||
while (i < 3 && more) {
|
||||
more = more && interpreter.step();
|
||||
i++;
|
||||
}
|
||||
|
||||
if (more) {
|
||||
setTimeout(runInterpreter, Settings.CodeInstructionRunTime);
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
}
|
||||
runInterpreter();
|
||||
});
|
||||
let more = true;
|
||||
while (more) {
|
||||
if (errorToThrow) throw errorToThrow;
|
||||
if (workerScript.env.stopFlag) return;
|
||||
for (let i = 0; more && i < 3; i++) more = interpreter.step();
|
||||
if (more) await new Promise((r) => setTimeout(r, Settings.CodeInstructionRunTime));
|
||||
}
|
||||
}
|
||||
|
||||
/* Since the JS Interpreter used for Netscript 1.0 only supports ES5, the keyword
|
||||
@@ -326,7 +313,7 @@ function createAndAddWorkerScript(runningScriptObj: RunningScript, server: BaseS
|
||||
return false;
|
||||
}
|
||||
|
||||
server.updateRamUsed(roundToTwo(server.ramUsed + ramUsage), Player);
|
||||
server.updateRamUsed(roundToTwo(server.ramUsed + ramUsage));
|
||||
|
||||
// Get the pid
|
||||
const pid = generateNextPid();
|
||||
@@ -346,20 +333,10 @@ function createAndAddWorkerScript(runningScriptObj: RunningScript, server: BaseS
|
||||
workerScripts.set(pid, workerScript);
|
||||
WorkerScriptStartStopEventEmitter.emit();
|
||||
|
||||
// Start the script's execution
|
||||
let scriptExecution: Promise<void> | null = null; // Script's resulting promise
|
||||
if (workerScript.name.endsWith(".js")) {
|
||||
scriptExecution = startNetscript2Script(workerScript);
|
||||
} else {
|
||||
scriptExecution = startNetscript1Script(workerScript);
|
||||
if (!(scriptExecution instanceof Promise)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Once the code finishes (either resolved or rejected, doesnt matter), set its
|
||||
// running status to false
|
||||
scriptExecution
|
||||
// Start the script's execution using the correct function for file type
|
||||
(workerScript.name.endsWith(".js") ? startNetscript2Script : startNetscript1Script)(workerScript)
|
||||
// Once the code finishes (either resolved or rejected, doesnt matter), set its
|
||||
// running status to false
|
||||
.then(function () {
|
||||
// On natural death, the earnings are transfered to the parent if it still exists.
|
||||
if (parent && !parent.env.stopFlag) {
|
||||
@@ -370,13 +347,15 @@ function createAndAddWorkerScript(runningScriptObj: RunningScript, server: BaseS
|
||||
workerScript.log("", () => "Script finished running");
|
||||
})
|
||||
.catch(function (e) {
|
||||
errorDialog(e, `RUNTIME ERROR\n${workerScript.name}@${workerScript.hostname} (PID - ${workerScript.pid})\n\n`);
|
||||
let logText = "Script crashed due to an error.";
|
||||
if (e instanceof ScriptDeath) logText = "Script killed.";
|
||||
workerScript.log("", () => logText);
|
||||
let initialText = `ERROR\n${workerScript.name}@${workerScript.hostname} (PID - ${workerScript.pid})\n\n`;
|
||||
if (e instanceof SyntaxError) e = `SYNTAX ${initialText}${e.message} (sorry we can't be more helpful)`;
|
||||
else if (e instanceof Error) {
|
||||
e = `RUNTIME ${initialText}${e.message}${e.stack ? `\nstack:\n${e.stack.toString()}` : ""}`;
|
||||
}
|
||||
errorDialog(e, typeof e === "string" && e.includes(initialText) ? "" : initialText);
|
||||
workerScript.log("", () => (e instanceof ScriptDeath ? "Script killed." : "Script crashed due to an error."));
|
||||
killWorkerScript(workerScript);
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user