diff --git a/src/Netscript/WorkerScript.ts b/src/Netscript/WorkerScript.ts index 52f5d1037..eb8a21ea2 100644 --- a/src/Netscript/WorkerScript.ts +++ b/src/Netscript/WorkerScript.ts @@ -68,7 +68,7 @@ export class WorkerScript { * Used for static RAM calculation. Stores names of all functions that have * already been checked by this script */ - loadedFns: IMap = {}; + loadedFns: IMap = {}; /** * Filename of script diff --git a/src/Script/RamCalculations.d.ts b/src/Script/RamCalculations.d.ts deleted file mode 100644 index 7a7b889c1..000000000 --- a/src/Script/RamCalculations.d.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { Script } from "./Script"; - -export declare function calculateRamUsage(codeCopy: string, otherScripts: Script[]): number; diff --git a/src/Script/RamCalculations.js b/src/Script/RamCalculations.ts similarity index 85% rename from src/Script/RamCalculations.js rename to src/Script/RamCalculations.ts index 7e3d3bd9d..126d6b98d 100644 --- a/src/Script/RamCalculations.js +++ b/src/Script/RamCalculations.ts @@ -11,6 +11,8 @@ import { parse } from "acorn"; import { RamCalculationErrorCode } from "./RamCalculationErrorCodes"; import { RamCosts, RamCostConstants } from "../Netscript/RamCostGenerator"; +import { Script } from "../Script/Script"; +import { WorkerScript } from "../Netscript/WorkerScript"; // These special strings are used to reference the presence of a given logical // construct within a user script. @@ -29,7 +31,11 @@ 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(otherScripts, code, workerScript) { +async function parseOnlyRamCalculate( + otherScripts: Script[], + code: string, + workerScript: WorkerScript, +): Promise { try { /** * Maps dependent identifiers to their dependencies. @@ -41,16 +47,16 @@ async function parseOnlyRamCalculate(otherScripts, code, workerScript) { * We walk the dependency graph to calculate RAM usage, given that some identifiers * reference Netscript functions which have a RAM cost. */ - let dependencyMap = {}; + let dependencyMap: { [key: string]: string[] } = {}; // Scripts we've parsed. const completedParses = new Set(); // Scripts we've discovered that need to be parsed. - const parseQueue = []; + const parseQueue: string[] = []; // Parses a chunk of code with a given module name, and updates parseQueue and dependencyMap. - function parseCode(code, moduleName) { + function parseCode(code: string, moduleName: string): void { const result = parseOnlyCalculateDeps(code, moduleName); completedParses.add(moduleName); @@ -72,6 +78,7 @@ async function parseOnlyRamCalculate(otherScripts, code, workerScript) { // Process additional modules, which occurs if the "main" script has any imports while (parseQueue.length > 0) { const nextModule = parseQueue.shift(); + if (nextModule === undefined) throw new Error("nextModule should not be undefined"); // Additional modules can either be imported from the web (in which case we use // a dynamic import), or from other in-game scripts @@ -122,6 +129,7 @@ async function parseOnlyRamCalculate(otherScripts, code, workerScript) { const resolvedRefs = new Set(); while (unresolvedRefs.length > 0) { const ref = unresolvedRefs.shift(); + if (ref === undefined) throw new Error("ref should not be undefined"); // Check if this is one of the special keys, and add the appropriate ram cost if so. if (ref === "hacknet" && !resolvedRefs.has("hacknet")) { @@ -154,7 +162,7 @@ async function parseOnlyRamCalculate(otherScripts, code, workerScript) { // Check if this identifier is a function in the workerScript environment. // If it is, then we need to get its RAM cost. try { - function applyFuncRam(func) { + function applyFuncRam(func: any) { if (typeof func === "function") { try { let res; @@ -216,27 +224,28 @@ async function parseOnlyRamCalculate(otherScripts, code, workerScript) { * for RAM usage calculations. It also returns an array of additional modules * that need to be parsed (i.e. are 'import'ed scripts). */ -function parseOnlyCalculateDeps(code, currentModule) { +function parseOnlyCalculateDeps(code: string, currentModule: string): any { const ast = parse(code, { sourceType: "module", ecmaVersion: "latest" }); // Everything from the global scope goes in ".". Everything else goes in ".function", where only // the outermost layer of functions counts. const globalKey = currentModule + memCheckGlobalKey; - const dependencyMap = {}; - dependencyMap[globalKey] = new Set(); + const dependencyMap: { [key: string]: Set | undefined } = {}; + dependencyMap[globalKey] = new Set(); // If we reference this internal name, we're really referencing that external name. // Filled when we import names from other modules. - let internalToExternal = {}; + let internalToExternal: { [key: string]: string | undefined } = {}; - var additionalModules = []; + let additionalModules: string[] = []; // References get added pessimistically. They are added for thisModule.name, name, and for // any aliases. - function addRef(key, name) { + function addRef(key: string, name: string): void { const s = dependencyMap[key] || (dependencyMap[key] = new Set()); - if (name in internalToExternal) { - s.add(internalToExternal[name]); + const external = internalToExternal[name]; + if (external !== undefined) { + s.add(external); } s.add(currentModule + "." + name); s.add(name); // For builtins like hack. @@ -249,36 +258,36 @@ function parseOnlyCalculateDeps(code, currentModule) { // walkDeeper is for doing recursive walks of expressions in composites that we handle. function commonVisitors() { return { - Identifier: (node, st) => { + Identifier: (node: any, st: any) => { if (objectPrototypeProperties.includes(node.name)) { return; } addRef(st.key, node.name); }, - WhileStatement: (node, st, walkDeeper) => { + WhileStatement: (node: any, st: any, walkDeeper: any) => { addRef(st.key, specialReferenceWHILE); node.test && walkDeeper(node.test, st); node.body && walkDeeper(node.body, st); }, - DoWhileStatement: (node, st, walkDeeper) => { + DoWhileStatement: (node: any, st: any, walkDeeper: any) => { addRef(st.key, specialReferenceWHILE); node.test && walkDeeper(node.test, st); node.body && walkDeeper(node.body, st); }, - ForStatement: (node, st, walkDeeper) => { + ForStatement: (node: any, st: any, walkDeeper: any) => { addRef(st.key, specialReferenceFOR); node.init && walkDeeper(node.init, st); node.test && walkDeeper(node.test, st); node.update && walkDeeper(node.update, st); node.body && walkDeeper(node.body, st); }, - IfStatement: (node, st, walkDeeper) => { + IfStatement: (node: any, st: any, walkDeeper: any) => { addRef(st.key, specialReferenceIF); node.test && walkDeeper(node.test, st); node.consequent && walkDeeper(node.consequent, st); node.alternate && walkDeeper(node.alternate, st); }, - MemberExpression: (node, st, walkDeeper) => { + MemberExpression: (node: any, st: any, walkDeeper: any) => { node.object && walkDeeper(node.object, st); node.property && walkDeeper(node.property, st); }, @@ -290,13 +299,15 @@ function parseOnlyCalculateDeps(code, currentModule) { { key: globalKey }, Object.assign( { - ImportDeclaration: (node, st) => { + ImportDeclaration: (node: any, st: any) => { const importModuleName = node.source.value; additionalModules.push(importModuleName); // This module's global scope refers to that module's global scope, no matter how we // import it. - dependencyMap[st.key].add(importModuleName + memCheckGlobalKey); + const set = dependencyMap[st.key]; + if (set === undefined) throw new Error("set should not be undefined"); + set.add(importModuleName + memCheckGlobalKey); for (let i = 0; i < node.specifiers.length; ++i) { const spec = node.specifiers[i]; @@ -305,11 +316,13 @@ function parseOnlyCalculateDeps(code, currentModule) { internalToExternal[spec.local.name] = importModuleName + "." + spec.imported.name; } else { // We depend on everything. - dependencyMap[st.key].add(importModuleName + ".*"); + const set = dependencyMap[st.key]; + if (set === undefined) throw new Error("set should not be undefined"); + set.add(importModuleName + ".*"); } } }, - FunctionDeclaration: (node) => { + FunctionDeclaration: (node: any) => { const key = currentModule + "." + node.id.name; walk.recursive(node, { key: key }, commonVisitors()); }, @@ -327,7 +340,10 @@ function parseOnlyCalculateDeps(code, currentModule) { * @param {Script[]} otherScripts - All other scripts on the server. * Used to account for imported scripts */ -export async function calculateRamUsage(codeCopy, otherScripts) { +export async function calculateRamUsage( + codeCopy: string, + otherScripts: Script[], +): Promise { // We don't need a real WorkerScript for this. Just an object that keeps // track of whatever's needed for RAM calculations const workerScript = { @@ -335,7 +351,7 @@ export async function calculateRamUsage(codeCopy, otherScripts) { env: { vars: RamCosts, }, - }; + } as WorkerScript; try { return await parseOnlyRamCalculate(otherScripts, codeCopy, workerScript);