UNPKG

eval5

Version:

A JavaScript interpreter written in JavaScript

1,272 lines (1,271 loc) 66.5 kB
"use strict"; var __assign = (this && this.__assign) || function () { __assign = Object.assign || function(t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; return __assign.apply(this, arguments); }; var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) { if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { if (ar || !(i in from)) { if (!ar) ar = Array.prototype.slice.call(from, 0, i); ar[i] = from[i]; } } return to.concat(ar || Array.prototype.slice.call(from)); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.Interpreter = void 0; var acorn_1 = require("acorn"); var messages_1 = require("./messages"); var version = "1.4.8"; function defineFunctionName(func, name) { Object.defineProperty(func, "name", { value: name, writable: false, enumerable: false, configurable: true, }); } var hasOwnProperty = Object.prototype.hasOwnProperty; var Break = Symbol("Break"); var Continue = Symbol("Continue"); var DefaultCase = Symbol("DefaultCase"); var EmptyStatementReturn = Symbol("EmptyStatementReturn"); var WithScopeName = Symbol("WithScopeName"); var SuperScopeName = Symbol("SuperScopeName"); var RootScopeName = Symbol("RootScopeName"); var GlobalScopeName = Symbol("GlobalScopeName"); function isFunction(func) { return typeof func === "function"; } var InternalInterpreterReflection = /** @class */ (function () { function InternalInterpreterReflection(interpreter) { this.interpreter = interpreter; } InternalInterpreterReflection.prototype.generator = function () { var interpreter = this.interpreter; function getCurrentScope() { return this.getCurrentScope(); } function getGlobalScope() { return this.getGlobalScope(); } function getCurrentContext() { return this.getCurrentContext(); } return { getOptions: interpreter.getOptions.bind(interpreter), getCurrentScope: getCurrentScope.bind(interpreter), getGlobalScope: getGlobalScope.bind(interpreter), getCurrentContext: getCurrentContext.bind(interpreter), getExecStartTime: interpreter.getExecStartTime.bind(interpreter), }; }; return InternalInterpreterReflection; }()); function internalEval(reflection, code, useGlobalScope) { if (useGlobalScope === void 0) { useGlobalScope = true; } if (!(reflection instanceof InternalInterpreterReflection)) { throw new Error("Illegal call"); } if (typeof code !== "string") return code; if (!code) return void 0; var instance = reflection.generator(); var opts = instance.getOptions(); var options = { timeout: opts.timeout, _initEnv: function () { // set caller context if (!useGlobalScope) { this.setCurrentContext(instance.getCurrentContext()); } // share timeout this.execStartTime = instance.getExecStartTime(); this.execEndTime = this.execStartTime; }, }; var currentScope = useGlobalScope ? instance.getGlobalScope() : instance.getCurrentScope(); var interpreter = new Interpreter(currentScope, options); return interpreter.evaluate(code); } Object.defineProperty(internalEval, "__IS_EVAL_FUNC", { value: true, writable: false, enumerable: false, configurable: false, }); function internalFunction(reflection) { var params = []; for (var _i = 1; _i < arguments.length; _i++) { params[_i - 1] = arguments[_i]; } if (!(reflection instanceof InternalInterpreterReflection)) { throw new Error("Illegal call"); } var instance = reflection.generator(); var code = params.pop(); var interpreter = new Interpreter(instance.getGlobalScope(), instance.getOptions()); var wrapCode = "\n\t\t (function anonymous(".concat(params.join(","), "){\n\t\t ").concat(code, "\n\t\t });\n\t\t "); return interpreter.evaluate(wrapCode); } Object.defineProperty(internalFunction, "__IS_FUNCTION_FUNC", { value: true, writable: false, enumerable: false, configurable: false, }); var Return = /** @class */ (function () { function Return(value) { this.value = value; } return Return; }()); var BreakLabel = /** @class */ (function () { function BreakLabel(value) { this.value = value; } return BreakLabel; }()); var ContinueLabel = /** @class */ (function () { function ContinueLabel(value) { this.value = value; } return ContinueLabel; }()); /** * scope chain * * superScope * ↓ * rootScope * ↓ * globalScope * ↓ * functionScope * */ var Scope = /** @class */ (function () { function Scope(data, parent, name) { if (parent === void 0) { parent = null; } this.name = name; this.parent = parent; this.data = data; this.labelStack = []; } return Scope; }()); function noop() { } function createScope(parent, name) { if (parent === void 0) { parent = null; } return new Scope(Object.create(null), parent, name); } function createRootContext(data) { return Object.create(data); } var BuildInObjects = { NaN: NaN, Infinity: Infinity, undefined: undefined, // null, Object: Object, Array: Array, String: String, Boolean: Boolean, Number: Number, Date: Date, RegExp: RegExp, Error: Error, URIError: URIError, TypeError: TypeError, RangeError: RangeError, SyntaxError: SyntaxError, ReferenceError: ReferenceError, Math: Math, parseInt: parseInt, parseFloat: parseFloat, isNaN: isNaN, isFinite: isFinite, decodeURI: decodeURI, decodeURIComponent: decodeURIComponent, encodeURI: encodeURI, encodeURIComponent: encodeURIComponent, escape: escape, unescape: unescape, eval: internalEval, Function: internalFunction, }; // ES5 Object if (typeof JSON !== "undefined") { BuildInObjects.JSON = JSON; } //ES6 Object if (typeof Promise !== "undefined") { BuildInObjects.Promise = Promise; } if (typeof Set !== "undefined") { BuildInObjects.Set = Set; } if (typeof Map !== "undefined") { BuildInObjects.Map = Map; } if (typeof Symbol !== "undefined") { BuildInObjects.Symbol = Symbol; } if (typeof Proxy !== "undefined") { BuildInObjects.Proxy = Proxy; } if (typeof WeakMap !== "undefined") { BuildInObjects.WeakMap = WeakMap; } if (typeof WeakSet !== "undefined") { BuildInObjects.WeakSet = WeakSet; } if (typeof Reflect !== "undefined") { BuildInObjects.Reflect = Reflect; } var Interpreter = exports.Interpreter = /** @class */ (function () { function Interpreter(context, options) { if (context === void 0) { context = Interpreter.global; } if (options === void 0) { options = {}; } this.sourceList = []; this.collectDeclVars = Object.create(null); this.collectDeclFuncs = Object.create(null); this.isVarDeclMode = false; this.lastExecNode = null; this.isRunning = false; this.options = { ecmaVersion: options.ecmaVersion || Interpreter.ecmaVersion, timeout: options.timeout || 0, rootContext: options.rootContext, globalContextInFunction: options.globalContextInFunction === undefined ? Interpreter.globalContextInFunction : options.globalContextInFunction, _initEnv: options._initEnv, }; this.context = context || Object.create(null); this.callStack = []; this.initEnvironment(this.context); } Interpreter.prototype.initEnvironment = function (ctx) { var scope; //init global scope if (ctx instanceof Scope) { scope = ctx; } else { var rootScope = null; var superScope = this.createSuperScope(ctx); if (this.options.rootContext) { rootScope = new Scope(createRootContext(this.options.rootContext), superScope, RootScopeName); } scope = new Scope(ctx, rootScope || superScope, GlobalScopeName); } this.globalScope = scope; this.currentScope = this.globalScope; //init global context to this this.globalContext = scope.data; this.currentContext = scope.data; // collect var/function declare this.collectDeclVars = Object.create(null); this.collectDeclFuncs = Object.create(null); this.execStartTime = Date.now(); this.execEndTime = this.execStartTime; var _initEnv = this.options._initEnv; if (_initEnv) { _initEnv.call(this); } }; Interpreter.prototype.getExecStartTime = function () { return this.execStartTime; }; Interpreter.prototype.getExecutionTime = function () { return this.execEndTime - this.execStartTime; }; Interpreter.prototype.setExecTimeout = function (timeout) { if (timeout === void 0) { timeout = 0; } this.options.timeout = timeout; }; Interpreter.prototype.getOptions = function () { return this.options; }; Interpreter.prototype.getGlobalScope = function () { return this.globalScope; }; Interpreter.prototype.getCurrentScope = function () { return this.currentScope; }; Interpreter.prototype.getCurrentContext = function () { return this.currentContext; }; Interpreter.prototype.isInterruptThrow = function (err) { return (err instanceof messages_1.InterruptThrowError || err instanceof messages_1.InterruptThrowReferenceError || err instanceof messages_1.InterruptThrowSyntaxError); }; Interpreter.prototype.createSuperScope = function (ctx) { var data = __assign({}, BuildInObjects); var buildInObjectKeys = Object.keys(data); buildInObjectKeys.forEach(function (key) { if (key in ctx) { delete data[key]; } }); return new Scope(data, null, SuperScopeName); }; Interpreter.prototype.setCurrentContext = function (ctx) { this.currentContext = ctx; }; Interpreter.prototype.setCurrentScope = function (scope) { this.currentScope = scope; }; Interpreter.prototype.evaluate = function (code) { if (code === void 0) { code = ""; } var node; if (!code) return; node = (0, acorn_1.parse)(code, { ranges: true, locations: true, ecmaVersion: this.options.ecmaVersion || Interpreter.ecmaVersion, }); return this.evaluateNode(node, code); }; Interpreter.prototype.appendCode = function (code) { return this.evaluate(code); }; Interpreter.prototype.evaluateNode = function (node, source) { var _this = this; if (source === void 0) { source = ""; } this.value = undefined; this.source = source; this.sourceList.push(source); this.isRunning = true; //reset timeout this.execStartTime = Date.now(); this.execEndTime = this.execStartTime; // reset this.collectDeclVars = Object.create(null); this.collectDeclFuncs = Object.create(null); var currentScope = this.getCurrentScope(); var currentContext = this.getCurrentContext(); var labelStack = currentScope.labelStack.concat([]); var callStack = this.callStack.concat([]); var reset = function () { _this.setCurrentScope(currentScope); //reset scope _this.setCurrentContext(currentContext); //reset context currentScope.labelStack = labelStack; //reset label stack _this.callStack = callStack; //reset call stack }; // start run try { var bodyClosure = this.createClosure(node); // add declares to data this.addDeclarationsToScope(this.collectDeclVars, this.collectDeclFuncs, this.getCurrentScope()); bodyClosure(); } catch (e) { throw e; } finally { reset(); this.execEndTime = Date.now(); } this.isRunning = false; return this.getValue(); }; Interpreter.prototype.createErrorMessage = function (msg, value, node) { var message = msg[1].replace("%0", String(value)); if (node !== null) { message += this.getNodePosition(node || this.lastExecNode); } return message; }; Interpreter.prototype.createError = function (message, error) { return new error(message); }; Interpreter.prototype.createThrowError = function (message, error) { return this.createError(message, error); }; Interpreter.prototype.createInternalThrowError = function (msg, value, node) { return this.createError(this.createErrorMessage(msg, value, node), msg[2]); }; Interpreter.prototype.checkTimeout = function () { if (!this.isRunning) return false; var timeout = this.options.timeout || 0; var now = Date.now(); if (now - this.execStartTime > timeout) { return true; } return false; }; Interpreter.prototype.getNodePosition = function (node) { if (node) { var errorCode = ""; //this.source.slice(node.start, node.end); return node.loc ? " [".concat(node.loc.start.line, ":").concat(node.loc.start.column, "]").concat(errorCode) : ""; } return ""; }; Interpreter.prototype.createClosure = function (node) { var _this = this; var closure; switch (node.type) { case "BinaryExpression": closure = this.binaryExpressionHandler(node); break; case "LogicalExpression": closure = this.logicalExpressionHandler(node); break; case "UnaryExpression": closure = this.unaryExpressionHandler(node); break; case "UpdateExpression": closure = this.updateExpressionHandler(node); break; case "ObjectExpression": closure = this.objectExpressionHandler(node); break; case "ArrayExpression": closure = this.arrayExpressionHandler(node); break; case "CallExpression": closure = this.callExpressionHandler(node); break; case "NewExpression": closure = this.newExpressionHandler(node); break; case "MemberExpression": closure = this.memberExpressionHandler(node); break; case "ThisExpression": closure = this.thisExpressionHandler(node); break; case "SequenceExpression": closure = this.sequenceExpressionHandler(node); break; case "Literal": closure = this.literalHandler(node); break; case "Identifier": closure = this.identifierHandler(node); break; case "AssignmentExpression": closure = this.assignmentExpressionHandler(node); break; case "FunctionDeclaration": closure = this.functionDeclarationHandler(node); break; case "VariableDeclaration": closure = this.variableDeclarationHandler(node); break; case "BlockStatement": case "Program": closure = this.programHandler(node); break; case "ExpressionStatement": closure = this.expressionStatementHandler(node); break; case "EmptyStatement": closure = this.emptyStatementHandler(node); break; case "ReturnStatement": closure = this.returnStatementHandler(node); break; case "FunctionExpression": closure = this.functionExpressionHandler(node); break; case "IfStatement": closure = this.ifStatementHandler(node); break; case "ConditionalExpression": closure = this.conditionalExpressionHandler(node); break; case "ForStatement": closure = this.forStatementHandler(node); break; case "WhileStatement": closure = this.whileStatementHandler(node); break; case "DoWhileStatement": closure = this.doWhileStatementHandler(node); break; case "ForInStatement": closure = this.forInStatementHandler(node); break; case "WithStatement": closure = this.withStatementHandler(node); break; case "ThrowStatement": closure = this.throwStatementHandler(node); break; case "TryStatement": closure = this.tryStatementHandler(node); break; case "ContinueStatement": closure = this.continueStatementHandler(node); break; case "BreakStatement": closure = this.breakStatementHandler(node); break; case "SwitchStatement": closure = this.switchStatementHandler(node); break; case "LabeledStatement": closure = this.labeledStatementHandler(node); break; case "DebuggerStatement": closure = this.debuggerStatementHandler(node); break; default: throw this.createInternalThrowError(messages_1.Messages.NodeTypeSyntaxError, node.type, node); } return function () { var args = []; for (var _i = 0; _i < arguments.length; _i++) { args[_i] = arguments[_i]; } var timeout = _this.options.timeout; if (timeout && timeout > 0 && _this.checkTimeout()) { throw _this.createInternalThrowError(messages_1.Messages.ExecutionTimeOutError, timeout, null); } _this.lastExecNode = node; return closure.apply(void 0, args); }; }; // a==b a/b Interpreter.prototype.binaryExpressionHandler = function (node) { var _this = this; var leftExpression = this.createClosure(node.left); var rightExpression = this.createClosure(node.right); return function () { var leftValue = leftExpression(); var rightValue = rightExpression(); switch (node.operator) { case "==": return leftValue == rightValue; case "!=": return leftValue != rightValue; case "===": return leftValue === rightValue; case "!==": return leftValue !== rightValue; case "<": return leftValue < rightValue; case "<=": return leftValue <= rightValue; case ">": return leftValue > rightValue; case ">=": return leftValue >= rightValue; case "<<": return leftValue << rightValue; case ">>": return leftValue >> rightValue; case ">>>": return leftValue >>> rightValue; case "+": return leftValue + rightValue; case "-": return leftValue - rightValue; case "*": return leftValue * rightValue; case "**": return Math.pow(leftValue, rightValue); case "/": return leftValue / rightValue; case "%": return leftValue % rightValue; case "|": return leftValue | rightValue; case "^": return leftValue ^ rightValue; case "&": return leftValue & rightValue; case "in": return leftValue in rightValue; case "instanceof": return leftValue instanceof rightValue; default: throw _this.createInternalThrowError(messages_1.Messages.BinaryOperatorSyntaxError, node.operator, node); } }; }; // a && b Interpreter.prototype.logicalExpressionHandler = function (node) { var _this = this; var leftExpression = this.createClosure(node.left); var rightExpression = this.createClosure(node.right); return function () { switch (node.operator) { case "||": return leftExpression() || rightExpression(); case "&&": return leftExpression() && rightExpression(); default: throw _this.createInternalThrowError(messages_1.Messages.LogicalOperatorSyntaxError, node.operator, node); } }; }; // protected isRootScope(node: ESTree.Expression | ESTree.Pattern): boolean { // if (node.type === "Identifier") { // const scope = this.getScopeFromName(node.name, this.getCurrentScope()); // return scope.name === "rootScope"; // } // return false; // } // typeof a !a() Interpreter.prototype.unaryExpressionHandler = function (node) { var _this = this; switch (node.operator) { case "delete": var objectGetter_1 = this.createObjectGetter(node.argument); var nameGetter_1 = this.createNameGetter(node.argument); return function () { // not allowed to delete root scope property // rootContext has move to prototype chai, so no judgment required // if (this.isRootScope(node.argument)) { // return false; // } var obj = objectGetter_1(); var name = nameGetter_1(); return delete obj[name]; }; default: var expression_1; // for typeof undefined var // typeof adf9ad if (node.operator === "typeof" && node.argument.type === "Identifier") { var objectGetter_2 = this.createObjectGetter(node.argument); var nameGetter_2 = this.createNameGetter(node.argument); expression_1 = function () { return objectGetter_2()[nameGetter_2()]; }; } else { expression_1 = this.createClosure(node.argument); } return function () { var value = expression_1(); switch (node.operator) { case "-": return -value; case "+": return +value; case "!": return !value; case "~": return ~value; case "void": return void value; case "typeof": return typeof value; default: throw _this.createInternalThrowError(messages_1.Messages.UnaryOperatorSyntaxError, node.operator, node); } }; } }; // ++a --a Interpreter.prototype.updateExpressionHandler = function (node) { var _this = this; var objectGetter = this.createObjectGetter(node.argument); var nameGetter = this.createNameGetter(node.argument); return function () { var obj = objectGetter(); var name = nameGetter(); _this.assertVariable(obj, name, node); switch (node.operator) { case "++": return node.prefix ? ++obj[name] : obj[name]++; case "--": return node.prefix ? --obj[name] : obj[name]--; default: throw _this.createInternalThrowError(messages_1.Messages.UpdateOperatorSyntaxError, node.operator, node); } }; }; // var o = {a: 1, b: 's', get name(){}, set name(){} ...} Interpreter.prototype.objectExpressionHandler = function (node) { var _this = this; var items = []; function getKey(keyNode) { if (keyNode.type === "Identifier") { // var o = {a:1} return keyNode.name; } else if (keyNode.type === "Literal") { // var o = {"a":1} return keyNode.value; } else { return this.throwError(messages_1.Messages.ObjectStructureSyntaxError, keyNode.type, keyNode); } } // collect value, getter, and/or setter. var properties = Object.create(null); node.properties.forEach(function (property) { var kind = property.kind; var key = getKey(property.key); if (!properties[key] || kind === "init") { properties[key] = {}; } properties[key][kind] = _this.createClosure(property.value); items.push({ key: key, property: property, }); }); return function () { var result = {}; var len = items.length; for (var i = 0; i < len; i++) { var item = items[i]; var key = item.key; var kinds = properties[key]; var value = kinds.init ? kinds.init() : undefined; var getter = kinds.get ? kinds.get() : function () { }; var setter = kinds.set ? kinds.set() : function (a) { }; if ("set" in kinds || "get" in kinds) { var descriptor = { configurable: true, enumerable: true, get: getter, set: setter, }; Object.defineProperty(result, key, descriptor); } else { var property = item.property; var kind = property.kind; // set function.name // var d = { test(){} } // var d = { test: function(){} } if (property.key.type === "Identifier" && property.value.type === "FunctionExpression" && kind === "init" && !property.value.id) { defineFunctionName(value, property.key.name); } result[key] = value; } } return result; }; }; // [1,2,3] Interpreter.prototype.arrayExpressionHandler = function (node) { var _this = this; //fix: [,,,1,2] var items = node.elements.map(function (element) { return element ? _this.createClosure(element) : element; }); return function () { var len = items.length; var result = Array(len); for (var i = 0; i < len; i++) { var item = items[i]; if (item) { result[i] = item(); } } return result; }; }; Interpreter.prototype.safeObjectGet = function (obj, key, node) { return obj[key]; }; Interpreter.prototype.createCallFunctionGetter = function (node) { var _this = this; switch (node.type) { case "MemberExpression": var objectGetter_3 = this.createClosure(node.object); var keyGetter_1 = this.createMemberKeyGetter(node); var source_1 = this.source; return function () { var obj = objectGetter_3(); var key = keyGetter_1(); var func = _this.safeObjectGet(obj, key, node); if (!func || !isFunction(func)) { var name_1 = source_1.slice(node.start, node.end); throw _this.createInternalThrowError(messages_1.Messages.FunctionUndefinedReferenceError, name_1, node); } // obj.eval = eval // obj.eval(...) if (func.__IS_EVAL_FUNC) { return function (code) { return func(new InternalInterpreterReflection(_this), code, true); }; } // obj.func = Function // obj.func(...) if (func.__IS_FUNCTION_FUNC) { return function () { var args = []; for (var _i = 0; _i < arguments.length; _i++) { args[_i] = arguments[_i]; } return func.apply(void 0, __spreadArray([new InternalInterpreterReflection(_this)], args, false)); }; } // method call // eg:obj.say(...) // eg: obj.say.call(...) // eg: obj.say.apply(...) // ====================== // obj.func(...) // func = func.bind(obj) // tips: // func(...) -> func.bind(obj)(...) // func.call(...) -> obj.func.call.bind(obj.func)(...) // func.apply(...) -> obj.func.apply.bind(obj.func)(...) // ...others return func.bind(obj); }; default: // test() or (0,test)() or a[1]() ... var closure_1 = this.createClosure(node); return function () { var name = ""; if (node.type === "Identifier") { name = node.name; } // const name: string = (<ESTree.Identifier>node).name; var func = closure_1(); if (!func || !isFunction(func)) { throw _this.createInternalThrowError(messages_1.Messages.FunctionUndefinedReferenceError, name, node); } // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval // var eval = eval; // function test(){ // eval(...); //note: use local scope in eval5,but in Browser is use global scope // } if (node.type === "Identifier" && func.__IS_EVAL_FUNC && name === "eval") { return function (code) { var scope = _this.getScopeFromName(name, _this.getCurrentScope()); var useGlobalScope = scope.name === SuperScopeName || // !scope.parent || // super scope scope.name === GlobalScopeName || // this.globalScope === scope || scope.name === RootScopeName; // use local scope if calling eval in super scope return func(new InternalInterpreterReflection(_this), code, !useGlobalScope); }; } // use global scope // var g_eval = eval; // g_eval("a+1"); //(0,eval)(...) ...eval alias if (func.__IS_EVAL_FUNC) { return function (code) { return func(new InternalInterpreterReflection(_this), code, true); }; } // Function('a', 'b', 'return a+b') if (func.__IS_FUNCTION_FUNC) { return function () { var args = []; for (var _i = 0; _i < arguments.length; _i++) { args[_i] = arguments[_i]; } return func.apply(void 0, __spreadArray([new InternalInterpreterReflection(_this)], args, false)); }; } var ctx = _this.options.globalContextInFunction; // with(obj) { // test() // test.call(obj, ...) // } if (node.type === "Identifier") { var scope = _this.getIdentifierScope(node); if (scope.name === WithScopeName) { ctx = scope.data; } } // function call // this = undefined // tips: // test(...) === test.call(undefined, ...) // fix: alert.call({}, ...) Illegal invocation return func.bind(ctx); }; } }; // func() Interpreter.prototype.callExpressionHandler = function (node) { var _this = this; var funcGetter = this.createCallFunctionGetter(node.callee); var argsGetter = node.arguments.map(function (arg) { return _this.createClosure(arg); }); return function () { return funcGetter().apply(void 0, argsGetter.map(function (arg) { return arg(); })); }; }; // var f = function() {...} Interpreter.prototype.functionExpressionHandler = function (node) { var _this = this; var self = this; var source = this.source; var oldDeclVars = this.collectDeclVars; var oldDeclFuncs = this.collectDeclFuncs; this.collectDeclVars = Object.create(null); this.collectDeclFuncs = Object.create(null); var name = node.id ? node.id.name : ""; /**anonymous*/ var paramLength = node.params.length; var paramsGetter = node.params.map(function (param) { return _this.createParamNameGetter(param); }); // set scope var bodyClosure = this.createClosure(node.body); var declVars = this.collectDeclVars; var declFuncs = this.collectDeclFuncs; this.collectDeclVars = oldDeclVars; this.collectDeclFuncs = oldDeclFuncs; return function () { // bind current scope var runtimeScope = self.getCurrentScope(); var func = function () { var args = []; for (var _i = 0; _i < arguments.length; _i++) { args[_i] = arguments[_i]; } self.callStack.push("".concat(name)); var prevScope = self.getCurrentScope(); var currentScope = createScope(runtimeScope, "FunctionScope(".concat(name, ")")); self.setCurrentScope(currentScope); self.addDeclarationsToScope(declVars, declFuncs, currentScope); // var t = function(){ typeof t } // function // t = function(){ typeof t } // function // z = function tx(){ typeof tx } // function // but // d = { say: function(){ typeof say } } // undefined if (name) { currentScope.data[name] = func; } // init arguments var currentScope.data["arguments"] = arguments; paramsGetter.forEach(function (getter, i) { currentScope.data[getter()] = args[i]; }); // init this var prevContext = self.getCurrentContext(); //for ThisExpression self.setCurrentContext(this); var result = bodyClosure(); //reset self.setCurrentContext(prevContext); self.setCurrentScope(prevScope); self.callStack.pop(); if (result instanceof Return) { return result.value; } }; defineFunctionName(func, name); Object.defineProperty(func, "length", { value: paramLength, writable: false, enumerable: false, configurable: true, }); Object.defineProperty(func, "toString", { value: function () { return source.slice(node.start, node.end); }, writable: true, configurable: true, enumerable: false, }); Object.defineProperty(func, "valueOf", { value: function () { return source.slice(node.start, node.end); }, writable: true, configurable: true, enumerable: false, }); return func; }; }; // new Ctrl() Interpreter.prototype.newExpressionHandler = function (node) { var _this = this; var source = this.source; var expression = this.createClosure(node.callee); var args = node.arguments.map(function (arg) { return _this.createClosure(arg); }); return function () { var construct = expression(); if (!isFunction(construct) || construct.__IS_EVAL_FUNC) { var callee = node.callee; var name_2 = source.slice(callee.start, callee.end); throw _this.createInternalThrowError(messages_1.Messages.IsNotConstructor, name_2, node); } // new Function(...) if (construct.__IS_FUNCTION_FUNC) { return construct.apply(void 0, __spreadArray([new InternalInterpreterReflection(_this)], args.map(function (arg) { return arg(); }), false)); } return new (construct.bind.apply(construct, __spreadArray([void 0], args.map(function (arg) { return arg(); }), false)))(); }; }; // a.b a['b'] Interpreter.prototype.memberExpressionHandler = function (node) { var objectGetter = this.createClosure(node.object); var keyGetter = this.createMemberKeyGetter(node); return function () { var obj = objectGetter(); var key = keyGetter(); return obj[key]; }; }; //this Interpreter.prototype.thisExpressionHandler = function (node) { var _this = this; return function () { return _this.getCurrentContext(); }; }; // var1,var2,... Interpreter.prototype.sequenceExpressionHandler = function (node) { var _this = this; var expressions = node.expressions.map(function (item) { return _this.createClosure(item); }); return function () { var result; var len = expressions.length; for (var i = 0; i < len; i++) { var expression = expressions[i]; result = expression(); } return result; }; }; // 1 'name' Interpreter.prototype.literalHandler = function (node) { return function () { if (node.regex) { return new RegExp(node.regex.pattern, node.regex.flags); } return node.value; }; }; // var1 ... Interpreter.prototype.identifierHandler = function (node) { var _this = this; return function () { var currentScope = _this.getCurrentScope(); var data = _this.getScopeDataFromName(node.name, currentScope); _this.assertVariable(data, node.name, node); return data[node.name]; }; }; Interpreter.prototype.getIdentifierScope = function (node) { var currentScope = this.getCurrentScope(); var scope = this.getScopeFromName(node.name, currentScope); return scope; }; // a=1 a+=2 Interpreter.prototype.assignmentExpressionHandler = function (node) { var _this = this; // var s = function(){} // s.name === s if (node.left.type === "Identifier" && node.right.type === "FunctionExpression" && !node.right.id) { node.right.id = { type: "Identifier", name: node.left.name, }; } var dataGetter = this.createObjectGetter(node.left); var nameGetter = this.createNameGetter(node.left); var rightValueGetter = this.createClosure(node.right); return function () { var data = dataGetter(); var name = nameGetter(); var rightValue = rightValueGetter(); if (node.operator !== "=") { // if a is undefined // a += 1 _this.assertVariable(data, name, node); } switch (node.operator) { case "=": return (data[name] = rightValue); case "+=": return (data[name] += rightValue); case "-=": return (data[name] -= rightValue); case "*=": return (data[name] *= rightValue); case "**=": return (data[name] = Math.pow(data[name], rightValue)); case "/=": return (data[name] /= rightValue); case "%=": return (data[name] %= rightValue); case "<<=": return (data[name] <<= rightValue); case ">>=": return (data[name] >>= rightValue); case ">>>=": return (data[name] >>>= rightValue); case "&=": return (data[name] &= rightValue); case "^=": return (data[name] ^= rightValue); case "|=": return (data[name] |= rightValue); default: throw _this.createInternalThrowError(messages_1.Messages.AssignmentExpressionSyntaxError, node.type, node); } }; }; // function test(){} Interpreter.prototype.functionDeclarationHandler = function (node) { if (node.id) { var functionClosure = this.functionExpressionHandler(node); Object.defineProperty(functionClosure, "isFunctionDeclareClosure", { value: true, writable: false, configurable: false, enumerable: false, }); this.funcDeclaration(node.id.name, functionClosure); } return function () { return EmptyStatementReturn; }; }; Interpreter.prototype.getVariableName = function (node) { if (node.type === "Identifier") { return node.name; } else { throw this.createInternalThrowError(messages_1.Messages.VariableTypeSyntaxError, node.type, node); } }; // var i; // var i=1; Interpreter.prototype.variableDeclarationHandler = function (node) { var _this = this; var assignmentsClosure; var assignments = []; for (var i = 0; i < node.declarations.length; i++) { var decl = node.declarations[i]; this.varDeclaration(this.getVariableName(decl.id)); if (decl.init) { assignments.push({ type: "AssignmentExpression", operator: "=", left: decl.id, right: decl.init, }); } } if (assignments.length) { assignmentsClosure = this.createClosure({ type: "BlockStatement", body: assignments, }); } return function () { if (assignmentsClosure) { var oldValue = _this.isVarDeclMode; _this.isVarDeclMode = true; assignmentsClosure(); _this.isVarDeclMode = oldValue; } return EmptyStatementReturn; }; }; Interpreter.prototype.assertVariable = function (data, name, node) { if (data === this.globalScope.data && !(name in data)) { throw this.createInternalThrowError(messages_1.Messages.VariableUndefinedReferenceError, name, node); } }; // {...} Interpreter.prototype.programHandler = function (node) { var _this = this; // const currentScope = this.getCurrentScope(); var stmtClosures = node.body.map(function (stmt) { // if (stmt.type === "EmptyStatement") return null; return _this.createClosure(stmt); }); return function () { var result = EmptyStatementReturn; for (var i = 0; i < stmtClosures.length; i++) { var stmtClosure = stmtClosures[i]; // save last value var ret = _this.setValue(stmtClosure()); // if (!stmtClosure) continue; // EmptyStatement if (ret === EmptyStatementReturn) continue; result = ret; // BlockStatement: break label; continue label; for(){ break ... } // ReturnStatement: return xx; if (result instanceof Return || result instanceof BreakLabel || result instanceof ContinueLabel || result === Break || result === Continue) { break; } } // save last value return result; }; }; // all expression: a+1 a&&b a() a.b ... Interpreter.prototype.expressionStatementHandler = function (node) { return this.createClosure(node.expression); }; Interpreter.prototype.emptyStatementHandler = function (node) { return function () { return EmptyStatementReturn; }; }; // return xx; Interpreter.prototype.returnStatementHandler = function (node) { var argumentClosure = node.argument ? this.createClosure(node.argument) : noop; return function () { return new Return(argumentClosure()); }; }; // if else Interpreter.prototype.ifStatementHandler = function (node) { var testClosure = this.createClosure(node.test); var consequentClosure = this.createClosure(node.consequent); var alternateClosure = node.alternate ? this.createClosure(node.alternate) : /*!important*/ function () { return EmptyStatementReturn; }; return function () { return testClosure() ? consequentClosure() : alternateClosure(); }; }; // test() ? true : false Interpreter.prototype.conditionalExpressionHandler = function (node) { return this.ifStatementHandler(node); }; // for(var i = 0; i < 10; i++) {...} Interpreter.prototype.forStatementHandler = function (node) { var _this = this; var initClosure = noop; var testClosure = node.test ? this.createClosure(node.test) : function () { return true; }; var updateClosure = noop; var bodyClosure = this.createClosure(node.body); if (node.type === "ForStatement") { initClosure = node.init ? this.createClosure(node.init) : initClosure; updateClosure = node.update ? this.createClosure(node.update) : noop; } return function (pNode) { var labelName; var result = EmptyStatementReturn; var shouldInitExec = node.type === "DoWhileStatement"; if (pNode && pNode.type === "LabeledStatement") { labelName = pNode.label.name; } for (initClosure(); shouldInitExec || testClosure(); updateClosure()) { shouldInitExec = false; // save last value var ret = _this.setValue(bodyClosure()); // notice: never return Break or Continue! if (ret === EmptyStatementReturn || ret === Continue) continue; if (ret === Break) { break; } result = ret;