UNPKG

l7eval5

Version:

中文 | [English](./README-en_US.md)

1,830 lines (1,422 loc) 56.9 kB
function isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Date.prototype.toString.call(Reflect.construct(Date, [], function () {})); return true; } catch (e) { return false; } } function _construct(Parent, args, Class) { if (isNativeReflectConstruct()) { _construct = Reflect.construct; } else { _construct = function _construct(Parent, args, Class) { var a = [null]; a.push.apply(a, args); var Constructor = Function.bind.apply(Parent, a); var instance = new Constructor(); if (Class) _setPrototypeOf(instance, Class.prototype); return instance; }; } return _construct.apply(null, arguments); } function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); } import { parse } from "acorn"; import { Messages, InterruptThrowError, InterruptThrowReferenceError, InterruptThrowSyntaxError } from "./messages"; var version = "0.0.3"; 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 = /*#__PURE__*/ function () { function InternalInterpreterReflection(interpreter) { this.interpreter = interpreter; } var _proto = InternalInterpreterReflection.prototype; _proto.generator = function generator() { 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 _initEnv() { // 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) { if (!(reflection instanceof InternalInterpreterReflection)) { throw new Error("Illegal call"); } var instance = reflection.generator(); for (var _len = arguments.length, params = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { params[_key - 1] = arguments[_key]; } var code = params.pop(); var interpreter = new Interpreter(instance.getGlobalScope(), instance.getOptions()); var wrapCode = "\n\t\t (function anonymous(" + params.join(",") + "){\n\t\t " + 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 = function Return(value) { this.value = value; }; var BreakLabel = function BreakLabel(value) { this.value = value; }; var ContinueLabel = function ContinueLabel(value) { this.value = value; }; /** * scope chain * * superScope * ↓ * rootScope * ↓ * globalScope * ↓ * functionScope * */ var Scope = function Scope(data, parent, name) { if (parent === void 0) { parent = null; } this.name = name; this.parent = parent; this.data = data; this.labelStack = []; }; 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, Float32Array: Float32Array, Float64Array: Float64Array, Uint32Array: Uint32Array, 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; } export var Interpreter = /*#__PURE__*/ 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); } var _proto2 = Interpreter.prototype; _proto2.initEnvironment = function initEnvironment(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); } }; _proto2.getExecStartTime = function getExecStartTime() { return this.execStartTime; }; _proto2.getExecutionTime = function getExecutionTime() { return this.execEndTime - this.execStartTime; }; _proto2.setExecTimeout = function setExecTimeout(timeout) { if (timeout === void 0) { timeout = 0; } this.options.timeout = timeout; }; _proto2.getOptions = function getOptions() { return this.options; }; _proto2.getGlobalScope = function getGlobalScope() { return this.globalScope; }; _proto2.getCurrentScope = function getCurrentScope() { return this.currentScope; }; _proto2.getCurrentContext = function getCurrentContext() { return this.currentContext; }; _proto2.isInterruptThrow = function isInterruptThrow(err) { return err instanceof InterruptThrowError || err instanceof InterruptThrowReferenceError || err instanceof InterruptThrowSyntaxError; }; _proto2.createSuperScope = function createSuperScope(ctx) { var data = Object.assign({}, BuildInObjects); var buildInObjectKeys = Object.keys(data); buildInObjectKeys.forEach(function (key) { if (key in ctx) { delete data[key]; } }); return new Scope(data, null, SuperScopeName); }; _proto2.setCurrentContext = function setCurrentContext(ctx) { this.currentContext = ctx; }; _proto2.setCurrentScope = function setCurrentScope(scope) { this.currentScope = scope; }; _proto2.evaluate = function evaluate(code) { if (code === void 0) { code = ""; } var node; if (!code) return; node = parse(code, { ranges: true, locations: true, ecmaVersion: this.options.ecmaVersion || Interpreter.ecmaVersion }); return this.evaluateNode(node, code); }; _proto2.appendCode = function appendCode(code) { return this.evaluate(code); }; _proto2.evaluateNode = function evaluateNode(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 reset() { _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(); }; _proto2.createErrorMessage = function createErrorMessage(msg, value, node) { var message = msg[1].replace("%0", String(value)); if (node !== null) { message += this.getNodePosition(node || this.lastExecNode); } return message; }; _proto2.createError = function createError(message, error) { return new error(message); }; _proto2.createThrowError = function createThrowError(message, error) { return this.createError(message, error); }; _proto2.createInternalThrowError = function createInternalThrowError(msg, value, node) { return this.createError(this.createErrorMessage(msg, value, node), msg[2]); }; _proto2.checkTimeout = function checkTimeout() { if (!this.isRunning) return false; var timeout = this.options.timeout || 0; var now = Date.now(); if (now - this.execStartTime > timeout) { return true; } return false; }; _proto2.getNodePosition = function getNodePosition(node) { if (node) { var errorCode = ""; //this.source.slice(node.start, node.end); return node.loc ? " [" + node.loc.start.line + ":" + node.loc.start.column + "]" + errorCode : ""; } return ""; }; _proto2.createClosure = function createClosure(node) { var _this2 = 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.NodeTypeSyntaxError, node.type, node); } return function () { var timeout = _this2.options.timeout; if (timeout && timeout > 0 && _this2.checkTimeout()) { throw _this2.createInternalThrowError(Messages.ExecutionTimeOutError, timeout, null); } _this2.lastExecNode = node; return closure.apply(void 0, arguments); }; } // a==b a/b ; _proto2.binaryExpressionHandler = function binaryExpressionHandler(node) { var _this3 = 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 _this3.createInternalThrowError(Messages.BinaryOperatorSyntaxError, node.operator, node); } }; } // a && b ; _proto2.logicalExpressionHandler = function logicalExpressionHandler(node) { var _this4 = 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 _this4.createInternalThrowError(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() ; _proto2.unaryExpressionHandler = function unaryExpressionHandler(node) { var _this5 = this; switch (node.operator) { case "delete": var objectGetter = this.createObjectGetter(node.argument); var nameGetter = 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(); var name = nameGetter(); return delete obj[name]; }; default: var expression; // for typeof undefined var // typeof adf9ad if (node.operator === "typeof" && node.argument.type === "Identifier") { var _objectGetter = this.createObjectGetter(node.argument); var _nameGetter = this.createNameGetter(node.argument); expression = function expression() { return _objectGetter()[_nameGetter()]; }; } else { expression = this.createClosure(node.argument); } return function () { var value = expression(); 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 _this5.createInternalThrowError(Messages.UnaryOperatorSyntaxError, node.operator, node); } }; } } // ++a --a ; _proto2.updateExpressionHandler = function updateExpressionHandler(node) { var _this6 = this; var objectGetter = this.createObjectGetter(node.argument); var nameGetter = this.createNameGetter(node.argument); return function () { var obj = objectGetter(); var name = nameGetter(); _this6.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 _this6.createInternalThrowError(Messages.UpdateOperatorSyntaxError, node.operator, node); } }; } // var o = {a: 1, b: 's', get name(){}, set name(){} ...} ; _proto2.objectExpressionHandler = function objectExpressionHandler(node) { var _this7 = 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.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] = _this7.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] ; _proto2.arrayExpressionHandler = function arrayExpressionHandler(node) { var _this8 = this; //fix: [,,,1,2] var items = node.elements.map(function (element) { return element ? _this8.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; }; }; _proto2.safeObjectGet = function safeObjectGet(obj, key, node) { return obj[key]; }; _proto2.createCallFunctionGetter = function createCallFunctionGetter(node) { var _this9 = this; switch (node.type) { case "MemberExpression": var objectGetter = this.createClosure(node.object); var keyGetter = this.createMemberKeyGetter(node); var source = this.source; return function () { var obj = objectGetter(); var key = keyGetter(); var func = _this9.safeObjectGet(obj, key, node); if (!func || !isFunction(func)) { var name = source.slice(node.start, node.end); throw _this9.createInternalThrowError(Messages.FunctionUndefinedReferenceError, name, node); } // obj.eval = eval // obj.eval(...) if (func.__IS_EVAL_FUNC) { return function (code) { return func(new InternalInterpreterReflection(_this9), code, true); }; } // obj.func = Function // obj.func(...) if (func.__IS_FUNCTION_FUNC) { return function () { for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) { args[_key2] = arguments[_key2]; } return func.apply(void 0, [new InternalInterpreterReflection(_this9)].concat(args)); }; } // 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 = this.createClosure(node); return function () { var name = ""; if (node.type === "Identifier") { name = node.name; } // const name: string = (<ESTree.Identifier>node).name; var func = closure(); if (!func || !isFunction(func)) { throw _this9.createInternalThrowError(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 = _this9.getScopeFromName(name, _this9.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(_this9), 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(_this9), code, true); }; } // Function('a', 'b', 'return a+b') if (func.__IS_FUNCTION_FUNC) { return function () { for (var _len3 = arguments.length, args = new Array(_len3), _key3 = 0; _key3 < _len3; _key3++) { args[_key3] = arguments[_key3]; } return func.apply(void 0, [new InternalInterpreterReflection(_this9)].concat(args)); }; } var ctx = _this9.options.globalContextInFunction; // with(obj) { // test() // test.call(obj, ...) // } if (node.type === "Identifier") { var scope = _this9.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() ; _proto2.callExpressionHandler = function callExpressionHandler(node) { var _this10 = this; var funcGetter = this.createCallFunctionGetter(node.callee); var argsGetter = node.arguments.map(function (arg) { return _this10.createClosure(arg); }); return function () { return funcGetter().apply(void 0, argsGetter.map(function (arg) { return arg(); })); }; } // var f = function() {...} ; _proto2.functionExpressionHandler = function functionExpressionHandler(node) { var _this11 = 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 _this11.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 func() { for (var _len4 = arguments.length, args = new Array(_len4), _key4 = 0; _key4 < _len4; _key4++) { args[_key4] = arguments[_key4]; } self.callStack.push("" + name); var prevScope = self.getCurrentScope(); var currentScope = createScope(runtimeScope, "FunctionScope(" + 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 value() { return source.slice(node.start, node.end); }, writable: true, configurable: true, enumerable: false }); Object.defineProperty(func, "valueOf", { value: function value() { return source.slice(node.start, node.end); }, writable: true, configurable: true, enumerable: false }); return func; }; } // new Ctrl() ; _proto2.newExpressionHandler = function newExpressionHandler(node) { var _this12 = this; var source = this.source; var expression = this.createClosure(node.callee); var args = node.arguments.map(function (arg) { return _this12.createClosure(arg); }); return function () { var construct = expression(); if (!isFunction(construct) || construct.__IS_EVAL_FUNC) { var callee = node.callee; var name = source.slice(callee.start, callee.end); throw _this12.createInternalThrowError(Messages.IsNotConstructor, name, node); } // new Function(...) if (construct.__IS_FUNCTION_FUNC) { return construct.apply(void 0, [new InternalInterpreterReflection(_this12)].concat(args.map(function (arg) { return arg(); }))); } return _construct(construct, args.map(function (arg) { return arg(); })); }; } // a.b a['b'] ; _proto2.memberExpressionHandler = function memberExpressionHandler(node) { var objectGetter = this.createClosure(node.object); var keyGetter = this.createMemberKeyGetter(node); return function () { var obj = objectGetter(); var key = keyGetter(); return obj[key]; }; } //this ; _proto2.thisExpressionHandler = function thisExpressionHandler(node) { var _this13 = this; return function () { return _this13.getCurrentContext(); }; } // var1,var2,... ; _proto2.sequenceExpressionHandler = function sequenceExpressionHandler(node) { var _this14 = this; var expressions = node.expressions.map(function (item) { return _this14.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' ; _proto2.literalHandler = function literalHandler(node) { return function () { if (node.regex) { return new RegExp(node.regex.pattern, node.regex.flags); } return node.value; }; } // var1 ... ; _proto2.identifierHandler = function identifierHandler(node) { var _this15 = this; return function () { var currentScope = _this15.getCurrentScope(); var data = _this15.getScopeDataFromName(node.name, currentScope); _this15.assertVariable(data, node.name, node); return data[node.name]; }; }; _proto2.getIdentifierScope = function getIdentifierScope(node) { var currentScope = this.getCurrentScope(); var scope = this.getScopeFromName(node.name, currentScope); return scope; } // a=1 a+=2 ; _proto2.assignmentExpressionHandler = function assignmentExpressionHandler(node) { var _this16 = 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 _this16.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 _this16.createInternalThrowError(Messages.AssignmentExpressionSyntaxError, node.type, node); } }; } // function test(){} ; _proto2.functionDeclarationHandler = function functionDeclarationHandler(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; }; }; _proto2.getVariableName = function getVariableName(node) { if (node.type === "Identifier") { return node.name; } else { throw this.createInternalThrowError(Messages.VariableTypeSyntaxError, node.type, node); } } // var i; // var i=1; ; _proto2.variableDeclarationHandler = function variableDeclarationHandler(node) { var _this17 = 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 = _this17.isVarDeclMode; _this17.isVarDeclMode = true; assignmentsClosure(); _this17.isVarDeclMode = oldValue; } return EmptyStatementReturn; }; }; _proto2.assertVariable = function assertVariable(data, name, node) { if (data === this.globalScope.data && !(name in data)) { throw this.createInternalThrowError(Messages.VariableUndefinedReferenceError, name, node); } } // {...} ; _proto2.programHandler = function programHandler(node) { var _this18 = this; // const currentScope = this.getCurrentScope(); var stmtClosures = node.body.map(function (stmt) { // if (stmt.type === "EmptyStatement") return null; return _this18.createClosure(stmt); }); return function () { var result = EmptyStatementReturn; for (var i = 0; i < stmtClosures.length; i++) { var stmtClosure = stmtClosures[i]; // save last value var ret = _this18.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 ... ; _proto2.expressionStatementHandler = function expressionStatementHandler(node) { return this.createClosure(node.expression); }; _proto2.emptyStatementHandler = function emptyStatementHandler(node) { return function () { return EmptyStatementReturn; }; } // return xx; ; _proto2.returnStatementHandler = function returnStatementHandler(node) { var argumentClosure = node.argument ? this.createClosure(node.argument) : noop; return function () { return new Return(argumentClosure()); }; } // if else ; _proto2.ifStatementHandler = function ifStatementHandler(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 ; _proto2.conditionalExpressionHandler = function conditionalExpressionHandler(node) { return this.ifStatementHandler(node); } // for(var i = 0; i < 10; i++) {...} ; _proto2.forStatementHandler = function forStatementHandler(node) { var _this19 = 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 = _this19.setValue(bodyClosure()); // notice: never return Break or Continue! if (ret === EmptyStatementReturn || ret === Continue) continue; if (ret === Break) { break; } result = ret; // stop continue label if (result instanceof ContinueLabel && result.value === labelName) { result = EmptyStatementReturn; continue; } if (result instanceof Return || result instanceof BreakLabel || result instanceof ContinueLabel) { break; } } return result; }; } // while(1) {...} ; _proto2.whileStatementHandler = function whileStatementHandler(node) { return this.forStatementHandler(node); }; _proto2.doWhileStatementHandler = function doWhileStatementHandler(node) { return this.forStatementHandler(node); }; _proto2.forInStatementHandler = function forInStatementHandler(node) { var _this20 = this; // for( k in obj) or for(o.k in obj) ... var left = node.left; var rightClosure = this.createClosure(node.right); var bodyClosure = this.createClosure(node.body); // for(var k in obj) {...} if (node.left.type === "VariableDeclaration") { // init var k this.createClosure(node.left)(); // reset left // for( k in obj) left = node.left.declarations[0].id; } return function (pNode) { var labelName; var result = EmptyStatementReturn; var x; if (pNode && pNode.type === "LabeledStatement") { labelName = pNode.label.name; } var data = rightClosure(); for (x in data) { // assign left to scope // k = x // o.k = x _this20.assignmentExpressionHandler({ type: "AssignmentExpression", operator: "=", left: left, right: { type: "Literal", value: x } })(); // save last value var ret = _this20.setValue(bodyClosure()); // notice: never return Break or Continue! if (ret === EmptyStatementReturn || ret === Continue) continue; if (ret === Break) { break; } result = ret; // stop continue label if (result instanceof ContinueLabel && result.value === labelName) { result = EmptyStatementReturn; continue; } if (result instanceof Return || result instanceof BreakLabel || result instanceof ContinueLabel) { break; } } return result; }; }; _proto2.withStatementHandler = function withStatementHandler(node) { var _this21 = this; var objectClosure = this.createClosure(node.object); var bodyClosure = this.createClosure(node.body); return function () { var data = objectClosure(); var currentScope = _this21.getCurrentScope(); var newScope = new Scope(data, currentScope, WithScopeName); // const data = objectClosure(); // copy all properties // for (let k in data) { // newScope.data[k] = data[k]; // } _this21.setCurrentScope(newScope); // save last value var result = _this21.setValue(bodyClosure()); _this21.setCurrentScope(currentScope); return result; }; }; _proto2.throwStatementHandler = function throwStatementHandler(node) { var _this22 = this; var argumentClosure = this.createClosure(node.argument); return function () { _this22.setValue(undefined); throw argumentClosure(); }; } // try{...}catch(e){...}finally{} ; _proto2.tryStatementHandler = function tryStatementHandler(node) { var _this23 = this; var blockClosure = this.createClosure(node.block); var handlerClosure = node.handler ? this.catchClauseHandler(node.handler) : null; var finalizerClosure = node.finalizer ? this.createClosure(node.finalizer) : null; return function () { var currentScope = _this23.getCurrentScope(); var currentContext = _this23.getCurrentContext(); var labelStack = currentScope.labelStack.concat([]); var callStack = _this23.callStack.concat([]); var result = EmptyStatementReturn; var finalReturn; var throwError; var reset = function reset() { _this23.setCurrentScope(currentScope); //reset scope _this23.setCurrentContext(currentContext); //reset context currentScope.labelStack = labelStack; //reset label stack _this23.callStack = callStack; //reset call stack }; /** * try{...}catch(e){...}finally{...} execution sequence: * try stmt * try throw * catch stmt (if) * finally stmt * * finally throw or finally return * catch throw or catch return * try return */ try { result = _this23.setValue(blockClosure()); if (result instanceof Return) { finalReturn = result; } } catch (err) { reset(); if (_this23.isInterruptThrow(err)) { throw err; } if (handlerClosure) { try { result = _this23.setValue(handlerClosure(err)); if (result instanceof Return) { finalReturn = result; } } catch (err) { reset(); if (_this23.isInterruptThrow(err)) { throw err; } // save catch throw error throwError = err; } } } // finally { if (finalizerClosure) { try { //do not save finally result result = finalizerClosure(); if (result instanceof Return) { finalReturn = result; } // finalReturn = finalizerClosure(); } catch (err) { reset(); if (_this23.isInterruptThrow(err)) { throw err; } // save finally throw error throwError = err; } // if (finalReturn instanceof Return) { // result = finalReturn; // } } // } if (throwError) throw throwError; if (finalReturn) { return finalReturn; } return result; }; } // ... catch(e){...} ; _proto2.catchClauseHandler = function catchClauseHandler(node) { var _this24 = this; var paramNameGetter = this.createParamNameGetter(node.param); var bodyClosure = this.createClosure(node.body); return function (e) { var result; var currentScope = _this24.getCurrentScope(); var scopeData = currentScope.data; // get param name "e" var paramName = paramNameGetter(); var isInScope = hasOwnProperty.call(scopeData, paramName); //paramName in scopeData; // save "e" var oldValue = scopeData[paramName]; // add "e" to scope scopeData[paramName] = e; // run result = bodyClosure(); // reset "e" if (isInScope) { scopeData[paramName] = oldValue; } else { //unset delete scopeData[paramName]; } return result; }; }; _proto2.continueStatementHandler = function continueStatementHandler(node) { return function () { return node.label ? new ContinueLabel(node.label.name) : Continue; }; }; _proto2.breakStatementHandler = function breakStatementHandler(n