mirror of
https://github.com/bitburner-official/bitburner-src.git
synced 2024-12-19 12:45:45 +01:00
Merge pull request #366 from danielyxie/jsinterpreter-import
Added import functionality to Netscript 1.0 with new JS Interpreter
This commit is contained in:
commit
cdfcfb7df7
2
dist/engine.bundle.js
vendored
2
dist/engine.bundle.js
vendored
File diff suppressed because one or more lines are too long
112
dist/vendor.bundle.js
vendored
112
dist/vendor.bundle.js
vendored
File diff suppressed because one or more lines are too long
81
package-lock.json
generated
81
package-lock.json
generated
@ -2180,8 +2180,7 @@
|
||||
"deep-is": {
|
||||
"version": "0.1.3",
|
||||
"resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz",
|
||||
"integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=",
|
||||
"dev": true
|
||||
"integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ="
|
||||
},
|
||||
"define-properties": {
|
||||
"version": "1.1.2",
|
||||
@ -2643,33 +2642,27 @@
|
||||
"dev": true
|
||||
},
|
||||
"escodegen": {
|
||||
"version": "1.8.1",
|
||||
"resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.8.1.tgz",
|
||||
"integrity": "sha1-WltTr0aTEQvrsIZ6o0MN07cKEBg=",
|
||||
"dev": true,
|
||||
"version": "1.11.0",
|
||||
"resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.11.0.tgz",
|
||||
"integrity": "sha512-IeMV45ReixHS53K/OmfKAIztN/igDHzTJUhZM3k1jMhIZWjk45SMwAtBsEXiJp3vSPmTcu6CXn7mDvFHRN66fw==",
|
||||
"requires": {
|
||||
"esprima": "2.7.3",
|
||||
"estraverse": "1.9.3",
|
||||
"esprima": "3.1.3",
|
||||
"estraverse": "4.2.0",
|
||||
"esutils": "2.0.2",
|
||||
"optionator": "0.8.2",
|
||||
"source-map": "0.2.0"
|
||||
"source-map": "0.6.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"estraverse": {
|
||||
"version": "1.9.3",
|
||||
"resolved": "https://registry.npmjs.org/estraverse/-/estraverse-1.9.3.tgz",
|
||||
"integrity": "sha1-r2fy3JIlgkFZUJJgkaQAXSnJu0Q=",
|
||||
"dev": true
|
||||
"esprima": {
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/esprima/-/esprima-3.1.3.tgz",
|
||||
"integrity": "sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM="
|
||||
},
|
||||
"source-map": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.2.0.tgz",
|
||||
"integrity": "sha1-2rc/vPwrqBm03gO9b26qSBZLP50=",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"amdefine": "1.0.1"
|
||||
}
|
||||
"version": "0.6.1",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
|
||||
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -2901,8 +2894,7 @@
|
||||
"esutils": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz",
|
||||
"integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=",
|
||||
"dev": true
|
||||
"integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs="
|
||||
},
|
||||
"etag": {
|
||||
"version": "1.8.1",
|
||||
@ -3285,8 +3277,7 @@
|
||||
"fast-levenshtein": {
|
||||
"version": "2.0.6",
|
||||
"resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
|
||||
"integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=",
|
||||
"dev": true
|
||||
"integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc="
|
||||
},
|
||||
"fastparse": {
|
||||
"version": "1.1.1",
|
||||
@ -4719,6 +4710,25 @@
|
||||
"integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=",
|
||||
"dev": true
|
||||
},
|
||||
"escodegen": {
|
||||
"version": "1.8.1",
|
||||
"resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.8.1.tgz",
|
||||
"integrity": "sha1-WltTr0aTEQvrsIZ6o0MN07cKEBg=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"esprima": "2.7.3",
|
||||
"estraverse": "1.9.3",
|
||||
"esutils": "2.0.2",
|
||||
"optionator": "0.8.2",
|
||||
"source-map": "0.2.0"
|
||||
}
|
||||
},
|
||||
"estraverse": {
|
||||
"version": "1.9.3",
|
||||
"resolved": "https://registry.npmjs.org/estraverse/-/estraverse-1.9.3.tgz",
|
||||
"integrity": "sha1-r2fy3JIlgkFZUJJgkaQAXSnJu0Q=",
|
||||
"dev": true
|
||||
},
|
||||
"glob": {
|
||||
"version": "5.0.15",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz",
|
||||
@ -4744,6 +4754,16 @@
|
||||
"integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=",
|
||||
"dev": true
|
||||
},
|
||||
"source-map": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.2.0.tgz",
|
||||
"integrity": "sha1-2rc/vPwrqBm03gO9b26qSBZLP50=",
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"amdefine": "1.0.1"
|
||||
}
|
||||
},
|
||||
"supports-color": {
|
||||
"version": "3.2.3",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz",
|
||||
@ -5216,7 +5236,6 @@
|
||||
"version": "0.3.0",
|
||||
"resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz",
|
||||
"integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"prelude-ls": "1.1.2",
|
||||
"type-check": "0.3.2"
|
||||
@ -6716,7 +6735,6 @@
|
||||
"version": "0.8.2",
|
||||
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz",
|
||||
"integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"deep-is": "0.1.3",
|
||||
"fast-levenshtein": "2.0.6",
|
||||
@ -6729,8 +6747,7 @@
|
||||
"wordwrap": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz",
|
||||
"integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=",
|
||||
"dev": true
|
||||
"integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus="
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -7908,8 +7925,7 @@
|
||||
"prelude-ls": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz",
|
||||
"integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=",
|
||||
"dev": true
|
||||
"integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ="
|
||||
},
|
||||
"prepend-http": {
|
||||
"version": "1.0.4",
|
||||
@ -10500,7 +10516,6 @@
|
||||
"version": "0.3.2",
|
||||
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz",
|
||||
"integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"prelude-ls": "1.1.2"
|
||||
}
|
||||
|
@ -15,6 +15,7 @@
|
||||
"brace": "^0.11.1",
|
||||
"decimal.js": "7.2.3",
|
||||
"enhanced-resolve": "^4.0.0",
|
||||
"escodegen": "^1.11.0",
|
||||
"escope": "^3.6.0",
|
||||
"file-saver": "^1.3.8",
|
||||
"interpret": "^1.0.0",
|
||||
|
@ -487,7 +487,12 @@ let CONSTANTS = {
|
||||
|
||||
LatestUpdate:
|
||||
"v0.40.0<br>" +
|
||||
"* Netscript 1.0 (NS1) now uses a fully-fledged ES5 Javascript Interpreter. This means many new features are now available in NS, and this also fixes several bugs.<br>" +
|
||||
"* Netscript 1.0 (NS1) now uses a fully-fledged ES5 Javascript Interpreter. This means many new features are now available in NS1, and this also fixes several bugs." +
|
||||
" However this also means any ES6+ features are no longer supported in NS1 <br>" +
|
||||
"* When a server is hacked with a very large number of threads and left with no money, the server's security level " +
|
||||
"now only increases by however many threads were needed to drain the server. For example, if you hack a server with " +
|
||||
"5000 threads but it only needed 2000 threads to deplete the server's money, then the server's security will only increase " +
|
||||
"as if you had hacked it with 2000 threads (change by hydroflame)<br>" +
|
||||
"* Added getCurrentAction() to Bladeburner API<br>" +
|
||||
"* Completely re-designed the Hacknet Node API<br>" +
|
||||
"* getSkillLevel() in Bladeburner API now returns an error if no argument is passed in (as opposed to an object with all skill levels). This may break scripts<br>" +
|
||||
@ -495,7 +500,7 @@ let CONSTANTS = {
|
||||
"* HP is now reset (restored) when Augmenting<br>" +
|
||||
"* Source-File 6 now increases both the level and experience gain of all combat stats (it was only experience gain previously)<br>" +
|
||||
"* Reverted a previous change for Source-File 12. It's benefits are now multiplicative rather than additive<br>" +
|
||||
"* Starting Infiltration security level for almost every location decreased by ~10%<br>" +
|
||||
"* Starting Infiltration security level for almost every location decreased by ~10%<br>" +
|
||||
"* Bug Fix: Infiltration buttons can no longer be clicked through NetscriptJS<br>"
|
||||
}
|
||||
|
||||
|
@ -14,13 +14,18 @@ import {NetscriptPort} from "./NetscriptPort";
|
||||
import {AllServers} from "./Server";
|
||||
import {Settings} from "./Settings";
|
||||
|
||||
import {parse} from "../utils/acorn";
|
||||
//TODO Maybe escodegen might be better?
|
||||
import {generate} from 'escodegen';
|
||||
|
||||
import {parse, Node} from "../utils/acorn";
|
||||
import {dialogBoxCreate} from "../utils/DialogBox";
|
||||
import {compareArrays} from "../utils/helpers/compareArrays";
|
||||
import {arrayToString} from "../utils/helpers/arrayToString";
|
||||
import {roundToTwo} from "../utils/helpers/roundToTwo";
|
||||
import {isString} from "../utils/StringHelperFunctions";
|
||||
|
||||
const walk = require("acorn/dist/walk");
|
||||
|
||||
function WorkerScript(runningScriptObj) {
|
||||
this.name = runningScriptObj.filename;
|
||||
this.running = false;
|
||||
@ -160,6 +165,17 @@ function startNetscript1Script(workerScript) {
|
||||
var code = workerScript.code;
|
||||
workerScript.running = true;
|
||||
|
||||
//Process imports
|
||||
var ast;
|
||||
try {
|
||||
ast = processNetscript1Imports(code, workerScript);
|
||||
} catch(e) {
|
||||
dialogBoxCreate("Error processing Imports in " + workerScript.name + ":<br>" + e);
|
||||
workerScript.env.stopFlag = true;
|
||||
workerScript.running = false;
|
||||
return;
|
||||
}
|
||||
|
||||
var interpreterInitialization = function(int, scope) {
|
||||
//Add the Netscript environment
|
||||
var ns = NetscriptFunctions(workerScript);
|
||||
@ -209,7 +225,7 @@ function startNetscript1Script(workerScript) {
|
||||
|
||||
var interpreter;
|
||||
try {
|
||||
interpreter = new Interpreter(code, interpreterInitialization);
|
||||
interpreter = new Interpreter(ast, interpreterInitialization);
|
||||
} catch(e) {
|
||||
dialogBoxCreate("Syntax ERROR in " + workerScript.name + ":<br>" + e);
|
||||
workerScript.env.stopFlag = true;
|
||||
@ -240,8 +256,6 @@ function startNetscript1Script(workerScript) {
|
||||
try {
|
||||
runInterpreter();
|
||||
} catch(e) {
|
||||
console.log("Caught in original");
|
||||
console.log(e);
|
||||
if (isString(e)) {
|
||||
workerScript.errorMessage = e;
|
||||
return reject(workerScript);
|
||||
@ -252,8 +266,122 @@ function startNetscript1Script(workerScript) {
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/* Since the JS Interpreter used for Netscript 1.0 only supports ES5, the keyword
|
||||
'import' throws an error. However, since we want to support import funtionality
|
||||
we'll implement it ourselves by parsing the Nodes in the AST out.
|
||||
|
||||
@param code - The script's code
|
||||
@returns - ES5-compliant AST with properly imported functions
|
||||
*/
|
||||
function processNetscript1Imports(code, workerScript) {
|
||||
//allowReserved prevents 'import' from throwing error in ES5
|
||||
var ast = parse(code, {ecmaVersion:6, allowReserved:true, sourceType:"module"});
|
||||
|
||||
var server = workerScript.getServer();
|
||||
if (server == null) {
|
||||
throw new Error("Failed to find underlying Server object for script");
|
||||
}
|
||||
|
||||
function getScript(scriptName) {
|
||||
for (let i = 0; i < server.scripts.length; ++i) {
|
||||
if (server.scripts[i].filename === scriptName) {
|
||||
return server.scripts[i];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
var generatedCode = ""; //Generated Javascript Code
|
||||
|
||||
//Walk over the tree and process ImportDeclaration nodes
|
||||
walk.simple(ast, {
|
||||
ImportDeclaration: (node) => {
|
||||
let scriptName = node.source.value;
|
||||
let script = getScript(scriptName);
|
||||
if (script == null) {
|
||||
throw new Error("'Import' failed due to invalid script: " + scriptName);
|
||||
}
|
||||
let scriptAst = parse(script.code, {ecmaVersion:5, allowReserved:true, sourceType:"module"});
|
||||
|
||||
if (node.specifiers.length === 1 && node.specifiers[0].type === "ImportNamespaceSpecifier") {
|
||||
//import * as namespace from script
|
||||
let namespace = node.specifiers[0].local.name;
|
||||
let fnNames = []; //Names only
|
||||
let fnDeclarations = []; //FunctionDeclaration Node objects
|
||||
walk.simple(scriptAst, {
|
||||
FunctionDeclaration: (node) => {
|
||||
fnNames.push(node.id.name);
|
||||
fnDeclarations.push(node);
|
||||
}
|
||||
});
|
||||
|
||||
//Now we have to generate the code that would create the namespace
|
||||
generatedCode =
|
||||
"var " + namespace + ";\n" +
|
||||
"(function (namespace) {\n";
|
||||
|
||||
//Add the function declarations
|
||||
fnDeclarations.forEach((fn) => {
|
||||
generatedCode += generate(fn);
|
||||
generatedCode += "\n";
|
||||
});
|
||||
|
||||
//Add functions to namespace
|
||||
fnNames.forEach((fnName) => {
|
||||
generatedCode += ("namespace." + fnName + " = " + fnName);
|
||||
generatedCode += "\n";
|
||||
});
|
||||
|
||||
//Finish
|
||||
generatedCode += (
|
||||
"})(" + namespace + " || " + "(" + namespace + " = {}));"
|
||||
)
|
||||
} else {
|
||||
//import {...} from script
|
||||
|
||||
//Get array of all fns to import
|
||||
let fnsToImport = [];
|
||||
node.specifiers.forEach((e) => {
|
||||
fnsToImport.push(e.local.name);
|
||||
});
|
||||
|
||||
//Walk through script and get FunctionDeclaration code for all specified fns
|
||||
let fnDeclarations = [];
|
||||
walk.simple(scriptAst, {
|
||||
FunctionDeclaration: (node) => {
|
||||
if (fnsToImport.includes(node.id.name)) {
|
||||
fnDeclarations.push(node);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
//Convert FunctionDeclarations into code
|
||||
fnDeclarations.forEach((fn) => {
|
||||
generatedCode += generate(fn);
|
||||
generatedCode += "\n";
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
//Remove ImportDeclarations from AST. These ImportDeclarations must be in top-level
|
||||
if (ast.type !== "Program" || ast.body == null) {
|
||||
throw new Error("Code could not be properly parsed");
|
||||
}
|
||||
for (let i = ast.body.length-1; i >= 0; --i) {
|
||||
if (ast.body[i].type === "ImportDeclaration") {
|
||||
ast.body.splice(i, 1);
|
||||
}
|
||||
}
|
||||
|
||||
//Convert the AST back into code
|
||||
code = generate(ast);
|
||||
|
||||
//Add the imported code and re-generate in ES5 (JS Interpreter for NS1 only supports ES5);
|
||||
code = generatedCode + code;
|
||||
return parse(code, {ecmaVersion:5});
|
||||
}
|
||||
|
||||
//Loop through workerScripts and run every script that is not currently running
|
||||
|
@ -192,7 +192,7 @@ $(document).keydown(function(event) {
|
||||
|
||||
if (event.keyCode === KEY.TAB) {
|
||||
event.preventDefault();
|
||||
|
||||
|
||||
//Autocomplete
|
||||
if (terminalInput == null) {return;}
|
||||
var input = terminalInput.value;
|
||||
@ -1253,7 +1253,7 @@ let Terminal = {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
post("Error: Invalid file. Only scripts (.script), text files (.txt), or .fconf can be edited with nano"); return;
|
||||
post("Error: Invalid file. Only scripts (.script, .ns, .js), text files (.txt), or .fconf can be edited with nano"); return;
|
||||
}
|
||||
Engine.loadScriptEditorContent(filename);
|
||||
break;
|
||||
|
Loading…
Reference in New Issue
Block a user