eval5
Version:
A JavaScript interpreter written in JavaScript
1,272 lines (1,271 loc) • 66.5 kB
JavaScript
"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;