2017-08-30 19:44:29 +02:00
import { BitNodeMultipliers } from "./BitNode.js" ;
import { CONSTANTS } from "./Constants.js" ;
import { Player } from "./Player.js" ;
import { Environment } from "./NetscriptEnvironment.js" ;
import { WorkerScript , addWorkerScript } from "./NetscriptWorker.js" ;
import { Server } from "./Server.js" ;
import { Settings } from "./Settings.js" ;
import { Script , findRunningScript ,
RunningScript } from "./Script.js" ;
2017-09-29 17:02:33 +02:00
import { Node } from "../utils/acorn.js" ;
2017-08-30 19:44:29 +02:00
import { printArray } from "../utils/HelperFunctions.js" ;
import { isValidIPAddress } from "../utils/IPAddress.js" ;
import { isString } from "../utils/StringHelperFunctions.js" ;
2017-11-01 23:56:30 +01:00
var Promise = require ( "bluebird" ) ;
Promise . config ( {
warnings : false ,
longStackTraces : false ,
cancellation : true ,
monitoring : false
} ) ;
2016-11-17 23:25:40 +01:00
/ * E v a l u a t o r
2017-11-01 23:56:30 +01:00
* Evaluates / Interprets the Abstract Syntax Tree generated by Acorns parser
2017-09-05 03:03:29 +02:00
*
* Returns a promise
2016-11-17 23:25:40 +01:00
* /
2016-11-30 00:07:24 +01:00
function evaluate ( exp , workerScript ) {
2017-11-01 23:56:30 +01:00
return Promise . delay ( Settings . CodeInstructionRunTime ) . then ( function ( ) {
var env = workerScript . env ;
if ( env . stopFlag ) { return Promise . reject ( workerScript ) ; }
if ( exp == null ) {
return Promise . reject ( makeRuntimeRejectMsg ( workerScript , "Error: NULL expression" ) ) ;
}
if ( env . stopFlag ) { return Promise . reject ( workerScript ) ; }
2017-06-05 06:48:37 +02:00
switch ( exp . type ) {
2017-06-28 11:47:42 +02:00
case "BlockStatement" :
case "Program" :
var evaluateProgPromise = evaluateProg ( exp , workerScript , 0 ) ; //TODO: make every block/program use individual enviroment
2017-11-01 23:56:30 +01:00
return evaluateProgPromise . then ( function ( w ) {
return Promise . resolve ( workerScript ) ;
} ) . catch ( function ( e ) {
2017-09-29 17:02:33 +02:00
if ( e . constructor === Array && e . length === 2 && e [ 0 ] === "RETURNSTATEMENT" ) {
2017-11-01 23:56:30 +01:00
return Promise . reject ( e ) ;
2017-09-29 17:02:33 +02:00
} else if ( isString ( e ) ) {
2017-06-28 11:47:42 +02:00
workerScript . errorMessage = e ;
2017-11-01 23:56:30 +01:00
return Promise . reject ( workerScript ) ;
2017-06-28 11:47:42 +02:00
} else if ( e instanceof WorkerScript ) {
2017-11-01 23:56:30 +01:00
return Promise . reject ( e ) ;
2017-06-11 00:44:33 +02:00
} else {
2017-11-01 23:56:30 +01:00
return Promise . reject ( workerScript ) ;
2017-06-11 00:44:33 +02:00
}
2017-06-28 11:47:42 +02:00
} ) ;
break ;
case "Literal" :
2017-11-01 23:56:30 +01:00
return Promise . resolve ( exp . value ) ;
2017-06-28 11:47:42 +02:00
break ;
case "Identifier" :
2018-01-27 07:52:39 +01:00
//Javascript constructor() method can be used as an exploit to run arbitrary code
if ( exp . name == "constructor" ) {
return Promise . reject ( makeRuntimeRejectMsg ( workerScript , "Illegal usage of constructor() method. If you have your own function named 'constructor', you must re-name it." ) ) ;
}
2017-06-28 11:47:42 +02:00
if ( ! ( exp . name in env . vars ) ) {
2017-11-01 23:56:30 +01:00
return Promise . reject ( makeRuntimeRejectMsg ( workerScript , "variable " + exp . name + " not defined" ) ) ;
2017-06-05 06:48:37 +02:00
}
2017-11-01 23:56:30 +01:00
return Promise . resolve ( env . get ( exp . name ) )
2017-06-05 06:48:37 +02:00
break ;
2017-06-28 11:47:42 +02:00
case "ExpressionStatement" :
2017-11-01 23:56:30 +01:00
return evaluate ( exp . expression , workerScript ) ;
2017-06-05 06:48:37 +02:00
break ;
2017-06-28 11:47:42 +02:00
case "ArrayExpression" :
var argPromises = exp . elements . map ( function ( arg ) {
return evaluate ( arg , workerScript ) ;
} ) ;
2017-11-01 23:56:30 +01:00
return Promise . all ( argPromises ) . then ( function ( array ) {
return Promise . resolve ( array )
2017-06-05 06:48:37 +02:00
} ) ;
break ;
2017-06-28 11:47:42 +02:00
case "CallExpression" :
2017-11-01 23:56:30 +01:00
return evaluate ( exp . callee , workerScript ) . then ( function ( func ) {
return Promise . map ( exp . arguments , function ( arg ) {
return evaluate ( arg , workerScript ) ;
} ) . then ( function ( args ) {
2017-09-29 17:02:33 +02:00
if ( func instanceof Node ) { //Player-defined function
//Create new Environment for the function
//Should be automatically garbage collected...
var funcEnv = env . extend ( ) ;
//Define function arguments in this new environment
for ( var i = 0 ; i < func . params . length ; ++ i ) {
var arg ;
if ( i >= args . length ) {
arg = null ;
} else {
arg = args [ i ] ;
}
funcEnv . def ( func . params [ i ] . name , arg ) ;
}
//Create a new WorkerScript for this function evaluation
var funcWorkerScript = new WorkerScript ( workerScript . scriptRef ) ;
2017-10-12 04:00:22 +02:00
funcWorkerScript . serverIp = workerScript . serverIp ;
2017-09-29 17:02:33 +02:00
funcWorkerScript . env = funcEnv ;
2017-10-12 04:00:22 +02:00
workerScript . fnWorker = funcWorkerScript ;
2017-09-29 17:02:33 +02:00
2017-11-01 23:56:30 +01:00
return evaluate ( func . body , funcWorkerScript ) . then ( function ( res ) {
2017-10-10 06:56:48 +02:00
//If the function finished successfuly, that means there
//was no return statement since a return statement rejects. So resolve to null
2017-10-12 04:00:22 +02:00
workerScript . fnWorker = null ;
2017-11-01 23:56:30 +01:00
return Promise . resolve ( null ) ;
2017-09-29 17:02:33 +02:00
} ) . catch ( function ( e ) {
2017-10-10 06:56:48 +02:00
if ( e . constructor === Array && e . length === 2 && e [ 0 ] === "RETURNSTATEMENT" ) {
//Return statement from function
2017-11-01 23:56:30 +01:00
return Promise . resolve ( e [ 1 ] ) ;
2017-10-12 04:00:22 +02:00
workerScript . fnWorker = null ;
2017-10-10 06:56:48 +02:00
} else if ( isString ( e ) ) {
2017-11-01 23:56:30 +01:00
return Promise . reject ( makeRuntimeRejectMsg ( workerScript , e ) ) ;
2017-10-04 03:50:13 +02:00
} else if ( e instanceof WorkerScript ) {
//Parse out the err message from the WorkerScript and re-reject
var errorMsg = e . errorMessage ;
var errorTextArray = errorMsg . split ( "|" ) ;
if ( errorTextArray . length === 4 ) {
errorMsg = errorTextArray [ 3 ] ;
2017-11-01 23:56:30 +01:00
return Promise . reject ( makeRuntimeRejectMsg ( workerScript , errorMsg ) ) ;
2017-10-04 03:50:13 +02:00
} else {
2017-10-12 04:00:22 +02:00
if ( env . stopFlag ) {
2017-11-01 23:56:30 +01:00
return Promise . reject ( workerScript ) ;
2017-10-12 04:00:22 +02:00
} else {
2017-11-01 23:56:30 +01:00
return Promise . reject ( makeRuntimeRejectMsg ( workerScript , "Error in one of your functions. Could not identify which function" ) ) ;
2017-10-12 04:00:22 +02:00
}
2017-10-04 03:50:13 +02:00
}
} else if ( e instanceof Error ) {
2017-11-01 23:56:30 +01:00
return Promise . reject ( makeRuntimeRejectMsg ( workerScript , e . toString ( ) ) ) ;
2017-10-04 03:50:13 +02:00
}
2017-09-29 17:02:33 +02:00
} ) ;
} else if ( exp . callee . type == "MemberExpression" ) {
2017-11-01 23:56:30 +01:00
return evaluate ( exp . callee . object , workerScript ) . then ( function ( object ) {
2017-06-28 11:47:42 +02:00
try {
2018-02-24 23:55:06 +01:00
if ( func === "NETSCRIPTFOREACH" ) {
return evaluateForeach ( object , args , workerScript ) . then ( function ( res ) {
return Promise . resolve ( res ) ;
} ) . catch ( function ( e ) {
return Promise . reject ( e ) ;
} ) ;
}
2017-07-13 18:54:29 +02:00
var res = func . apply ( object , args ) ;
2017-11-01 23:56:30 +01:00
return Promise . resolve ( res ) ;
2017-06-28 11:47:42 +02:00
} catch ( e ) {
2017-11-01 23:56:30 +01:00
return Promise . reject ( makeRuntimeRejectMsg ( workerScript , e ) ) ;
2017-06-28 11:47:42 +02:00
}
2017-05-12 06:59:07 +02:00
} ) ;
} else {
2017-06-28 11:47:42 +02:00
try {
var out = func . apply ( null , args ) ;
if ( out instanceof Promise ) {
2017-11-01 23:56:30 +01:00
return out . then ( function ( res ) {
return Promise . resolve ( res )
2017-06-28 11:47:42 +02:00
} ) . catch ( function ( e ) {
2017-11-01 23:56:30 +01:00
return Promise . reject ( e ) ;
2017-06-28 11:47:42 +02:00
} ) ;
} else {
2017-11-01 23:56:30 +01:00
return Promise . resolve ( out ) ;
2017-06-28 11:47:42 +02:00
}
} catch ( e ) {
2017-07-13 18:54:29 +02:00
if ( isScriptErrorMessage ( e ) ) {
2017-11-01 23:56:30 +01:00
return Promise . reject ( e ) ;
2017-07-13 18:54:29 +02:00
} else {
2017-11-01 23:56:30 +01:00
return Promise . reject ( makeRuntimeRejectMsg ( workerScript , e ) ) ;
2017-07-13 18:54:29 +02:00
}
2017-06-28 11:47:42 +02:00
}
2017-05-12 06:59:07 +02:00
}
2017-06-28 11:47:42 +02:00
} ) ;
2017-05-12 06:59:07 +02:00
} ) ;
2017-06-05 06:48:37 +02:00
break ;
2017-06-28 11:47:42 +02:00
case "MemberExpression" :
2017-11-01 23:56:30 +01:00
return evaluate ( exp . object , workerScript ) . then ( function ( object ) {
2017-06-28 11:47:42 +02:00
if ( exp . computed ) {
2017-11-01 23:56:30 +01:00
return evaluate ( exp . property , workerScript ) . then ( function ( index ) {
2017-07-29 18:25:40 +02:00
if ( index >= object . length ) {
2017-11-01 23:56:30 +01:00
return Promise . reject ( makeRuntimeRejectMsg ( workerScript , "Invalid index for arrays" ) ) ;
2017-07-29 18:25:40 +02:00
}
2017-11-01 23:56:30 +01:00
return Promise . resolve ( object [ index ] ) ;
2017-06-28 11:47:42 +02:00
} ) . catch ( function ( e ) {
2017-11-09 02:55:21 +01:00
if ( e instanceof WorkerScript || isScriptErrorMessage ( e ) ) {
return Promise . reject ( e ) ;
} else {
return Promise . reject ( makeRuntimeRejectMsg ( workerScript , "Invalid MemberExpression" ) ) ;
}
2017-06-28 11:47:42 +02:00
} ) ;
} else {
2018-01-27 07:52:39 +01:00
if ( exp . property . name === "constructor" ) {
return Promise . reject ( makeRuntimeRejectMsg ( workerScript , "Illegal usage of constructor() method. If you have your own function named 'constructor', you must re-name it." ) ) ;
}
2018-02-24 23:55:06 +01:00
if ( object != null && object instanceof Array && exp . property . name === "forEach" ) {
return "NETSCRIPTFOREACH" ;
}
2017-06-28 11:47:42 +02:00
try {
2017-11-01 23:56:30 +01:00
return Promise . resolve ( object [ exp . property . name ] )
2017-06-28 11:47:42 +02:00
} catch ( e ) {
2017-11-01 23:56:30 +01:00
return Promise . reject ( makeRuntimeRejectMsg ( workerScript , "Failed to get property: " + e . toString ( ) ) ) ;
2017-06-28 11:47:42 +02:00
}
}
2017-06-05 06:48:37 +02:00
} ) ;
break ;
2017-06-28 11:47:42 +02:00
case "LogicalExpression" :
case "BinaryExpression" :
2017-11-02 22:47:09 +01:00
return evalBinary ( exp , workerScript ) ;
2017-06-05 06:48:37 +02:00
break ;
2017-07-27 04:56:14 +02:00
case "UnaryExpression" :
2017-11-02 22:47:09 +01:00
return evalUnary ( exp , workerScript ) ;
2017-07-27 04:56:14 +02:00
break ;
2017-06-28 11:47:42 +02:00
case "AssignmentExpression" :
2017-11-02 22:47:09 +01:00
return evalAssignment ( exp , workerScript ) ;
2017-06-05 06:48:37 +02:00
break ;
2017-06-28 11:47:42 +02:00
case "UpdateExpression" :
if ( exp . argument . type === "Identifier" ) {
if ( exp . argument . name in env . vars ) {
2017-11-01 23:56:30 +01:00
if ( exp . operator === "++" || exp . operator === "--" ) {
switch ( exp . operator ) {
case "++" :
env . set ( exp . argument . name , env . get ( exp . argument . name ) + 1 ) ;
break ;
case "--" :
env . set ( exp . argument . name , env . get ( exp . argument . name ) - 1 ) ;
break ;
default : break ;
}
return Promise . resolve ( env . get ( exp . argument . name ) ) ;
}
//Not sure what prefix UpdateExpressions there would be besides ++/--
2017-06-28 11:47:42 +02:00
if ( exp . prefix ) {
2017-11-01 23:56:30 +01:00
return Promise . resolve ( env . get ( exp . argument . name ) )
2017-06-28 11:47:42 +02:00
}
switch ( exp . operator ) {
default :
2017-11-01 23:56:30 +01:00
return Promise . reject ( makeRuntimeRejectMsg ( workerScript , "Unrecognized token: " + exp . type + ". You are trying to use code that is currently unsupported" ) ) ;
2017-06-05 19:50:32 +02:00
}
2017-11-01 23:56:30 +01:00
return Promise . resolve ( env . get ( exp . argument . name ) )
2017-06-28 11:47:42 +02:00
} else {
2017-11-01 23:56:30 +01:00
return Promise . reject ( makeRuntimeRejectMsg ( workerScript , "variable " + exp . argument . name + " not defined" ) ) ;
2017-06-26 01:39:17 +02:00
}
2017-06-05 06:48:37 +02:00
} else {
2017-11-01 23:56:30 +01:00
return Promise . reject ( makeRuntimeRejectMsg ( workerScript , "argument must be an identifier" ) ) ;
2017-06-28 11:47:42 +02:00
}
break ;
case "EmptyStatement" :
2017-11-01 23:56:30 +01:00
return Promise . resolve ( false ) ;
2017-06-28 11:47:42 +02:00
break ;
case "ReturnStatement" :
2017-11-01 23:56:30 +01:00
return evaluate ( exp . argument , workerScript ) . then ( function ( res ) {
return Promise . reject ( [ "RETURNSTATEMENT" , res ] ) ;
2017-09-29 17:02:33 +02:00
} ) ;
2017-06-28 11:47:42 +02:00
break ;
case "BreakStatement" :
2017-11-01 23:56:30 +01:00
return Promise . reject ( "BREAKSTATEMENT" ) ;
2017-06-28 11:47:42 +02:00
break ;
2017-09-21 23:27:31 +02:00
case "ContinueStatement" :
2017-11-01 23:56:30 +01:00
return Promise . reject ( "CONTINUESTATEMENT" ) ;
2017-09-21 23:27:31 +02:00
break ;
2017-06-28 11:47:42 +02:00
case "IfStatement" :
2017-11-02 22:47:09 +01:00
return evaluateIf ( exp , workerScript ) ;
2017-06-28 11:47:42 +02:00
break ;
case "SwitchStatement" :
2017-09-05 03:03:29 +02:00
var lineNum = getErrorLineNumber ( exp , workerScript ) ;
2017-11-01 23:56:30 +01:00
return Promise . reject ( makeRuntimeRejectMsg ( workerScript , "Switch statements are not yet implemented in Netscript (line " + ( lineNum + 1 ) + ")" ) ) ;
break ;
2017-06-28 11:47:42 +02:00
case "WhileStatement" :
2017-11-02 22:47:09 +01:00
return evaluateWhile ( exp , workerScript ) . then ( function ( res ) {
return Promise . resolve ( res ) ;
2017-06-28 11:47:42 +02:00
} ) . catch ( function ( e ) {
2017-08-15 22:22:46 +02:00
if ( e == "BREAKSTATEMENT" ||
( e instanceof WorkerScript && e . errorMessage == "BREAKSTATEMENT" ) ) {
2017-11-01 23:56:30 +01:00
return Promise . resolve ( "whileLoopBroken" ) ;
2017-08-15 22:22:46 +02:00
} else {
2017-11-01 23:56:30 +01:00
return Promise . reject ( e ) ;
2017-08-15 22:22:46 +02:00
}
2017-06-28 11:47:42 +02:00
} ) ;
break ;
case "ForStatement" :
2017-11-01 23:56:30 +01:00
return evaluate ( exp . init , workerScript ) . then ( function ( expInit ) {
2017-06-28 11:47:42 +02:00
return evaluateFor ( exp , workerScript ) ;
} ) . then ( function ( forLoopRes ) {
2017-11-01 23:56:30 +01:00
return Promise . resolve ( "forLoopDone" ) ;
2017-06-28 11:47:42 +02:00
} ) . catch ( function ( e ) {
2017-08-15 22:22:46 +02:00
if ( e == "BREAKSTATEMENT" ||
( e instanceof WorkerScript && e . errorMessage == "BREAKSTATEMENT" ) ) {
2017-11-01 23:56:30 +01:00
return Promise . resolve ( "forLoopBroken" ) ;
2017-08-15 22:22:46 +02:00
} else {
2017-11-01 23:56:30 +01:00
return Promise . reject ( e ) ;
2017-08-15 22:22:46 +02:00
}
2017-06-28 11:47:42 +02:00
} ) ;
2017-06-05 06:48:37 +02:00
break ;
2017-09-29 17:02:33 +02:00
case "FunctionDeclaration" :
if ( exp . id && exp . id . name ) {
env . set ( exp . id . name , exp ) ;
2017-11-01 23:56:30 +01:00
return Promise . resolve ( true ) ;
2017-09-29 17:02:33 +02:00
} else {
var lineNum = getErrorLineNumber ( exp , workerScript ) ;
2017-11-01 23:56:30 +01:00
return Promise . reject ( makeRuntimeRejectMsg ( workerScript , "Invalid function declaration at line " + lineNum + 1 ) ) ;
2017-09-29 17:02:33 +02:00
}
break ;
2017-06-05 06:48:37 +02:00
default :
2017-09-05 03:03:29 +02:00
var lineNum = getErrorLineNumber ( exp , workerScript ) ;
2017-11-01 23:56:30 +01:00
return Promise . reject ( makeRuntimeRejectMsg ( workerScript , "Unrecognized token: " + exp . type + " (line " + ( lineNum + 1 ) + "). This is currently unsupported in Netscript" ) ) ;
2017-06-05 06:48:37 +02:00
break ;
} //End switch
2017-11-01 23:56:30 +01:00
} ) . catch ( function ( e ) {
return Promise . reject ( e ) ;
2017-06-05 06:48:37 +02:00
} ) ; // End Promise
2016-11-17 23:25:40 +01:00
}
2017-06-28 11:47:42 +02:00
function evalBinary ( exp , workerScript ) {
2017-11-01 23:56:30 +01:00
return evaluate ( exp . left , workerScript ) . then ( function ( expLeft ) {
//Short circuiting
if ( expLeft == true && exp . operator === "||" ) {
return Promise . resolve ( true ) ;
}
if ( expLeft == false && exp . operator === "&&" ) {
return Promise . resolve ( false ) ;
}
return evaluate ( exp . right , workerScript ) . then ( function ( expRight ) {
switch ( exp . operator ) {
case "===" :
case "==" :
return Promise . resolve ( expLeft === expRight ) ;
break ;
case "!==" :
case "!=" :
return Promise . resolve ( expLeft !== expRight ) ;
break ;
case "<" :
return Promise . resolve ( expLeft < expRight ) ;
break ;
case "<=" :
return Promise . resolve ( expLeft <= expRight ) ;
break ;
case ">" :
return Promise . resolve ( expLeft > expRight ) ;
break ;
case ">=" :
return Promise . resolve ( expLeft >= expRight ) ;
break ;
case "+" :
return Promise . resolve ( expLeft + expRight ) ;
break ;
case "-" :
return Promise . resolve ( expLeft - expRight ) ;
break ;
case "*" :
return Promise . resolve ( expLeft * expRight ) ;
break ;
case "/" :
if ( expRight === 0 ) {
return Promise . reject ( makeRuntimeRejectMsg ( workerScript , "ERROR: Divide by zero" ) ) ;
} else {
return Promise . resolve ( expLeft / expRight ) ;
}
break ;
case "%" :
return Promise . resolve ( expLeft % expRight ) ;
break ;
case "in" :
return Promise . resolve ( expLeft in expRight ) ;
break ;
case "instanceof" :
return Promise . resolve ( expLeft instanceof expRight ) ;
break ;
case "||" :
return Promise . resolve ( expLeft || expRight ) ;
break ;
case "&&" :
return Promise . resolve ( expLeft && expRight ) ;
break ;
default :
return Promise . reject ( makeRuntimeRejectMsg ( workerScript , "Unsupported operator: " + exp . operator ) ) ;
2017-09-15 16:06:59 +02:00
}
2017-06-28 11:47:42 +02:00
} ) ;
} ) ;
}
function evalUnary ( exp , workerScript ) {
2017-07-27 04:56:14 +02:00
var env = workerScript . env ;
2017-11-01 23:56:30 +01:00
if ( env . stopFlag ) { return Promise . reject ( workerScript ) ; }
return evaluate ( exp . argument , workerScript ) . then ( function ( res ) {
if ( exp . operator == "!" ) {
return Promise . resolve ( ! res ) ;
} else if ( exp . operator == "-" ) {
if ( isNaN ( res ) ) {
return Promise . resolve ( res ) ;
2017-07-27 04:56:14 +02:00
} else {
2017-11-01 23:56:30 +01:00
return Promise . resolve ( - 1 * res ) ;
2017-06-28 11:47:42 +02:00
}
2017-11-01 23:56:30 +01:00
} else {
return Promise . reject ( makeRuntimeRejectMsg ( workerScript , "Unimplemented unary operator: " + exp . operator ) ) ;
}
2017-06-28 11:47:42 +02:00
} ) ;
}
2017-09-15 16:06:59 +02:00
//Takes in a MemberExpression that should represent a Netscript array (possible multidimensional)
//The return value is an array of the form:
// [0th index (leftmost), array name, 1st index, 2nd index, ...]
function getArrayElement ( exp , workerScript ) {
2017-11-01 23:56:30 +01:00
var indices = [ ] ;
return evaluate ( exp . property , workerScript ) . then ( function ( idx ) {
if ( isNaN ( idx ) ) {
return Promise . reject ( makeRuntimeRejectMsg ( workerScript , "Invalid access to array. Index is not a number: " + idx ) ) ;
} else {
if ( exp . object . name === undefined && exp . object . object ) {
return getArrayElement ( exp . object , workerScript ) . then ( function ( res ) {
res . push ( idx ) ;
indices = res ;
return Promise . resolve ( indices ) ;
} ) . catch ( function ( e ) {
return Promise . reject ( e ) ;
} ) ;
2017-09-15 16:06:59 +02:00
} else {
2017-11-01 23:56:30 +01:00
indices . push ( idx ) ;
indices . push ( exp . object . name ) ;
return Promise . resolve ( indices ) ;
2017-09-15 16:06:59 +02:00
}
2017-11-01 23:56:30 +01:00
}
2017-09-15 16:06:59 +02:00
} ) ;
}
2017-06-28 11:47:42 +02:00
function evalAssignment ( exp , workerScript ) {
var env = workerScript . env ;
2017-11-01 23:56:30 +01:00
if ( env . stopFlag ) { return Promise . reject ( workerScript ) ; }
2017-06-28 11:47:42 +02:00
2017-11-01 23:56:30 +01:00
if ( exp . left . type != "Identifier" && exp . left . type != "MemberExpression" ) {
return Promise . reject ( makeRuntimeRejectMsg ( workerScript , "Cannot assign to " + JSON . stringify ( exp . left ) ) ) ;
}
2017-06-28 11:47:42 +02:00
2017-11-01 23:56:30 +01:00
if ( exp . operator !== "=" && ! ( exp . left . name in env . vars ) ) {
return Promise . reject ( makeRuntimeRejectMsg ( workerScript , "variable " + exp . left . name + " not defined" ) ) ;
}
2017-07-25 16:39:56 +02:00
2017-11-01 23:56:30 +01:00
return evaluate ( exp . right , workerScript ) . then ( function ( expRight ) {
if ( exp . left . type == "MemberExpression" ) {
//Assign to array element
//Array object designed by exp.left.object.name
//Index designated by exp.left.property
return getArrayElement ( exp . left , workerScript ) . then ( function ( res ) {
if ( ! ( res instanceof Array ) || res . length < 2 ) {
return Promise . reject ( makeRuntimeRejectMsg ( workerScript , "Error evaluating array assignment. This is (probably) a bug please report to game dev" ) ) ;
}
2017-09-15 16:06:59 +02:00
2017-11-01 23:56:30 +01:00
//The array name is the second value
var arrName = res . splice ( 1 , 1 ) ;
arrName = arrName [ 0 ] ;
2017-09-15 16:06:59 +02:00
2017-11-01 23:56:30 +01:00
env . setArrayElement ( arrName , res , expRight ) ;
return Promise . resolve ( false ) ;
} ) . catch ( function ( e ) {
return Promise . reject ( e ) ;
} ) ;
} else {
//Other assignments
try {
switch ( exp . operator ) {
case "=" :
env . set ( exp . left . name , expRight ) ;
break ;
case "+=" :
env . set ( exp . left . name , env . get ( exp . left . name ) + expRight ) ;
break ;
case "-=" :
env . set ( exp . left . name , env . get ( exp . left . name ) - expRight ) ;
break ;
case "*=" :
env . set ( exp . left . name , env . get ( exp . left . name ) * expRight ) ;
break ;
case "/=" :
env . set ( exp . left . name , env . get ( exp . left . name ) / expRight ) ;
break ;
case "%=" :
env . set ( exp . left . name , env . get ( exp . left . name ) % expRight ) ;
break ;
default :
return Promise . reject ( makeRuntimeRejectMsg ( workerScript , "Bitwise assignment is not implemented" ) ) ;
2017-06-28 11:47:42 +02:00
}
2017-11-01 23:56:30 +01:00
return Promise . resolve ( false ) ;
} catch ( e ) {
return Promise . reject ( makeRuntimeRejectMsg ( workerScript , "Failed to set environment variable: " + e . toString ( ) ) ) ;
2017-06-28 11:47:42 +02:00
}
2017-11-01 23:56:30 +01:00
}
2017-06-28 11:47:42 +02:00
} ) ;
}
function evaluateIf ( exp , workerScript , i ) {
var env = workerScript . env ;
2017-11-01 23:56:30 +01:00
return evaluate ( exp . test , workerScript ) . then ( function ( condRes ) {
if ( condRes ) {
return evaluate ( exp . consequent , workerScript ) . then ( function ( res ) {
return Promise . resolve ( true ) ;
} , function ( e ) {
return Promise . reject ( e ) ;
} ) ;
} else if ( exp . alternate ) {
return evaluate ( exp . alternate , workerScript ) . then ( function ( res ) {
return Promise . resolve ( true ) ;
} , function ( e ) {
return Promise . reject ( e ) ;
} ) ;
} else {
return Promise . resolve ( "endIf" ) ;
}
2017-05-12 06:59:07 +02:00
} ) ;
}
2016-11-30 00:07:24 +01:00
//Evaluate the looping part of a for loop (Initialization block is NOT done in here)
function evaluateFor ( exp , workerScript ) {
2017-11-01 23:56:30 +01:00
var env = workerScript . env ;
if ( env . stopFlag ) { return Promise . reject ( workerScript ) ; }
2017-11-02 22:47:09 +01:00
return new Promise ( function ( resolve , reject ) {
function recurse ( ) {
//Don't return a promise so the promise chain is broken on each recursion (saving memory)
evaluate ( exp . test , workerScript ) . then ( function ( resCond ) {
if ( resCond ) {
return evaluate ( exp . body , workerScript ) . then ( function ( res ) {
return evaluate ( exp . update , workerScript ) ;
} ) . catch ( function ( e ) {
if ( e == "CONTINUESTATEMENT" ||
( e instanceof WorkerScript && e . errorMessage == "CONTINUESTATEMENT" ) ) {
//Continue statement, recurse to next iteration
return evaluate ( exp . update , workerScript ) . then ( function ( resPostloop ) {
return evaluateFor ( exp , workerScript ) ;
} ) . then ( function ( foo ) {
return Promise . resolve ( "endForLoop" ) ;
} ) . catch ( function ( e ) {
return Promise . reject ( e ) ;
} ) ;
} else {
return Promise . reject ( e ) ;
}
} ) . then ( recurse , reject ) . catch ( function ( e ) {
return Promise . reject ( e ) ;
} ) ;
} else {
resolve ( ) ;
}
} ) . catch ( function ( e ) {
reject ( e ) ;
} ) ;
}
recurse ( ) ;
} ) ;
2018-02-24 23:55:06 +01:00
}
function evaluateForeach ( arr , args , workerScript ) {
console . log ( "evaluateForeach called" ) ;
if ( ! ( arr instanceof Array ) ) {
return Promise . reject ( "Invalid array passed into forEach" ) ;
}
if ( ! ( args instanceof Array ) && args . length != 1 ) {
return Promise . reject ( "Invalid argument passed into forEach" ) ;
}
var func = args [ 0 ] ;
if ( typeof func !== "function" ) {
return Promise . reject ( "Invalid function passed into forEach" ) ;
}
console . log ( func ) ;
return new Promise ( function ( resolve , reject ) {
//Don't return a promise so the promise chain is broken on each recursion
function recurse ( i ) {
console . log ( "recurse() called with i: " + i ) ;
if ( i >= arr . length ) {
resolve ( ) ;
} else {
return Promise . delay ( Settings . CodeInstructionRunTime ) . then ( function ( ) {
console . log ( "About to apply function" ) ;
var res = func . apply ( null , [ arr [ i ] ] ) ;
console . log ( "Applied function" ) ;
++ i ;
Promise . resolve ( res ) . then ( function ( val ) {
recurse ( i ) ;
} , reject ) . catch ( function ( e ) {
return Promise . reject ( e ) ;
} ) ;
} ) ;
}
2017-11-01 23:56:30 +01:00
}
2018-02-24 23:55:06 +01:00
recurse ( 0 ) ;
} ) ;
2016-11-30 00:07:24 +01:00
}
function evaluateWhile ( exp , workerScript ) {
2017-11-01 23:56:30 +01:00
var env = workerScript . env ;
if ( env . stopFlag ) { return Promise . reject ( workerScript ) ; }
2017-11-02 22:47:09 +01:00
return new Promise ( function ( resolve , reject ) {
function recurse ( ) {
//Don't return a promise so the promise chain is broken on each recursion (saving memory)
evaluate ( exp . test , workerScript ) . then ( function ( resCond ) {
if ( resCond ) {
return evaluate ( exp . body , workerScript ) . catch ( function ( e ) {
if ( e == "CONTINUESTATEMENT" ||
( e instanceof WorkerScript && e . errorMessage == "CONTINUESTATEMENT" ) ) {
//Continue statement, recurse
return evaluateWhile ( exp , workerScript ) . then ( function ( foo ) {
return Promise . resolve ( "endWhileLoop" ) ;
} , function ( e ) {
return Promise . reject ( e ) ;
} ) ;
} else {
return Promise . reject ( e ) ;
}
} ) . then ( recurse , reject ) . catch ( function ( e ) {
2017-11-01 23:56:30 +01:00
return Promise . reject ( e ) ;
} ) ;
} else {
2017-11-02 22:47:09 +01:00
resolve ( ) ;
2017-11-01 23:56:30 +01:00
}
} ) . catch ( function ( e ) {
2017-11-02 22:47:09 +01:00
reject ( e ) ;
2017-11-01 23:56:30 +01:00
} ) ;
}
2017-11-02 22:47:09 +01:00
recurse ( ) ;
2017-11-01 23:56:30 +01:00
} ) ;
2016-11-30 00:07:24 +01:00
}
function evaluateProg ( exp , workerScript , index ) {
2017-11-01 23:56:30 +01:00
var env = workerScript . env ;
if ( env . stopFlag ) { return Promise . reject ( workerScript ) ; }
if ( index >= exp . body . length ) {
return Promise . resolve ( "progFinished" ) ;
} else {
//Evaluate this line of code in the prog
//After the code finishes evaluating, evaluate the next line recursively
return evaluate ( exp . body [ index ] , workerScript ) . then ( function ( res ) {
return evaluateProg ( exp , workerScript , index + 1 ) ;
} ) . then ( function ( res ) {
return Promise . resolve ( workerScript ) ;
} ) . catch ( function ( e ) {
return Promise . reject ( e ) ;
} ) ;
}
}
function killNetscriptDelay ( workerScript ) {
2017-11-02 22:47:09 +01:00
/ *
2017-11-01 23:56:30 +01:00
if ( workerScript instanceof WorkerScript ) {
if ( workerScript . delay ) {
workerScript . delay . cancel ( ) ;
}
}
2017-11-02 22:47:09 +01:00
* /
if ( workerScript instanceof WorkerScript ) {
if ( workerScript . delay ) {
clearTimeout ( workerScript . delay ) ;
workerScript . delayResolve ( ) ;
}
}
2016-11-30 00:07:24 +01:00
}
2017-09-15 16:06:59 +02:00
function netscriptDelay ( time , workerScript ) {
2017-11-02 22:47:09 +01:00
/ *
workerScript . delay = new Promise ( function ( resolve , reject , onCancel ) {
Promise . delay ( time ) . then ( function ( ) {
resolve ( ) ;
workerScript . delay = null ;
} ) ;
onCancel ( function ( ) {
console . log ( "Cancelling and rejecting this Promise" ) ;
reject ( workerScript ) ;
} )
} ) ;
return workerScript . delay ;
* /
return new Promise ( function ( resolve , reject ) {
workerScript . delay = setTimeout ( ( ) => {
workerScript . delay = null ;
resolve ( ) ;
} , time ) ;
workerScript . delayResolve = resolve ;
} ) ;
2017-06-05 06:48:37 +02:00
}
2017-05-25 14:18:34 +02:00
function makeRuntimeRejectMsg ( workerScript , msg ) {
return "|" + workerScript . serverIp + "|" + workerScript . name + "|" + msg ;
}
2017-09-15 16:06:59 +02:00
/ *
2016-11-17 23:25:40 +01:00
function apply _op ( op , a , b ) {
function num ( x ) {
if ( typeof x != "number" )
throw new Error ( "Expected number but got " + x ) ;
return x ;
}
function div ( x ) {
if ( num ( x ) == 0 )
throw new Error ( "Divide by zero" ) ;
return x ;
}
switch ( op ) {
2017-05-23 20:17:37 +02:00
case "+" : return a + b ;
2016-11-17 23:25:40 +01:00
case "-" : return num ( a ) - num ( b ) ;
case "*" : return num ( a ) * num ( b ) ;
case "/" : return num ( a ) / div ( b ) ;
case "%" : return num ( a ) % div ( b ) ;
case "&&" : return a !== false && b ;
case "||" : return a !== false ? a : b ;
case "<" : return num ( a ) < num ( b ) ;
case ">" : return num ( a ) > num ( b ) ;
case "<=" : return num ( a ) <= num ( b ) ;
case ">=" : return num ( a ) >= num ( b ) ;
case "==" : return a === b ;
case "!=" : return a !== b ;
}
throw new Error ( "Can't apply operator " + op ) ;
2017-05-15 08:35:09 +02:00
}
2017-09-15 16:06:59 +02:00
* /
2017-05-15 08:35:09 +02:00
//Run a script from inside a script using run() command
2017-06-17 04:53:57 +02:00
function runScriptFromScript ( server , scriptname , args , workerScript , threads = 1 ) {
2017-07-13 18:54:29 +02:00
//Check if the script is already running
var runningScriptObj = findRunningScript ( scriptname , args , server ) ;
if ( runningScriptObj != null ) {
workerScript . scriptRef . log ( scriptname + " is already running on " + server . hostname ) ;
return Promise . resolve ( false ) ;
}
2017-06-28 11:47:42 +02:00
2018-02-24 23:55:06 +01:00
//'null/undefined' arguments are not allowed
for ( var i = 0 ; i < args . length ; ++ i ) {
if ( args [ i ] == null ) {
workerScript . scriptRef . log ( "ERROR: Cannot execute a script with null/undefined as an argument" ) ;
return Promise . resolve ( false ) ;
}
}
2017-07-13 18:54:29 +02:00
//Check if the script exists and if it does run it
for ( var i = 0 ; i < server . scripts . length ; ++ i ) {
if ( server . scripts [ i ] . filename == scriptname ) {
//Check for admin rights and that there is enough RAM availble to run
var script = server . scripts [ i ] ;
var ramUsage = script . ramUsage ;
2017-09-13 16:22:22 +02:00
threads = Math . round ( Number ( threads ) ) ; //Convert to number and round
2017-07-13 18:54:29 +02:00
ramUsage = ramUsage * threads * Math . pow ( CONSTANTS . MultithreadingRAMCost , threads - 1 ) ;
var ramAvailable = server . maxRam - server . ramUsed ;
2017-06-28 11:47:42 +02:00
2017-07-13 18:54:29 +02:00
if ( server . hasAdminRights == false ) {
workerScript . scriptRef . log ( "Cannot run script " + scriptname + " on " + server . hostname + " because you do not have root access!" ) ;
return Promise . resolve ( false ) ;
} else if ( ramUsage > ramAvailable ) {
workerScript . scriptRef . log ( "Cannot run script " + scriptname + "(t=" + threads + ") on " + server . hostname + " because there is not enough available RAM!" ) ;
return Promise . resolve ( false ) ;
} else {
//Able to run script
workerScript . scriptRef . log ( "Running script: " + scriptname + " on " + server . hostname + " with " + threads + " threads and args: " + printArray ( args ) + ". May take a few seconds to start up..." ) ;
var runningScriptObj = new RunningScript ( script , args ) ;
runningScriptObj . threads = threads ;
2017-11-01 23:56:30 +01:00
server . runningScripts . push ( runningScriptObj ) ; //Push onto runningScripts
2017-07-13 18:54:29 +02:00
addWorkerScript ( runningScriptObj , server ) ;
return Promise . resolve ( true ) ;
2017-05-15 08:35:09 +02:00
}
2017-06-28 11:47:42 +02:00
}
2017-07-13 18:54:29 +02:00
}
workerScript . scriptRef . log ( "Could not find script " + scriptname + " on " + server . hostname ) ;
return Promise . resolve ( false ) ;
2017-05-15 08:35:09 +02:00
}
2016-12-14 00:52:32 +01:00
2017-09-05 03:03:29 +02:00
//Takes in a
function getErrorLineNumber ( exp , workerScript ) {
var code = workerScript . scriptRef . scriptRef . code ;
//Split code up to the start of the node
code = code . substring ( 0 , exp . start ) ;
return ( code . match ( /\n/g ) || [ ] ) . length ;
}
2017-05-01 07:39:48 +02:00
function isScriptErrorMessage ( msg ) {
2017-08-30 19:44:29 +02:00
if ( ! isString ( msg ) ) { return false ; }
let splitMsg = msg . split ( "|" ) ;
2017-05-01 07:39:48 +02:00
if ( splitMsg . length != 4 ) {
return false ;
}
var ip = splitMsg [ 1 ] ;
if ( ! isValidIPAddress ( ip ) ) {
return false ;
}
return true ;
}
2016-12-14 00:52:32 +01:00
//The same as Player's calculateHackingChance() function but takes in the server as an argument
function scriptCalculateHackingChance ( server ) {
2017-11-01 23:56:30 +01:00
var difficultyMult = ( 100 - server . hackDifficulty ) / 100 ;
2017-09-15 16:06:59 +02:00
var skillMult = ( 1.75 * Player . hacking _skill ) + ( 0.2 * Player . intelligence ) ;
2016-12-14 00:52:32 +01:00
var skillChance = ( skillMult - server . requiredHackingSkill ) / skillMult ;
2017-05-24 15:49:52 +02:00
var chance = skillChance * difficultyMult * Player . hacking _chance _mult ;
2017-07-22 00:54:55 +02:00
if ( chance > 1 ) { return 1 ; }
2017-04-17 14:26:54 +02:00
if ( chance < 0 ) { return 0 ; }
else { return chance ; }
2016-12-14 00:52:32 +01:00
}
//The same as Player's calculateHackingTime() function but takes in the server as an argument
function scriptCalculateHackingTime ( server ) {
2017-11-01 23:56:30 +01:00
var difficultyMult = server . requiredHackingSkill * server . hackDifficulty ;
var skillFactor = ( 2.5 * difficultyMult + 500 ) / ( Player . hacking _skill + 50 + ( 0.1 * Player . intelligence ) ) ;
var hackingTime = 5 * skillFactor / Player . hacking _speed _mult ; //This is in seconds
return hackingTime ;
2016-12-14 00:52:32 +01:00
}
2017-07-25 16:39:56 +02:00
//The same as Player's calculateExpGain() function but takes in the server as an argument
2016-12-14 00:52:32 +01:00
function scriptCalculateExpGain ( server ) {
2017-06-15 03:19:52 +02:00
if ( server . baseDifficulty == null ) {
server . baseDifficulty = server . hackDifficulty ;
}
2017-08-30 19:44:29 +02:00
return ( server . baseDifficulty * Player . hacking _exp _mult * 0.3 + 3 ) * BitNodeMultipliers . HackExpGain ;
2016-12-14 00:52:32 +01:00
}
//The same as Player's calculatePercentMoneyHacked() function but takes in the server as an argument
function scriptCalculatePercentMoneyHacked ( server ) {
2017-11-01 23:56:30 +01:00
var difficultyMult = ( 100 - server . hackDifficulty ) / 100 ;
2016-12-14 00:52:32 +01:00
var skillMult = ( Player . hacking _skill - ( server . requiredHackingSkill - 1 ) ) / Player . hacking _skill ;
2017-07-22 00:54:55 +02:00
var percentMoneyHacked = difficultyMult * skillMult * Player . hacking _money _mult / 240 ;
2017-04-18 06:32:17 +02:00
if ( percentMoneyHacked < 0 ) { return 0 ; }
if ( percentMoneyHacked > 1 ) { return 1 ; }
2017-08-30 19:44:29 +02:00
return percentMoneyHacked * BitNodeMultipliers . ScriptHackMoney ;
2017-05-24 15:49:52 +02:00
}
2017-05-31 19:37:32 +02:00
//Amount of time to execute grow() in milliseconds
2017-05-24 15:49:52 +02:00
function scriptCalculateGrowTime ( server ) {
var difficultyMult = server . requiredHackingSkill * server . hackDifficulty ;
2017-11-01 23:56:30 +01:00
var skillFactor = ( 2.5 * difficultyMult + 500 ) / ( Player . hacking _skill + 50 + ( 0.1 * Player . intelligence ) ) ;
var growTime = 16 * skillFactor / Player . hacking _speed _mult ; //This is in seconds
return growTime * 1000 ;
2017-05-31 19:37:32 +02:00
}
//Amount of time to execute weaken() in milliseconds
function scriptCalculateWeakenTime ( server ) {
var difficultyMult = server . requiredHackingSkill * server . hackDifficulty ;
2017-11-01 23:56:30 +01:00
var skillFactor = ( 2.5 * difficultyMult + 500 ) / ( Player . hacking _skill + 50 + ( 0.1 * Player . intelligence ) ) ;
var weakenTime = 20 * skillFactor / Player . hacking _speed _mult ; //This is in seconds
return weakenTime * 1000 ;
2017-07-25 16:39:56 +02:00
}
2017-08-30 19:44:29 +02:00
export { makeRuntimeRejectMsg , netscriptDelay , runScriptFromScript ,
scriptCalculateHackingChance , scriptCalculateHackingTime ,
scriptCalculateExpGain , scriptCalculatePercentMoneyHacked ,
scriptCalculateGrowTime , scriptCalculateWeakenTime , evaluate ,
2017-11-01 23:56:30 +01:00
isScriptErrorMessage , killNetscriptDelay } ;