From 95a1c18139fbb48a68b117aa3f35d38ccc047ab5 Mon Sep 17 00:00:00 2001 From: Snarling <84951833+Snarling@users.noreply.github.com> Date: Wed, 17 Aug 2022 17:11:59 -0400 Subject: [PATCH 1/5] synchronize write and scp --- src/NetscriptFunctions.ts | 53 +++++++++++++------------------ src/Script/RamCalculations.ts | 59 +++++++++-------------------------- src/Script/Script.ts | 4 +-- 3 files changed, 38 insertions(+), 78 deletions(-) diff --git a/src/NetscriptFunctions.ts b/src/NetscriptFunctions.ts index 2c617b4b6..df60d64e8 100644 --- a/src/NetscriptFunctions.ts +++ b/src/NetscriptFunctions.ts @@ -829,11 +829,7 @@ const base: InternalAPI = { }, scp: (ctx: NetscriptContext) => - async ( - _scriptname: unknown, - _destination: unknown, - _source: unknown = ctx.workerScript.hostname, - ): Promise => { + (_scriptname: unknown, _destination: unknown, _source: unknown = ctx.workerScript.hostname): boolean => { const destination = helpers.string(ctx, "destination", _destination); const source = helpers.string(ctx, "source", _source); if (Array.isArray(_scriptname)) { @@ -843,14 +839,12 @@ const base: InternalAPI = { throw helpers.makeRuntimeErrorMsg(ctx, "No scripts to copy"); } let res = true; - await Promise.all( - scripts.map(async function (script) { - if (!(await NetscriptFunctions(ctx.workerScript).scp(script, destination, source))) { - res = false; - } - }), - ); - return Promise.resolve(res); + scripts.map(function (script) { + if (!NetscriptFunctions(ctx.workerScript).scp(script, destination, source)) { + res = false; + } + }); + return res; } const scriptName = helpers.string(ctx, "scriptName", _scriptname); @@ -880,18 +874,18 @@ const base: InternalAPI = { if (!found) { helpers.log(ctx, () => `File '${scriptName}' does not exist.`); - return Promise.resolve(false); + return false; } for (let i = 0; i < destServer.messages.length; ++i) { if (destServer.messages[i] === scriptName) { helpers.log(ctx, () => `File '${scriptName}' copied over to '${destServer?.hostname}'.`); - return Promise.resolve(true); // Already exists + return true; // Already exists } } destServer.messages.push(scriptName); helpers.log(ctx, () => `File '${scriptName}' copied over to '${destServer?.hostname}'.`); - return Promise.resolve(true); + return true; } // Scp for text files @@ -905,7 +899,7 @@ const base: InternalAPI = { } if (txtFile === undefined) { helpers.log(ctx, () => `File '${scriptName}' does not exist.`); - return Promise.resolve(false); + return false; } for (let i = 0; i < destServer.textFiles.length; ++i) { @@ -913,13 +907,13 @@ const base: InternalAPI = { // Overwrite destServer.textFiles[i].text = txtFile.text; helpers.log(ctx, () => `File '${scriptName}' copied over to '${destServer?.hostname}'.`); - return Promise.resolve(true); + return true; } } const newFile = new TextFile(txtFile.fn, txtFile.text); destServer.textFiles.push(newFile); helpers.log(ctx, () => `File '${scriptName}' copied over to '${destServer?.hostname}'.`); - return Promise.resolve(true); + return true; } // Scp for script files @@ -932,7 +926,7 @@ const base: InternalAPI = { } if (sourceScript == null) { helpers.log(ctx, () => `File '${scriptName}' does not exist.`); - return Promise.resolve(false); + return false; } // Overwrite script if it already exists @@ -943,11 +937,11 @@ const base: InternalAPI = { // If it's the exact same file don't actually perform the // copy to avoid recompiling uselessly. Players tend to scp // liberally. - if (oldScript.code === sourceScript.code) return Promise.resolve(true); + if (oldScript.code === sourceScript.code) return true; oldScript.code = sourceScript.code; oldScript.ramUsage = sourceScript.ramUsage; oldScript.markUpdated(); - return Promise.resolve(true); + return true; } } @@ -958,13 +952,8 @@ const base: InternalAPI = { newScript.server = destServer.hostname; destServer.scripts.push(newScript); helpers.log(ctx, () => `File '${scriptName}' copied over to '${destServer?.hostname}'.`); - return new Promise((resolve) => { - if (destServer === null) { - resolve(false); - return; - } - newScript.updateRamUsage(Player, destServer.scripts).then(() => resolve(true)); - }); + newScript.updateRamUsage(Player, destServer.scripts); + return true; }, ls: (ctx: NetscriptContext) => @@ -1501,7 +1490,7 @@ const base: InternalAPI = { }, write: (ctx: NetscriptContext) => - (_port: unknown, data: unknown = "", _mode: unknown = "a"): Promise => { + (_port: unknown, data: unknown = "", _mode: unknown = "a"): void => { const port = helpers.string(ctx, "port", _port); const mode = helpers.string(ctx, "mode", _mode); if (isString(port)) { @@ -1545,7 +1534,7 @@ const base: InternalAPI = { const txtFile = getTextFile(fn, server); if (txtFile == null) { createTextFile(fn, String(data), server); - return Promise.resolve(); + return; } if (mode === "w") { txtFile.write(String(data)); @@ -1553,7 +1542,7 @@ const base: InternalAPI = { txtFile.append(String(data)); } } - return Promise.resolve(); + return; } else { throw helpers.makeRuntimeErrorMsg(ctx, `Invalid argument: ${port}`); } diff --git a/src/Script/RamCalculations.ts b/src/Script/RamCalculations.ts index 7c69aff5b..61538bf4b 100644 --- a/src/Script/RamCalculations.ts +++ b/src/Script/RamCalculations.ts @@ -44,7 +44,7 @@ const memCheckGlobalKey = ".__GLOBAL__"; * @param {WorkerScript} workerScript - Object containing RAM costs of Netscript functions. Also used to * keep track of what functions have/havent been accounted for */ -async function parseOnlyRamCalculate(player: IPlayer, otherScripts: Script[], code: string): Promise { +function parseOnlyRamCalculate(player: IPlayer, otherScripts: Script[], code: string): RamCalculation { try { /** * Maps dependent identifiers to their dependencies. @@ -88,47 +88,22 @@ async function parseOnlyRamCalculate(player: IPlayer, otherScripts: Script[], co while (parseQueue.length > 0) { const nextModule = parseQueue.shift(); if (nextModule === undefined) throw new Error("nextModule should not be undefined"); + if (nextModule.startsWith("https://") || nextModule.startsWith("http://")) continue; - // Additional modules can either be imported from the web (in which case we use - // a dynamic import), or from other in-game scripts - let code; - if (nextModule.startsWith("https://") || nextModule.startsWith("http://")) { - try { - // eslint-disable-next-line no-await-in-loop - const module = await eval("import(nextModule)"); - code = ""; - for (const prop in module) { - if (typeof module[prop] === "function") { - code += module[prop].toString() + ";\n"; - } - } - } catch (e) { - console.error(`Error dynamically importing module from ${nextModule} for RAM calculations: ${e}`); - return { cost: RamCalculationErrorCode.URLImportError }; + let script = null; + const fn = nextModule.startsWith("./") ? nextModule.slice(2) : nextModule; + for (const s of otherScripts) { + if (areImportsEquals(s.filename, fn)) { + script = s; + break; } - } else { - if (!Array.isArray(otherScripts)) { - console.warn(`parseOnlyRamCalculate() not called with array of scripts`); - return { cost: RamCalculationErrorCode.ImportError }; - } - - let script = null; - const fn = nextModule.startsWith("./") ? nextModule.slice(2) : nextModule; - for (const s of otherScripts) { - if (areImportsEquals(s.filename, fn)) { - script = s; - break; - } - } - - if (script == null) { - return { cost: RamCalculationErrorCode.ImportError }; // No such script on the server - } - - code = script.code; } - parseCode(code, nextModule); + if (script == null) { + return { cost: RamCalculationErrorCode.ImportError }; // No such script on the server + } + + parseCode(script.code, nextModule); } // Finally, walk the reference map and generate a ram cost. The initial set of keys to scan @@ -406,13 +381,9 @@ function parseOnlyCalculateDeps(code: string, currentModule: string): ParseDepsR * @param {Script[]} otherScripts - All other scripts on the server. * Used to account for imported scripts */ -export async function calculateRamUsage( - player: IPlayer, - codeCopy: string, - otherScripts: Script[], -): Promise { +export function calculateRamUsage(player: IPlayer, codeCopy: string, otherScripts: Script[]): RamCalculation { try { - return await parseOnlyRamCalculate(player, otherScripts, codeCopy); + return parseOnlyRamCalculate(player, otherScripts, codeCopy); } catch (e) { console.error(`Failed to parse script for RAM calculations:`); console.error(e); diff --git a/src/Script/Script.ts b/src/Script/Script.ts index e4ea95c3d..ba0079c8c 100644 --- a/src/Script/Script.ts +++ b/src/Script/Script.ts @@ -113,8 +113,8 @@ export class Script { * Calculates and updates the script's RAM usage based on its code * @param {Script[]} otherScripts - Other scripts on the server. Used to process imports */ - async updateRamUsage(player: IPlayer, otherScripts: Script[]): Promise { - const res = await calculateRamUsage(player, this.code, otherScripts); + updateRamUsage(player: IPlayer, otherScripts: Script[]): void { + const res = calculateRamUsage(player, this.code, otherScripts); if (res.cost > 0) { this.ramUsage = roundToTwo(res.cost); this.ramUsageEntries = res.entries; From f6a8d5d337d309eaf60b474ed55e23cb8a8b6f17 Mon Sep 17 00:00:00 2001 From: Snarling <84951833+Snarling@users.noreply.github.com> Date: Wed, 17 Aug 2022 18:55:12 -0400 Subject: [PATCH 2/5] synchronize scp and write --- src/NetscriptFunctions.ts | 3 ++- src/NetscriptJSEvaluator.ts | 2 +- src/ScriptEditor/ui/ScriptEditorRoot.tsx | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/NetscriptFunctions.ts b/src/NetscriptFunctions.ts index df60d64e8..cd128000b 100644 --- a/src/NetscriptFunctions.ts +++ b/src/NetscriptFunctions.ts @@ -839,7 +839,8 @@ const base: InternalAPI = { throw helpers.makeRuntimeErrorMsg(ctx, "No scripts to copy"); } let res = true; - scripts.map(function (script) { + scripts.forEach(function (script) { + //TODO: This array method still needs to be reworked because it is terribly inefficient. if (!NetscriptFunctions(ctx.workerScript).scp(script, destination, source)) { res = false; } diff --git a/src/NetscriptJSEvaluator.ts b/src/NetscriptJSEvaluator.ts index d3e31f1d2..46a9ef5c1 100644 --- a/src/NetscriptJSEvaluator.ts +++ b/src/NetscriptJSEvaluator.ts @@ -29,7 +29,7 @@ export async function compile(player: IPlayer, script: Script, scripts: Script[] // but not really behaves like import. Particularly, it cannot // load fully dynamic content. So we hide the import from webpack // by placing it inside an eval call. - await script.updateRamUsage(player, scripts); + script.updateRamUsage(player, scripts); const uurls = _getScriptUrls(script, scripts, []); const url = uurls[uurls.length - 1].url; if (script.url && script.url !== url) { diff --git a/src/ScriptEditor/ui/ScriptEditorRoot.tsx b/src/ScriptEditor/ui/ScriptEditorRoot.tsx index fa4db9370..7256fd3a9 100644 --- a/src/ScriptEditor/ui/ScriptEditorRoot.tsx +++ b/src/ScriptEditor/ui/ScriptEditorRoot.tsx @@ -277,7 +277,7 @@ export function Root(props: IProps): React.ReactElement { } setUpdatingRam(true); const codeCopy = newCode + ""; - const ramUsage = await calculateRamUsage(props.player, codeCopy, props.player.getCurrentServer().scripts); + const ramUsage = calculateRamUsage(props.player, codeCopy, props.player.getCurrentServer().scripts); if (ramUsage.cost > 0) { const entries = ramUsage.entries?.sort((a, b) => b.cost - a.cost) ?? []; const entriesDisp = []; From 07e9cb9277f6632bd3f647a8bb3d1f75b57f9af1 Mon Sep 17 00:00:00 2001 From: Snarling <84951833+Snarling@users.noreply.github.com> Date: Wed, 17 Aug 2022 20:31:26 -0400 Subject: [PATCH 3/5] refactor scp function --- src/NetscriptFunctions.ts | 198 +++++++++++++++++--------------------- 1 file changed, 86 insertions(+), 112 deletions(-) diff --git a/src/NetscriptFunctions.ts b/src/NetscriptFunctions.ts index cd128000b..d6998449e 100644 --- a/src/NetscriptFunctions.ts +++ b/src/NetscriptFunctions.ts @@ -829,132 +829,106 @@ const base: InternalAPI = { }, scp: (ctx: NetscriptContext) => - (_scriptname: unknown, _destination: unknown, _source: unknown = ctx.workerScript.hostname): boolean => { + (_files: unknown, _destination: unknown, _source: unknown = ctx.workerScript.hostname): boolean => { const destination = helpers.string(ctx, "destination", _destination); const source = helpers.string(ctx, "source", _source); - if (Array.isArray(_scriptname)) { - // Recursively call scp on all elements of array - const scripts: string[] = _scriptname; - if (scripts.length === 0) { - throw helpers.makeRuntimeErrorMsg(ctx, "No scripts to copy"); - } - let res = true; - scripts.forEach(function (script) { - //TODO: This array method still needs to be reworked because it is terribly inefficient. - if (!NetscriptFunctions(ctx.workerScript).scp(script, destination, source)) { - res = false; - } - }); - return res; - } - - const scriptName = helpers.string(ctx, "scriptName", _scriptname); - - // Invalid file type - if (!isValidFilePath(scriptName)) { - throw helpers.makeRuntimeErrorMsg(ctx, `Invalid filename: '${scriptName}'`); - } - - // Invalid file name - if (!scriptName.endsWith(".lit") && !isScriptFilename(scriptName) && !scriptName.endsWith("txt")) { - throw helpers.makeRuntimeErrorMsg(ctx, "Only works for scripts, .lit and .txt files"); - } - const destServer = helpers.getServer(ctx, destination); - const currServ = helpers.getServer(ctx, source); + const sourceServ = helpers.getServer(ctx, source); + const files = Array.isArray(_files) ? _files : [_files]; - // Scp for lit files - if (scriptName.endsWith(".lit")) { - let found = false; - for (let i = 0; i < currServ.messages.length; ++i) { - if (currServ.messages[i] == scriptName) { - found = true; - break; + //First loop through filenames to find all errors before moving anything. + for (const file of files) { + // Not a string + if (typeof file !== "string") + throw helpers.makeRuntimeErrorMsg(ctx, "files should be a string or an array of strings."); + + // Invalid file name + if (!isValidFilePath(file)) throw helpers.makeRuntimeErrorMsg(ctx, `Invalid filename: '${file}'`); + + // Invalid file type + if (!file.endsWith(".lit") && !isScriptFilename(file) && !file.endsWith("txt")) { + throw helpers.makeRuntimeErrorMsg(ctx, "Only works for scripts, .lit and .txt files."); + } + } + + let noFailures = true; + //ts detects files as any[] here even though we would have thrown in the above loop if it wasn't string[] + for (const file of files as string[]) { + // Scp for lit files + if (file.endsWith(".lit")) { + const sourceMessage = sourceServ.messages.find((message) => message === file); + if (!sourceMessage) { + helpers.log(ctx, () => `File '${file}' does not exist.`); + noFailures = false; + continue; } - } - if (!found) { - helpers.log(ctx, () => `File '${scriptName}' does not exist.`); - return false; - } - - for (let i = 0; i < destServer.messages.length; ++i) { - if (destServer.messages[i] === scriptName) { - helpers.log(ctx, () => `File '${scriptName}' copied over to '${destServer?.hostname}'.`); - return true; // Already exists + const destMessage = destServer.messages.find((message) => message === file); + if (destMessage) { + helpers.log(ctx, () => `File '${file}' was already on '${destServer?.hostname}'.`); + continue; } - } - destServer.messages.push(scriptName); - helpers.log(ctx, () => `File '${scriptName}' copied over to '${destServer?.hostname}'.`); - return true; - } - // Scp for text files - if (scriptName.endsWith(".txt")) { - let txtFile; - for (let i = 0; i < currServ.textFiles.length; ++i) { - if (currServ.textFiles[i].fn === scriptName) { - txtFile = currServ.textFiles[i]; - break; + destServer.messages.push(file); + helpers.log(ctx, () => `File '${file}' copied over to '${destServer?.hostname}'.`); + continue; + } + + // Scp for text files + if (file.endsWith(".txt")) { + const sourceTextFile = sourceServ.textFiles.find((textFile) => textFile.fn === file); + if (!sourceTextFile) { + helpers.log(ctx, () => `File '${file}' does not exist.`); + noFailures = false; + continue; } - } - if (txtFile === undefined) { - helpers.log(ctx, () => `File '${scriptName}' does not exist.`); - return false; - } - for (let i = 0; i < destServer.textFiles.length; ++i) { - if (destServer.textFiles[i].fn === scriptName) { - // Overwrite - destServer.textFiles[i].text = txtFile.text; - helpers.log(ctx, () => `File '${scriptName}' copied over to '${destServer?.hostname}'.`); - return true; + const destTextFile = destServer.textFiles.find((textFile) => textFile.fn === file); + if (destTextFile) { + destTextFile.text = sourceTextFile.text; + helpers.log(ctx, () => `File '${file}' overwritten on '${destServer?.hostname}'.`); + continue; } + + const newFile = new TextFile(sourceTextFile.fn, sourceTextFile.text); + destServer.textFiles.push(newFile); + helpers.log(ctx, () => `File '${file}' copied over to '${destServer?.hostname}'.`); + continue; } - const newFile = new TextFile(txtFile.fn, txtFile.text); - destServer.textFiles.push(newFile); - helpers.log(ctx, () => `File '${scriptName}' copied over to '${destServer?.hostname}'.`); - return true; + + // Scp for script files + const sourceScript = sourceServ.scripts.find((script) => script.filename === file); + if (!sourceScript) { + helpers.log(ctx, () => `File '${file}' does not exist.`); + noFailures = false; + continue; + } + + // Overwrite script if it already exists + const destScript = destServer.scripts.find((script) => script.filename === file); + if (destScript) { + if (destScript.code === sourceScript.code) { + helpers.log(ctx, () => `Identical file '${file}' was already on '${destServer?.hostname}'`); + continue; + } + destScript.code = sourceScript.code; + destScript.ramUsage = destScript.ramUsage; + destScript.markUpdated(); + helpers.log(ctx, () => `WARNING: File '${file}' overwritten on '${destServer?.hostname}'`); + continue; + } + + // Create new script if it does not already exist + const newScript = new Script(Player, file); + newScript.code = sourceScript.code; + newScript.ramUsage = sourceScript.ramUsage; + newScript.server = destServer.hostname; + destServer.scripts.push(newScript); + helpers.log(ctx, () => `File '${file}' copied over to '${destServer?.hostname}'.`); + newScript.updateRamUsage(Player, destServer.scripts); } - // Scp for script files - let sourceScript = null; - for (let i = 0; i < currServ.scripts.length; ++i) { - if (scriptName == currServ.scripts[i].filename) { - sourceScript = currServ.scripts[i]; - break; - } - } - if (sourceScript == null) { - helpers.log(ctx, () => `File '${scriptName}' does not exist.`); - return false; - } - - // Overwrite script if it already exists - for (let i = 0; i < destServer.scripts.length; ++i) { - if (scriptName == destServer.scripts[i].filename) { - helpers.log(ctx, () => `WARNING: File '${scriptName}' overwritten on '${destServer?.hostname}'`); - const oldScript = destServer.scripts[i]; - // If it's the exact same file don't actually perform the - // copy to avoid recompiling uselessly. Players tend to scp - // liberally. - if (oldScript.code === sourceScript.code) return true; - oldScript.code = sourceScript.code; - oldScript.ramUsage = sourceScript.ramUsage; - oldScript.markUpdated(); - return true; - } - } - - // Create new script if it does not already exist - const newScript = new Script(Player, scriptName); - newScript.code = sourceScript.code; - newScript.ramUsage = sourceScript.ramUsage; - newScript.server = destServer.hostname; - destServer.scripts.push(newScript); - helpers.log(ctx, () => `File '${scriptName}' copied over to '${destServer?.hostname}'.`); - newScript.updateRamUsage(Player, destServer.scripts); - return true; + return noFailures; }, ls: (ctx: NetscriptContext) => From 823cdf70caad5ce1a9813bf31b50062d025dfcf2 Mon Sep 17 00:00:00 2001 From: Snarling <84951833+Snarling@users.noreply.github.com> Date: Fri, 19 Aug 2022 18:21:31 -0400 Subject: [PATCH 4/5] Fix compile race conditions --- src/NetscriptFunctions.ts | 25 ++-------- src/NetscriptJSEvaluator.ts | 50 ++++++++++--------- src/Script/Script.ts | 7 +-- ...termineAllPossibilitiesForTabCompletion.ts | 6 +-- 4 files changed, 37 insertions(+), 51 deletions(-) diff --git a/src/NetscriptFunctions.ts b/src/NetscriptFunctions.ts index d6998449e..dfb9dc060 100644 --- a/src/NetscriptFunctions.ts +++ b/src/NetscriptFunctions.ts @@ -1465,34 +1465,19 @@ const base: InternalAPI = { }, write: (ctx: NetscriptContext) => - (_port: unknown, data: unknown = "", _mode: unknown = "a"): void => { + (_port: unknown, _data: unknown = "", _mode: unknown = "a"): void => { const port = helpers.string(ctx, "port", _port); + const data = helpers.string(ctx, "data", _data); const mode = helpers.string(ctx, "mode", _mode); if (isString(port)) { // Write to script or text file let fn = port; - if (!isValidFilePath(fn)) { - throw helpers.makeRuntimeErrorMsg(ctx, `Invalid filepath: ${fn}`); - } + if (!isValidFilePath(fn)) throw helpers.makeRuntimeErrorMsg(ctx, `Invalid filepath: ${fn}`); - if (fn.lastIndexOf("/") === 0) { - fn = removeLeadingSlash(fn); - } + if (fn.lastIndexOf("/") === 0) fn = removeLeadingSlash(fn); - // Coerce 'data' to be a string - try { - data = String(data); - } catch (e: unknown) { - throw helpers.makeRuntimeErrorMsg( - ctx, - `Invalid data (${String(e)}). Data being written must be convertible to a string`, - ); - } + const server = helpers.getServer(ctx, ctx.workerScript.hostname); - const server = ctx.workerScript.getServer(); - if (server == null) { - throw helpers.makeRuntimeErrorMsg(ctx, "Error getting Server. This is a bug. Report to dev."); - } if (isScriptFilename(fn)) { // Write to script let script = ctx.workerScript.getScriptOnServer(fn, server); diff --git a/src/NetscriptJSEvaluator.ts b/src/NetscriptJSEvaluator.ts index 46a9ef5c1..e7fff05b0 100644 --- a/src/NetscriptJSEvaluator.ts +++ b/src/NetscriptJSEvaluator.ts @@ -11,6 +11,8 @@ import { WorkerScript } from "./Netscript/WorkerScript"; import { Script } from "./Script/Script"; import { areImportsEquals } from "./Terminal/DirectoryHelpers"; import { IPlayer } from "./PersonObjects/IPlayer"; +import { ScriptModule } from "./Script/ScriptModule"; +import { queue } from "jquery"; // Acorn type def is straight up incomplete so we have to fill with our own. export type Node = any; @@ -20,8 +22,23 @@ function makeScriptBlob(code: string): Blob { return new Blob([code], { type: "text/javascript" }); } -export async function compile(player: IPlayer, script: Script, scripts: Script[]): Promise { - if (!shouldCompile(script, scripts)) return; +export async function compile(player: IPlayer, script: Script, scripts: Script[]): Promise { + //!shouldCompile ensures that script.module is non-null, hence the "as". + if (!shouldCompile(script, scripts)) return script.module as Promise; + script.queueCompile = true; + //If we're already in the middle of compiling (script.module has not resolved yet), wait for the previous compilation to finish + //If script.module is null, this does nothing. + await script.module; + //If multiple compiles were called on the same script before a compilation could be completed this ensures only one complilation is actually performed. + if (!script.queueCompile) return script.module as Promise; + script.queueCompile = false; + script.updateRamUsage(player, scripts); + const uurls = _getScriptUrls(script, scripts, []); + const url = uurls[uurls.length - 1].url; + if (script.url && script.url !== url) URL.revokeObjectURL(script.url); + + if (script.dependencies.length > 0) script.dependencies.forEach((dep) => URL.revokeObjectURL(dep.url)); + script.url = uurls[uurls.length - 1].url; // The URL at the top is the one we want to import. It will // recursively import all the other modules in the urlStack. // @@ -29,27 +46,9 @@ export async function compile(player: IPlayer, script: Script, scripts: Script[] // but not really behaves like import. Particularly, it cannot // load fully dynamic content. So we hide the import from webpack // by placing it inside an eval call. - script.updateRamUsage(player, scripts); - const uurls = _getScriptUrls(script, scripts, []); - const url = uurls[uurls.length - 1].url; - if (script.url && script.url !== url) { - URL.revokeObjectURL(script.url); - // Thoughts: Should we be revoking any URLs here? - // If a script is modified repeatedly between two states, - // we could reuse the blob at a later time. - // BlobCache.removeByValue(script.url); - // URL.revokeObjectURL(script.url); - // if (script.dependencies.length > 0) { - // script.dependencies.forEach((dep) => { - // removeBlobFromCache(dep.url); - // URL.revokeObjectURL(dep.url); - // }); - // } - } - if (script.dependencies.length > 0) script.dependencies.forEach((dep) => URL.revokeObjectURL(dep.url)); - script.url = uurls[uurls.length - 1].url; script.module = new Promise((resolve) => resolve(eval("import(uurls[uurls.length - 1].url)"))); script.dependencies = uurls; + return script.module; } // Begin executing a user JS script, and return a promise that resolves @@ -67,9 +66,8 @@ export async function executeJSScript( ): Promise { const script = workerScript.getScript(); if (script === null) throw new Error("script is null"); - await compile(player, script, scripts); + const loadedModule = await compile(player, script, scripts); workerScript.ramUsage = script.ramUsage; - const loadedModule = await script.module; const ns = workerScript.env.vars; @@ -113,7 +111,11 @@ function isDependencyOutOfDate(filename: string, scripts: Script[], scriptModule */ function shouldCompile(script: Script, scripts: Script[]): boolean { if (!script.module) return true; - return script.dependencies.some((dep) => isDependencyOutOfDate(dep.filename, scripts, script.moduleSequenceNumber)); + if (script.dependencies.some((dep) => isDependencyOutOfDate(dep.filename, scripts, script.moduleSequenceNumber))) { + script.module = null; + return true; + } + return false; } // Gets a stack of blob urls, the top/right-most element being diff --git a/src/Script/Script.ts b/src/Script/Script.ts index ba0079c8c..e70c0433b 100644 --- a/src/Script/Script.ts +++ b/src/Script/Script.ts @@ -46,15 +46,16 @@ export class Script { ramUsage = 0; ramUsageEntries?: RamUsageEntry[]; + // Used to deconflict multiple simultaneous compilations. + queueCompile = false; + // hostname of server that this script is on. server = ""; constructor(player: IPlayer | null = null, fn = "", code = "", server = "", otherScripts: Script[] = []) { this.filename = fn; this.code = code; - this.ramUsage = 0; this.server = server; // hostname of server this script is on - this.module = null; this.moduleSequenceNumber = ++globalModuleSequenceNumber; if (this.code !== "" && player !== null) { this.updateRamUsage(player, otherScripts); @@ -105,7 +106,7 @@ export class Script { const [dependentScript] = otherScripts.filter( (s) => s.filename === dependent.filename && s.server == dependent.server, ); - if (dependentScript !== null) dependentScript.markUpdated(); + dependentScript?.markUpdated(); } } diff --git a/src/Terminal/determineAllPossibilitiesForTabCompletion.ts b/src/Terminal/determineAllPossibilitiesForTabCompletion.ts index 81a94390f..7355c7d1d 100644 --- a/src/Terminal/determineAllPossibilitiesForTabCompletion.ts +++ b/src/Terminal/determineAllPossibilitiesForTabCompletion.ts @@ -285,10 +285,8 @@ export async function determineAllPossibilitiesForTabCompletion( return processFilepath(script.filename) === fn || script.filename === "/" + fn; }); if (!script) return; // Doesn't exist. - if (!script.module) { - await compile(p, script, currServ.scripts); - } - const loadedModule = await script.module; + //Will return the already compiled module if recompilation not needed. + const loadedModule = await compile(p, script, currServ.scripts); if (!loadedModule || !loadedModule.autocomplete) return; // Doesn't have an autocomplete function. const runArgs = { "--tail": Boolean, "-t": Number }; From 1dc1a7ed6f22d7b25124984ac91b0bcb3774738d Mon Sep 17 00:00:00 2001 From: Snarling <84951833+Snarling@users.noreply.github.com> Date: Fri, 19 Aug 2022 18:36:24 -0400 Subject: [PATCH 5/5] lint --- src/NetscriptJSEvaluator.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/NetscriptJSEvaluator.ts b/src/NetscriptJSEvaluator.ts index e7fff05b0..8e5f00861 100644 --- a/src/NetscriptJSEvaluator.ts +++ b/src/NetscriptJSEvaluator.ts @@ -12,7 +12,6 @@ import { Script } from "./Script/Script"; import { areImportsEquals } from "./Terminal/DirectoryHelpers"; import { IPlayer } from "./PersonObjects/IPlayer"; import { ScriptModule } from "./Script/ScriptModule"; -import { queue } from "jquery"; // Acorn type def is straight up incomplete so we have to fill with our own. export type Node = any;