convert to ts

This commit is contained in:
Olivier Gagnon 2021-09-24 18:13:20 -04:00
parent 4abc1df840
commit ad75fa5ebc
3 changed files with 42 additions and 29 deletions

@ -68,7 +68,7 @@ export class WorkerScript {
* Used for static RAM calculation. Stores names of all functions that have * Used for static RAM calculation. Stores names of all functions that have
* already been checked by this script * already been checked by this script
*/ */
loadedFns: IMap<string> = {}; loadedFns: IMap<boolean> = {};
/** /**
* Filename of script * Filename of script

@ -1,3 +0,0 @@
import { Script } from "./Script";
export declare function calculateRamUsage(codeCopy: string, otherScripts: Script[]): number;

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