prepack
Version:
Execute a JS bundle, serialize global state and side effects to a snapshot that can be quickly restored.
1,208 lines (985 loc) • 59.8 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.FunctionImplementation = undefined;
var _errors = require("../errors.js");
var _completions = require("../completions.js");
var _realm = require("../realm.js");
var _environment = require("../environment.js");
var _index = require("../values/index.js");
var _call = require("./call.js");
var _abstract = require("../methods/abstract.js");
var _construct = require("../methods/construct.js");
var _index2 = require("../methods/index.js");
var _iterator = require("../methods/iterator.js");
var _ObjectExpression = require("../evaluators/ObjectExpression.js");
var _singletons = require("../singletons.js");
var _traverseFast = require("../utils/traverse-fast.js");
var _traverseFast2 = _interopRequireDefault(_traverseFast);
var _invariant = require("../invariant.js");
var _invariant2 = _interopRequireDefault(_invariant);
var _parse = require("../utils/parse.js");
var _parse2 = _interopRequireDefault(_parse);
var _strict = require("../utils/strict.js");
var _strict2 = _interopRequireDefault(_strict);
var _babelTypes = require("babel-types");
var t = _interopRequireWildcard(_babelTypes);
function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function InternalCall(realm, F, thisArgument, argsList, tracerIndex) {
// 1. Assert: F is an ECMAScript function object.
(0, _invariant2.default)(F instanceof _index.FunctionValue, "expected function value");
// Tracing: Give all registered tracers a chance to detour, wrapping around each other if needed.
while (tracerIndex < realm.tracers.length) {
let tracer = realm.tracers[tracerIndex];
let nextIndex = ++tracerIndex;
let detourResult = tracer.detourCall(F, thisArgument, argsList, undefined, () => InternalCall(realm, F, thisArgument, argsList, nextIndex));
if (detourResult instanceof _index.Value) return detourResult;
}
// 2. If F's [[FunctionKind]] internal slot is "classConstructor", throw a TypeError exception.
if (F.$FunctionKind === "classConstructor") throw realm.createErrorThrowCompletion(realm.intrinsics.TypeError, "not callable");
// 3. Let callerContext be the running execution context.
let callerContext = realm.getRunningContext();
// 4. Let calleeContext be PrepareForOrdinaryCall(F, undefined).
let calleeContext = (0, _call.PrepareForOrdinaryCall)(realm, F, undefined);
let calleeEnv = calleeContext.lexicalEnvironment;
let result;
try {
for (let t1 of realm.tracers) t1.beforeCall(F, thisArgument, argsList, undefined);
// 5. Assert: calleeContext is now the running execution context.
(0, _invariant2.default)(realm.getRunningContext() === calleeContext, "calleeContext should be current execution context");
// 6. Perform OrdinaryCallBindThis(F, calleeContext, thisArgument).
(0, _call.OrdinaryCallBindThis)(realm, F, calleeContext, thisArgument);
// 7. Let result be OrdinaryCallEvaluateBody(F, argumentsList).
result = (0, _call.OrdinaryCallEvaluateBody)(realm, F, argsList);
} finally {
// 8. Remove calleeContext from the execution context stack and restore callerContext as the running execution context.
realm.popContext(calleeContext);
realm.onDestroyScope(calleeContext.lexicalEnvironment);
if (calleeContext.lexicalEnvironment !== calleeEnv) realm.onDestroyScope(calleeEnv);
(0, _invariant2.default)(realm.getRunningContext() === callerContext);
for (let t2 of realm.tracers) t2.afterCall(F, thisArgument, argsList, undefined, result);
}
// 9. If result.[[Type]] is return, return NormalCompletion(result.[[Value]]).
if (result instanceof _completions.ReturnCompletion) {
return result.value;
}
// 10. ReturnIfAbrupt(result). or if possibly abrupt
if (result instanceof _completions.Completion) {
throw result;
}
// 11. Return NormalCompletion(undefined).
return realm.intrinsics.undefined;
}
// ECMA262 9.4.1.1
/**
* Copyright (c) 2017-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
function $BoundCall(realm, F, thisArgument, argumentsList) {
// 1. Let target be the value of F's [[BoundTargetFunction]] internal slot.
let target = F.$BoundTargetFunction;
// 2. Let boundThis be the value of F's [[BoundThis]] internal slot.
let boundThis = F.$BoundThis;
// 3. Let boundArgs be the value of F's [[BoundArguments]] internal slot.
let boundArgs = F.$BoundArguments;
// 4. Let args be a new list containing the same values as the list boundArgs in the same order followed
// by the same values as the list argumentsList in the same order.
let args = boundArgs.concat(argumentsList);
// 5. Return ? Call(target, boundThis, args).
return (0, _call.Call)(realm, target, boundThis, args);
}
// ECMA262 9.4.1.2
function $BoundConstruct(realm, F, argumentsList, newTarget) {
// 1. Let target be the value of F's [[BoundTargetFunction]] internal slot.
let target = F.$BoundTargetFunction;
// 2. Assert: target has a [[Construct]] internal method.
(0, _invariant2.default)(target.$Construct !== undefined, "doesn't have a construct internal method");
// 3. Let boundArgs be the value of F's [[BoundArguments]] internal slot.
let boundArgs = F.$BoundArguments;
// 4. Let args be a new list containing the same values as the list boundArgs in the same order followed
// by the same values as the list argumentsList in the same order.
let args = boundArgs.concat(argumentsList);
// 5. If SameValue(F, newTarget) is true, let newTarget be target.
if ((0, _abstract.SameValue)(realm, F, newTarget)) newTarget = target;
// 6. Return ? Construct(target, args, newTarget).
return (0, _construct.Construct)(realm, target, args, newTarget);
}
function InternalConstruct(realm, F, argumentsList, newTarget, thisArgument, tracerIndex) {
// 1. Assert: F is an ECMAScript function object.
(0, _invariant2.default)(F instanceof _index.FunctionValue, "expected function");
// 2. Assert: Type(newTarget) is Object.
(0, _invariant2.default)(newTarget instanceof _index.ObjectValue, "expected object");
if (!realm.hasRunningContext()) {
(0, _invariant2.default)(realm.useAbstractInterpretation);
throw new _errors.FatalError("no running context");
}
// 3. Let callerContext be the running execution context.
let callerContext = realm.getRunningContext();
// 4. Let kind be F's [[ConstructorKind]] internal slot.
let kind = F.$ConstructorKind;
// 5. If kind is "base", then
if (thisArgument === undefined && kind === "base") {
// a. Let thisArgument be ? OrdinaryCreateFromConstructor(newTarget, "%ObjectPrototype%").
thisArgument = _singletons.Create.OrdinaryCreateFromConstructor(realm, newTarget, "ObjectPrototype");
}
// Tracing: Give all registered tracers a chance to detour, wrapping around each other if needed.
while (tracerIndex < realm.tracers.length) {
let tracer = realm.tracers[tracerIndex];
let nextIndex = ++tracerIndex;
let detourResult = tracer.detourCall(F, thisArgument, argumentsList, newTarget, () => InternalConstruct(realm, F, argumentsList, newTarget, thisArgument, nextIndex));
if (detourResult instanceof _index.ObjectValue) return detourResult;
(0, _invariant2.default)(detourResult === undefined);
}
// 6. Let calleeContext be PrepareForOrdinaryCall(F, newTarget).
let calleeContext = (0, _call.PrepareForOrdinaryCall)(realm, F, newTarget);
let calleeEnv = calleeContext.lexicalEnvironment;
// 7. Assert: calleeContext is now the running execution context.
(0, _invariant2.default)(realm.getRunningContext() === calleeContext, "expected calleeContext to be running context");
let result, envRec;
try {
for (let t1 of realm.tracers) t1.beforeCall(F, thisArgument, argumentsList, newTarget);
// 8. If kind is "base", perform OrdinaryCallBindThis(F, calleeContext, thisArgument).
if (kind === "base") {
(0, _invariant2.default)(thisArgument, "this wasn't initialized for some reason");
(0, _call.OrdinaryCallBindThis)(realm, F, calleeContext, thisArgument);
}
// 9. Let constructorEnv be the LexicalEnvironment of calleeContext.
let constructorEnv = calleeContext.lexicalEnvironment;
// 10. Let envRec be constructorEnv's EnvironmentRecord.
envRec = constructorEnv.environmentRecord;
// 11. Let result be OrdinaryCallEvaluateBody(F, argumentsList).
result = (0, _call.OrdinaryCallEvaluateBody)(realm, F, argumentsList);
} finally {
// 12. Remove calleeContext from the execution context stack and restore callerContext as the running execution context.
realm.popContext(calleeContext);
realm.onDestroyScope(calleeContext.lexicalEnvironment);
if (calleeContext.lexicalEnvironment !== calleeEnv) realm.onDestroyScope(calleeEnv);
(0, _invariant2.default)(realm.getRunningContext() === callerContext);
for (let t2 of realm.tracers) t2.afterCall(F, thisArgument, argumentsList, newTarget, result);
}
// 13. If result.[[Type]] is return, then
if (result instanceof _completions.ReturnCompletion) {
// a. If Type(result.[[Value]]) is Object, return NormalCompletion(result.[[Value]]).
if (result.value.mightBeObject()) {
return result.value.throwIfNotConcreteObject();
}
// b. If kind is "base", return NormalCompletion(thisArgument).
if (kind === "base") {
(0, _invariant2.default)(thisArgument, "this wasn't initialized for some reason");
return thisArgument;
}
// c. If result.[[Value]] is not undefined, throw a TypeError exception.
if (!result.value.mightBeUndefined()) throw realm.createErrorThrowCompletion(realm.intrinsics.TypeError, "constructor must return Object");
result.value.throwIfNotConcrete();
} else if (result instanceof _completions.AbruptCompletion) {
// 14. Else, ReturnIfAbrupt(result).
throw result;
}
// 15. Return ? envRec.GetThisBinding().
let envRecThisBinding = envRec.GetThisBinding();
(0, _invariant2.default)(envRecThisBinding instanceof _index.ObjectValue);
return envRecThisBinding;
}
class FunctionImplementation {
FindVarScopedDeclarations(ast_node) {
function FindVarScopedDeclarationsFor(ast, level) {
let statements = [];
switch (ast.type) {
case "Program":
statements = ast.body;
break;
case "BlockStatement":
statements = ast.body;
break;
case "DoWhileStatement":
statements = [ast.body];
break;
case "WhileStatement":
statements = [ast.body];
break;
case "IfStatement":
let astIfStatement = ast;
statements = [astIfStatement.consequent, astIfStatement.alternate];
break;
case "ForStatement":
let astForStatement = ast;
statements = [astForStatement.init, astForStatement.body];
break;
case "ForInStatement":
let astForInStatement = ast;
statements = [astForInStatement.left, astForInStatement.body];
break;
case "ForOfStatement":
let astForOfStatement = ast;
statements = [astForOfStatement.left, astForOfStatement.body];
break;
case "LabeledStatement":
statements = [ast.body];
break;
case "WithStatement":
statements = [ast.body];
break;
case "SwitchStatement":
for (let switchCase of ast.cases) {
statements = statements.concat(switchCase.consequent);
}
break;
case "TryStatement":
let astTryStatement = ast;
statements = [astTryStatement.block];
if (astTryStatement.finalizer) statements.push(astTryStatement.finalizer);
if (astTryStatement.handler) statements.push(astTryStatement.handler.body);
break;
case "VariableDeclaration":
return ast.kind === "var" ? [ast] : [];
case "FunctionDeclaration":
return level < 2 ? [ast] : [];
default:
return [];
}
let decls = [];
for (let statement of statements) {
if (statement) {
decls = decls.concat(FindVarScopedDeclarationsFor(statement, level + 1));
}
}
return decls;
}
return FindVarScopedDeclarationsFor(ast_node, 0);
}
// ECMA262 9.2.12
FunctionDeclarationInstantiation(realm, func, argumentsList) {
// 1. Let calleeContext be the running execution context.
let calleeContext = realm.getRunningContext();
// 2. Let env be the LexicalEnvironment of calleeContext.
let env = calleeContext.lexicalEnvironment;
// 3. Let envRec be env's EnvironmentRecord.
let envRec = env.environmentRecord;
// 4. Let code be the value of the [[ECMAScriptCode]] internal slot of func.
let code = func.$ECMAScriptCode;
(0, _invariant2.default)(code !== undefined);
// 5. Let strict be the value of the [[Strict]] internal slot of func.
let strict = func.$Strict;
// 6. Let formals be the value of the [[FormalParameters]] internal slot of func.
let formals = func.$FormalParameters;
(0, _invariant2.default)(formals !== undefined);
// 7. Let parameterNames be the BoundNames of formals.
let parameterNames = Object.create(null);
for (let param of formals) {
let paramBindings = t.getBindingIdentifiers(param, true);
for (let name in paramBindings) {
parameterNames[name] = (parameterNames[name] || []).concat(paramBindings[name]);
}
}
// 8. If parameterNames has any duplicate entries, let hasDuplicates be true. Otherwise, let hasDuplicates be false.
let hasDuplicates = false;
for (let name in parameterNames) {
let identifiers = parameterNames[name];
if (identifiers.length > 1) hasDuplicates = true;
}
parameterNames = Object.keys(parameterNames);
// 9. Let simpleParameterList be IsSimpleParameterList of formals.
let simpleParameterList = true;
for (let param of formals) {
if (param.type !== "Identifier") {
simpleParameterList = false;
break;
}
}
// 10. Let hasParameterExpressions be ContainsExpression of formals.
let hasParameterExpressions = false;
(0, _invariant2.default)(formals !== undefined);
for (let param of formals) {
if (_singletons.Environment.ContainsExpression(realm, param)) {
hasParameterExpressions = true;
break;
}
}
// 11. Let varNames be the VarDeclaredNames of code.
let varNames = [];
(0, _traverseFast2.default)(code, node => {
if (node.type === "VariableDeclaration" && node.kind === "var") {
varNames = varNames.concat(Object.keys(t.getBindingIdentifiers(node)));
}
if (node.type === "FunctionExpression" || node.type === "FunctionDeclaration") {
return true;
}
return false;
});
// 12. Let varDeclarations be the VarScopedDeclarations of code.
let varDeclarations = this.FindVarScopedDeclarations(code);
// 13. Let lexicalNames be the LexicallyDeclaredNames of code.
let lexicalNames = [];
// 14. Let functionNames be an empty List.
let functionNames = [];
// 15. Let functionsToInitialize be an empty List.
let functionsToInitialize = [];
// 16. For each d in varDeclarations, in reverse list order do
for (let d of varDeclarations.reverse()) {
// a. If d is neither a VariableDeclaration or a ForBinding, then
if (d.type !== "VariableDeclaration") {
// i. Assert: d is either a FunctionDeclaration or a GeneratorDeclaration.
(0, _invariant2.default)(d.type === "FunctionDeclaration" || d.type === "GeneratorDeclaration");
// ii. Let fn be the sole element of the BoundNames of d.
let fn = _singletons.Environment.BoundNames(realm, d)[0];
// iii. If fn is not an element of functionNames, then
if (functionNames.indexOf(fn) < 0) {
// 1. Insert fn as the first element of functionNames.
functionNames.unshift(fn);
// 2. NOTE If there are multiple FunctionDeclarations or GeneratorDeclarations for the same name, the last declaration is used.
// 3. Insert d as the first element of functionsToInitialize.
functionsToInitialize.unshift(d);
}
}
}
// 17. Let argumentsObjectNeeded be true.
let argumentsObjectNeeded = true;
// 18. If the value of the [[realmMode]] internal slot of func is lexical, then
if (func.$ThisMode === "lexical") {
// a. NOTE Arrow functions never have an arguments objects.
// b. Let argumentsObjectNeeded be false.
argumentsObjectNeeded = false;
} else if (parameterNames.indexOf("arguments") >= 0) {
// 19. Else if "arguments" is an element of parameterNames, then
// a. Let argumentsObjectNeeded be false.
argumentsObjectNeeded = false;
} else if (hasParameterExpressions === false) {
// 20. Else if hasParameterExpressions is false, then
// a. If "arguments" is an element of functionNames or if "arguments" is an element of lexicalNames, then
if (functionNames.indexOf("arguments") >= 0 || lexicalNames.indexOf("arguments") >= 0) {
// i. Let argumentsObjectNeeded be false.
argumentsObjectNeeded = true;
}
}
// 21. For each String paramName in parameterNames, do
for (let paramName of parameterNames) {
// a. Let alreadyDeclared be envRec.HasBinding(paramName).
let alreadyDeclared = envRec.HasBinding(paramName);
// b. NOTE Early errors ensure that duplicate parameter names can only occur in non-strict functions that do not have parameter default values or rest parameters.
// c. If alreadyDeclared is false, then
if (alreadyDeclared === false) {
// i. Perform ! envRec.CreateMutableBinding(paramName, false).
envRec.CreateMutableBinding(paramName, false);
// ii. If hasDuplicates is true, then
if (hasDuplicates === true) {
// 1. Perform ! envRec.InitializeBinding(paramName, undefined).
envRec.InitializeBinding(paramName, realm.intrinsics.undefined);
}
}
}
// 22. If argumentsObjectNeeded is true, then
if (argumentsObjectNeeded === true) {
let ao;
// a. If strict is true or if simpleParameterList is false, then
if (strict === true || simpleParameterList === false) {
// i. Let ao be CreateUnmappedArgumentsObject(argumentsList).
ao = _singletons.Create.CreateUnmappedArgumentsObject(realm, argumentsList);
} else {
// b. Else,
// i. NOTE mapped argument object is only provided for non-strict functions that don't have a rest parameter, any parameter default value initializers, or any destructured parameters.
// ii. Let ao be CreateMappedArgumentsObject(func, formals, argumentsList, envRec).
(0, _invariant2.default)(formals !== undefined);
ao = _singletons.Create.CreateMappedArgumentsObject(realm, func, formals, argumentsList, envRec);
}
// c. If strict is true, then
if (strict === true) {
// i. Perform ! envRec.CreateImmutableBinding("arguments", false).
envRec.CreateImmutableBinding("arguments", false);
} else {
// d. Else,
// i. Perform ! envRec.CreateMutableBinding("arguments", false).
envRec.CreateMutableBinding("arguments", false);
}
// e. Call envRec.InitializeBinding("arguments", ao).
envRec.InitializeBinding("arguments", ao);
// f. Append "arguments" to parameterNames.
parameterNames.push("arguments");
}
// 23. Let iteratorRecord be Record {[[Iterator]]: CreateListIterator(argumentsList), [[Done]]: false}.
let iteratorRecord = {
$Iterator: (0, _iterator.CreateListIterator)(realm, argumentsList),
$Done: false
};
// 24. If hasDuplicates is true, then
if (hasDuplicates === true) {
// a. Perform ? IteratorBindingInitialization for formals with iteratorRecord and undefined as arguments.
(0, _invariant2.default)(formals !== undefined);
_singletons.Environment.IteratorBindingInitialization(realm, formals, iteratorRecord, strict);
} else {
// 25. Else,
// a. Perform ? IteratorBindingInitialization for formals with iteratorRecord and env as arguments.
(0, _invariant2.default)(formals !== undefined);
_singletons.Environment.IteratorBindingInitialization(realm, formals, iteratorRecord, strict, env);
}
// 26. If hasParameterExpressions is false, then
let varEnv, varEnvRec;
if (hasParameterExpressions === false) {
// a. NOTE Only a single lexical environment is needed for the parameters and top-level vars.
// b. Let instantiatedVarNames be a copy of the List parameterNames.
let instantiatedVarNames = parameterNames.slice();
// c. For each n in varNames, do
for (let n of varNames) {
// i. If n is not an element of instantiatedVarNames, then
if (instantiatedVarNames.indexOf(n) < 0) {
// 1. Append n to instantiatedVarNames.
instantiatedVarNames.push(n);
// 2. Perform ! envRec.CreateMutableBinding(n, false).
envRec.CreateMutableBinding(n, false);
// 3. Call envRec.InitializeBinding(n, undefined).
envRec.InitializeBinding(n, realm.intrinsics.undefined);
}
}
// e. Let varEnv be env.
varEnv = env;
// f. Let varEnvRec be envRec.
varEnvRec = envRec;
} else {
// 27. Else,
// a. NOTE A separate Environment Record is needed to ensure that closures created by expressions in the formal parameter list do not have visibility of declarations in the function body.
// b. Let varEnv be NewDeclarativeEnvironment(env).
varEnv = _singletons.Environment.NewDeclarativeEnvironment(realm, env);
// At this point we haven't set any context's lexical environment to varEnv (and we might never do so),
// so it shouldn't be active
realm.activeLexicalEnvironments.delete(varEnv);
// c. Let varEnvRec be varEnv's EnvironmentRecord.
varEnvRec = varEnv.environmentRecord;
// d. Set the VariableEnvironment of calleeContext to varEnv.
calleeContext.variableEnvironment = varEnv;
// e. Let instantiatedVarNames be a new empty List.
let instantiatedVarNames = [];
// f. For each n in varNames, do
for (let n of varNames) {
// i. If n is not an element of instantiatedVarNames, then
if (instantiatedVarNames.indexOf(n) < 0) {
// 1. Append n to instantiatedVarNames.
instantiatedVarNames.push(n);
// 2. Perform ! varEnvRec.CreateMutableBinding(n, false).
varEnvRec.CreateMutableBinding(n, false);
// 3. If n is not an element of parameterNames or if n is an element of functionNames, let initialValue be undefined.
let initialValue;
if (parameterNames.indexOf(n) < 0 || functionNames.indexOf(n) < 0) {
initialValue = realm.intrinsics.undefined;
} else {
// 4. Else,
// a. Let initialValue be ! envRec.GetBindingValue(n, false).
initialValue = envRec.GetBindingValue(n, false);
}
// 5. Call varEnvRec.InitializeBinding(n, initialValue).
varEnvRec.InitializeBinding(n, initialValue);
// 6. NOTE vars whose names are the same as a formal parameter, initially have the same value as the corresponding initialized parameter.
}
}
}
// 28. NOTE: Annex B.3.3.1 adds additional steps at realm point.
let lexEnv;
// 29. If strict is false, then
if (strict === false) {
// a. Let lexEnv be NewDeclarativeEnvironment(varEnv).
lexEnv = _singletons.Environment.NewDeclarativeEnvironment(realm, varEnv);
// b. NOTE: Non-strict functions use a separate lexical Environment Record for top-level lexical declarations so that a direct eval (see 12.3.4.1) can determine whether any var scoped declarations introduced by the eval code conflict with pre-existing top-level lexically scoped declarations. realm is not needed for strict functions because a strict direct eval always places all declarations into a new Environment Record.
} else {
// 30. Else, let lexEnv be varEnv.
lexEnv = varEnv;
// Since we previously removed varEnv, make sure to re-add it when it's used.
realm.activeLexicalEnvironments.add(varEnv);
}
// 31. Let lexEnvRec be lexEnv's EnvironmentRecord.
let lexEnvRec = lexEnv.environmentRecord;
// 32. Set the LexicalEnvironment of calleeContext to lexEnv.
calleeContext.lexicalEnvironment = lexEnv;
// 33. Let lexDeclarations be the LexicallyScopedDeclarations of code.
let lexDeclarations = [];
// 34. For each element d in lexDeclarations do
for (let d of lexDeclarations) {
// a. NOTE A lexically declared name cannot be the same as a function/generator declaration, formal parameter, or a var name. Lexically declared names are only instantiated here but not initialized.
// b. For each element dn of the BoundNames of d do
for (let dn of _singletons.Environment.BoundNames(realm, d)) {
// i. If IsConstantDeclaration of d is true, then
if (d.kind === "const") {
// 1. Perform ! lexEnvRec.CreateImmutableBinding(dn, true).
lexEnvRec.CreateImmutableBinding(dn, true);
} else {
// ii. Else,
// 1. Perform ! lexEnvRec.CreateMutableBinding(dn, false).
lexEnvRec.CreateMutableBinding(dn, false);
}
}
}
// 35. For each parsed grammar phrase f in functionsToInitialize, do
for (let f of functionsToInitialize) {
// a. Let fn be the sole element of the BoundNames of f.
let fn = _singletons.Environment.BoundNames(realm, f)[0];
// b. Let fo be the result of performing InstantiateFunctionObject for f with argument lexEnv.
let fo = lexEnv.evaluate(f, strict);
(0, _invariant2.default)(fo instanceof _index.Value);
// c. Perform ! varEnvRec.SetMutableBinding(fn, fo, false).
varEnvRec.SetMutableBinding(fn, fo, false);
}
// 36. Return NormalCompletion(empty).
return realm.intrinsics.empty;
}
// ECMA262 9.2.11
SetFunctionName(realm, F, name, prefix) {
// 1. Assert: F is an extensible object that does not have a name own property.
(0, _invariant2.default)(F.getExtensible(), "expected object to be extensible and not have a name property");
// 2. Assert: Type(name) is either Symbol or String.
(0, _invariant2.default)(typeof name === "string" || name instanceof _index.StringValue || name instanceof _index.SymbolValue || name instanceof _index.AbstractValue, "expected name to be a string or symbol");
if (typeof name === "string") name = new _index.StringValue(realm, name);
// 3. Assert: If prefix was passed, then Type(prefix) is String.
(0, _invariant2.default)(prefix === undefined || typeof prefix === "string", "expected prefix to be a string if passed");
// 4. If Type(name) is Symbol, then
if (name instanceof _index.SymbolValue) {
// a. Let description be name's [[Description]] value.
let description = name.$Description;
// b. If description is undefined, let name be the empty String.
if (description === undefined) {
name = realm.intrinsics.emptyString;
} else {
// c. Else, let name be the concatenation of "[", description, and "]".
(0, _invariant2.default)(description instanceof _index.Value);
name = new _index.StringValue(realm, `[${description.throwIfNotConcreteString().value}]`);
}
}
// 5. If prefix was passed, then
if (prefix) {
// a. Let name be the concatenation of prefix, code unit 0x0020 (SPACE), and name.
if (name instanceof _index.AbstractValue) {
let prefixVal = new _index.StringValue(realm, prefix + " ");
name = _index.AbstractValue.createFromBinaryOp(realm, "+", prefixVal, name, name.expressionLocation);
} else {
name = new _index.StringValue(realm, `${prefix} ${name.value}`);
}
}
// 6. Return ! DefinePropertyOrThrow(F, "name", PropertyDescriptor{[[Value]]: name, [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true}).
return _singletons.Properties.DefinePropertyOrThrow(realm, F, "name", {
value: name,
enumerable: false,
writable: false,
configurable: true
});
}
// ECMA262 9.2.3
FunctionInitialize(realm, F, kind, ParameterList, Body, Scope) {
// Note that F is a new object, and we can thus write to internal slots
(0, _invariant2.default)(realm.isNewObject(F));
// 1. Assert: F is an extensible object that does not have a length own property.
(0, _invariant2.default)(F.getExtensible(), "expected to be extensible and no length property");
// 2. Let len be the ExpectedArgumentCount of ParameterList.
let len = 0;
for (let FormalParameter of ParameterList) {
if (FormalParameter.type === "AssignmentPattern") {
break;
}
len += 1;
}
// 3. Perform ! DefinePropertyOrThrow(F, "length", PropertyDescriptor{[[Value]]: len, [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true}).
_singletons.Properties.DefinePropertyOrThrow(realm, F, "length", {
value: new _index.NumberValue(realm, len),
writable: false,
enumerable: false,
configurable: true
});
// 4. Let Strict be the value of the [[Strict]] internal slot of F.
let Strict = F.$Strict;
if (!Strict) {
_singletons.Properties.DefinePropertyOrThrow(realm, F, "caller", {
value: new _index.UndefinedValue(realm),
writable: true,
enumerable: false,
configurable: true
});
}
// 5. Set the [[Environment]] internal slot of F to the value of Scope.
F.$Environment = Scope;
// 6. Set the [[FormalParameters]] internal slot of F to ParameterList.
F.$FormalParameters = ParameterList;
// 7. Set the [[ECMAScriptCode]] internal slot of F to Body.
Body.uniqueOrderedTag = realm.functionBodyUniqueTagSeed++;
F.$ECMAScriptCode = Body;
// 8. Set the [[ScriptOrModule]] internal slot of F to GetActiveScriptOrModule().
F.$ScriptOrModule = _singletons.Environment.GetActiveScriptOrModule(realm);
// 9. If kind is Arrow, set the [[realmMode]] internal slot of F to lexical.
if (kind === "arrow") {
F.$ThisMode = "lexical";
} else if (Strict === true) {
// 10. Else if Strict is true, set the [[realmMode]] internal slot of F to strict.
F.$ThisMode = "strict";
} else {
// 11. Else set the [[realmMode]] internal slot of F to global.
F.$ThisMode = "global";
}
// Return F.
return F;
}
// ECMA262 9.2.6
GeneratorFunctionCreate(realm, kind, ParameterList, Body, Scope, Strict) {
// 1. Let functionPrototype be the intrinsic object %Generator%.
let functionPrototype = realm.intrinsics.Generator;
// 2. Let F be FunctionAllocate(functionPrototype, Strict, "generator").
let F = this.FunctionAllocate(realm, functionPrototype, Strict, "generator");
// 3. Return FunctionInitialize(F, kind, ParameterList, Body, Scope).
return this.FunctionInitialize(realm, F, kind, ParameterList, Body, Scope);
}
// ECMA262 9.2.7
AddRestrictedFunctionProperties(F, realm) {
// 1. Assert: realm.[[Intrinsics]].[[%ThrowTypeError%]] exists and has been initialized.
// 2. Let thrower be realm.[[Intrinsics]].[[%ThrowTypeError%]].
let thrower = realm.intrinsics.ThrowTypeError;
(0, _invariant2.default)(thrower);
let desc = {
get: thrower,
set: thrower,
enumerable: false,
configurable: true
};
// 3. Perform ! DefinePropertyOrThrow(F, "caller", PropertyDescriptor {[[Get]]: thrower, [[Set]]: thrower, [[Enumerable]]: false, [[Configurable]]: true}).
_singletons.Properties.DefinePropertyOrThrow(realm, F, "caller", desc);
// 4. Return ! DefinePropertyOrThrow(F, "arguments", PropertyDescriptor {[[Get]]: thrower, [[Set]]: thrower, [[Enumerable]]: false, [[Configurable]]: true}).
return _singletons.Properties.DefinePropertyOrThrow(realm, F, "arguments", desc);
}
// ECMA262 9.2.1
$Call(realm, F, thisArgument, argsList) {
return InternalCall(realm, F, thisArgument, argsList, 0);
}
// ECMA262 9.2.2
$Construct(realm, F, argumentsList, newTarget) {
return InternalConstruct(realm, F, argumentsList, newTarget, undefined, 0);
}
// ECMA262 9.2.3
FunctionAllocate(realm, functionPrototype, strict, functionKind) {
// 1. Assert: Type(functionPrototype) is Object.
(0, _invariant2.default)(functionPrototype instanceof _index.ObjectValue, "expected functionPrototype to be an object");
// 2. Assert: functionKind is either "normal", "non-constructor" or "generator".
(0, _invariant2.default)(functionKind === "normal" || functionKind === "non-constructor" || functionKind === "generator", "invalid functionKind");
// 3. If functionKind is "normal", let needsConstruct be true.
let needsConstruct;
if (functionKind === "normal") {
needsConstruct = true;
} else {
// 4. Else, let needsConstruct be false.
needsConstruct = false;
}
// 5. If functionKind is "non-constructor", let functionKind be "normal".
if (functionKind === "non-constructor") {
functionKind = "normal";
}
// 6. Let F be a newly created ECMAScript function object with the internal slots listed in Table 27. All of those internal slots are initialized to undefined.
let F = new _index.ECMAScriptSourceFunctionValue(realm);
// 7. Set F's essential internal methods to the default ordinary object definitions specified in 9.1.
// 8. Set F's [[Call]] internal method to the definition specified in 9.2.1.
F.$Call = (thisArgument, argsList) => {
return this.$Call(realm, F, thisArgument, argsList);
};
// 9. If needsConstruct is true, then
if (needsConstruct === true) {
// a. Set F's [[Construct]] internal method to the definition specified in 9.2.2.
F.$Construct = (argumentsList, newTarget) => {
return this.$Construct(realm, F, argumentsList, newTarget);
};
// b. Set the [[ConstructorKind]] internal slot of F to "base".
F.$ConstructorKind = "base";
}
// 10. Set the [[Strict]] internal slot of F to strict.
F.$Strict = strict;
// 11. Set the [[FunctionKind]] internal slot of F to functionKind.
F.$FunctionKind = functionKind;
// 12. Set the [[Prototype]] internal slot of F to functionPrototype.
F.$Prototype = functionPrototype;
// 13. Set the [[Extensible]] internal slot of F to true.
F.setExtensible(true);
// 14. Set the [[Realm]] internal slot of F to the current Realm Record.
F.$Realm = realm;
// 15. Return F.
return F;
}
// ECMA262 9.4.1.3
BoundFunctionCreate(realm, targetFunction, boundThis, boundArgs) {
// 1. Assert: Type(targetFunction) is Object.
(0, _invariant2.default)(targetFunction instanceof _index.ObjectValue, "expected an object");
// 2. Let proto be ? targetFunction.[[GetPrototypeOf]]().
let proto = targetFunction.$GetPrototypeOf();
// 3. Let obj be a newly created object.
let obj = new _index.BoundFunctionValue(realm);
// 4. Set obj's essential internal methods to the default ordinary object definitions specified in 9.1.
// 5. Set the [[Call]] internal method of obj as described in 9.4.1.1.
obj.$Call = (thisArgument, argsList) => {
return $BoundCall(realm, obj, thisArgument, argsList);
};
// 6. If targetFunction has a [[Construct]] internal method, then
if (targetFunction.$Construct) {
// a. Set the [[Construct]] internal method of obj as described in 9.4.1.2.
obj.$Construct = (thisArgument, argsList) => {
return $BoundConstruct(realm, obj, thisArgument, argsList);
};
}
// 7. Set the [[Prototype]] internal slot of obj to proto.
obj.$Prototype = proto;
// 8. Set the [[Extensible]] internal slot of obj to true.
obj.setExtensible(true);
// 9. Set the [[BoundTargetFunction]] internal slot of obj to targetFunction.
obj.$BoundTargetFunction = targetFunction;
// 10. Set the [[BoundThis]] internal slot of obj to the value of boundThis.
obj.$BoundThis = boundThis;
// 11. Set the [[BoundArguments]] internal slot of obj to boundArgs.
obj.$BoundArguments = boundArgs;
// 12. Return obj.
return obj;
}
// ECMA262 18.2.1.1
PerformEval(realm, x, evalRealm, strictCaller, direct) {
// 1. Assert: If direct is false, then strictCaller is also false.
if (direct === false) (0, _invariant2.default)(strictCaller === false, "strictCaller is only allowed on direct eval");
// 2. If Type(x) is not String, return x.
if (!(x instanceof _index.StringValue)) return x;
// 3. Let script be the ECMAScript code that is the result of parsing x, interpreted as UTF-16 encoded Unicode text
// as described in 6.1.4, for the goal symbol Script. If the parse fails, throw a SyntaxError exception. If any
// early errors are detected, throw a SyntaxError or a ReferenceError exception, depending on the type of the
// error (but see also clause 16). Parsing and early error detection may be interweaved in an implementation
// dependent manner.
let ast = (0, _parse2.default)(realm, x.value, "eval", "script");
let script = ast.program;
// 4. If script Contains ScriptBody is false, return undefined.
if (!script.body) return realm.intrinsics.undefined;
// 5. Let body be the ScriptBody of script.
let body = t.blockStatement(script.body, script.directives);
// 6. If strictCaller is true, let strictEval be true.
let strictEval;
if (strictCaller) {
strictEval = true;
} else {
// 7. Else, let strictEval be IsStrict of script.
strictEval = (0, _strict2.default)(script);
}
// 8. Let ctx be the running execution context. If direct is true, ctx will be the execution context that
// performed the direct eval. If direct is false, ctx will be the execution context for the invocation of
// the eval function.
let ctx = realm.getRunningContext();
// 9. If direct is true, then
let lexEnv, varEnv;
if (direct) {
// a. Let lexEnv be NewDeclarativeEnvironment(ctx's LexicalEnvironment).
lexEnv = _singletons.Environment.NewDeclarativeEnvironment(realm, ctx.lexicalEnvironment);
// b. Let varEnv be ctx's VariableEnvironment.
varEnv = ctx.variableEnvironment;
} else {
// 10. Else,
// a. Let lexEnv be NewDeclarativeEnvironment(evalRealm.[[GlobalEnv]]).
lexEnv = _singletons.Environment.NewDeclarativeEnvironment(realm, evalRealm.$GlobalEnv);
// b. Let varEnv be evalRealm.[[GlobalEnv]].
varEnv = evalRealm.$GlobalEnv;
}
// 11. If strictEval is true, let varEnv be lexEnv.
if (strictEval) varEnv = lexEnv;
// 12. If ctx is not already suspended, suspend ctx.
ctx.suspend();
// 13. Let evalCxt be a new ECMAScript code execution context.
let evalCxt = new _realm.ExecutionContext();
evalCxt.isStrict = strictEval;
// 14. Set the evalCxt's Function to null.
evalCxt.setFunction(null);
// 15. Set the evalCxt's Realm to evalRealm.
evalCxt.setRealm(evalRealm);
// 16. Set the evalCxt's ScriptOrModule to ctx's ScriptOrModule.
evalCxt.ScriptOrModule = ctx.ScriptOrModule;
// 17. Set the evalCxt's VariableEnvironment to varEnv.
evalCxt.variableEnvironment = varEnv;
// 18. Set the evalCxt's LexicalEnvironment to lexEnv.
evalCxt.lexicalEnvironment = lexEnv;
// 19. Push evalCxt on to the execution context stack; evalCxt is now the running execution context.
realm.pushContext(evalCxt);
let result;
try {
// 20. Let result be EvalDeclarationInstantiation(body, varEnv, lexEnv, strictEval).
(0, _invariant2.default)(varEnv);
try {
result = this.EvalDeclarationInstantiation(realm, body, varEnv, lexEnv, strictEval);
} catch (e) {
if (e instanceof _completions.AbruptCompletion) {
result = e;
} else {
throw e;
}
}
(0, _invariant2.default)(result instanceof _index.Value || result instanceof _completions.AbruptCompletion);
// 21. If result.[[Type]] is normal, then
if (result instanceof _index.Value) {
// Evaluate expressions that passed for directives.
if (script.directives) {
for (let directive of script.directives) {
result = new _index.StringValue(realm, directive.value.value);
}
}
// a. Let result be the result of evaluating body.
result = this.EvaluateStatements(script.body, result, strictEval, lexEnv, realm);
}
// 22. If result.[[Type]] is normal and result.[[Value]] is empty, then
if (result instanceof _index.EmptyValue) {
// a. Let result be NormalCompletion(undefined).
result = realm.intrinsics.undefined;
}
} finally {
// 23. Suspend evalCxt and remove it from the execution context stack.
evalCxt.suspend();
realm.popContext(evalCxt);
realm.onDestroyScope(evalCxt.lexicalEnvironment);
}
// 24. Resume the context that is now on the top of the execution context stack as the running execution context.
(0, _invariant2.default)(realm.getRunningContext() === ctx);
ctx.resume();
// 25. Return Completion(result).
if (result instanceof _index.Value) {
return result;
} else {
(0, _invariant2.default)(result instanceof _completions.AbruptCompletion);
throw result;
}
}
// If c is an abrupt completion and realm.savedCompletion is defined, the result is an instance of
// JoinedAbruptCompletions and the effects that have been captured since the PossiblyNormalCompletion instance
// in realm.savedCompletion has been created, becomes the effects of the branch that terminates in c.
// If c is a normal completion, the result is realm.savedCompletion, with its value updated to c.
// If c is undefined, the result is just realm.savedCompletion.
// Call this only when a join point has been reached.
incorporateSavedCompletion(realm, c) {
let savedCompletion = realm.savedCompletion;
if (savedCompletion !== undefined) {
if (savedCompletion.savedPathConditions) {
// Since we are joining several control flow paths, we need the curent path conditions to reflect
// only the refinements that applied at the corresponding fork point.
realm.pathConditions = savedCompletion.savedPathConditions;
savedCompletion.savedPathConditions = [];
}
realm.savedCompletion = undefined;
if (c === undefined) return savedCompletion;
if (c instanceof _index.Value) {
_singletons.Join.updatePossiblyNormalCompletionWithValue(realm, savedCompletion, c);
return savedCompletion;
} else {
let e = realm.getCapturedEffects(savedCompletion);
(0, _invariant2.default)(e !== undefined);
realm.stopEffectCaptureAndUndoEffects(savedCompletion);
let joined_effects = _singletons.Join.joinPossiblyNormalCompletionWithAbruptCompletion(realm, savedCompletion, c, e);
realm.applyEffects(joined_effects);
let jc = joined_effects[0];
(0, _invariant2.default)(jc instanceof _completions.AbruptCompletion);
return jc;
}
}
return c;
}
EvaluateStatements(body, initialBlockValue, strictCode, blockEnv, realm) {
let blockValue = initialBlockValue;
for (let node of body) {
if (node.type !== "FunctionDeclaration") {
let res = blockEnv.evaluateCompletionDeref(node, strictCode);
if (!(res instanceof _index.EmptyValue)) {
if (res instanceof _completions.AbruptCompletion) throw (0, _index2.UpdateEmpty)(realm, res, blockValue || realm.intrinsics.empty);
(0, _invariant2.default)(res instanceof _index.Value);
blockValue = res;
}
}
}
// 7. Return blockValue.
return blockValue || realm.intrinsics.empty;
}
PartiallyEvaluateStatements(body, blockValue, strictCode, blockEnv, realm) {
let statementAsts = [];
for (let node of body) {
if (node.type !== "FunctionDeclaration") {
let [res, nast, nio] = blockEnv.partiallyEvaluateCompletionDeref(node, strictCode);
for (let ioAst of nio) statementAsts.push(ioAst);
statementAsts.push(nast);
if (!(res instanceof _index.EmptyValue)) {
if (blockValue === undefined || blockValue instanceof _index.Value) {
if (res instanceof _completions.AbruptCompletion) return [(0, _index2.UpdateEmpty)(realm, res, blockValue || realm.intrinsics.empty), statementAsts];
(0, _invariant2.default)(res instanceof _completions.NormalCompletion || res instanceof _index.Value);
blockValue = res;
}
}
}
}
// 7. Return blockValue.
return [blockValue || realm.intrinsics.empty, statementAsts];
}
// ECMA262 9.2.5
FunctionCreate(realm, kind, ParameterList, Body, Scope, Strict, prototype) {
// 1. If the prototype argument was not passed, then
if (!prototype) {
// a. Let prototype be the intrinsic object %FunctionPrototype%.
prototype = realm.intrinsics.FunctionPrototype;
}
// 2. If kind is not Normal, let allocKind be "non-constructor".
let allocKind;
if (kind !== "normal") {
allocKind = "non-constructor";
} else {
// 3. Else, let allocKind be "normal".
allocKind = "normal";
}
// 4. Let F be FunctionAllocate(prototype, Strict, allocKind).
let F = this.FunctionAllocate(realm, prototype, Strict, allocKind);
// ECMAScript 2016, section 17:
// "Every other data property described in clauses 18 through 26 and in Annex B.2 has the attributes { [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: true } unless otherwise specified."
// Because we call `AddRestrictedFunctionProperties` on `FunctionPrototype`, accessing property "arguments" will raise a `TypeError` by default.
// However, in non-strict mode this behavior is not desired, so we will add them as own properties of each `FunctionValue`, in accordance with ECMA 17.
// Note: "arguments" ***MUST NOT*** be set if the function is in strict mode or is an arrow, method, constructor, or generator function.
// See 16.2 "Forbidden Extensions"
if (!Strict && kind === "normal") {
_singletons.Properties.DefinePropertyOrThrow(realm, F, "arguments", {
value: realm.intrinsics.undefined,
enumerable: false,
writable: true,
configurable: true
});
}
// 5. Return FunctionInitialize(F, kind, ParameterList, Body, Scope).
return this.FunctionInitialize(realm, F, kind, ParameterList, Body, Scope);
}
// ECMA262 18.2.1.2
EvalDeclarationInstantiation(realm, body, varEnv, lexEnv, strict) {
// 1. Let varNames be the VarDeclaredNames of body.
let varNames = [];
(0, _traverseFast2.default)(body, node => {
if (node.type === "VariableDeclaration" && node.kind === "var") {
varNames = varNames.concat(Object.keys(t.getBindingIdentifiers(node)));
}
if (node.type === "FunctionExpression" || node.type === "FunctionDeclaration") {
return true;
}
return false;
});
// 2. Let varDeclarations be the VarScopedDeclarations of body.
let varDeclarations = this.FindVarScopedDeclarations(body);
// 3. Let lexEnvRec be lexEnv's EnvironmentRecord.
let lexEnvRec = lexEnv.environmentRecord;
// 4. Let varEnvRec be varEnv's EnvironmentRecord.
let varEnvRec = varEnv.environmentRecord;
// 5. If strict is false, then
if (!strict) {
// a. If varEnvRec is a global Environment Record, then
if (varEnvRec instanceof _environment.GlobalEnvironmentRecord) {
// i. For each name in varNames, do
for (let name of varNames) {
// 1. If varEnvRec.HasLexicalDeclaration(name) is true, throw a SyntaxError exception.
if (varEnvRec.HasLexicalDeclaration(name)) {
throw realm.createErrorThrowCompletion(realm.intrinsics.SyntaxError, new _index.StringValue(realm, name + " global object is restricted"));
}
// 2. NOTE: eval will not create a global var declaration that would be shadowed by a global lexical declaration.
}
}
// b. Let thisLex be lexEnv.
let thisLex = lexEnv;
// c. Assert: The following loop will terminate.
// d. Repeat while thisLex is not the same as varEnv,
while (thisLex !== varEnv) {
// i. Let thisEnvRec be thisLex's EnvironmentRecord.
let thisEnvRec = thisLex.environmentRecord;
// ii. If thisEnvRec is not an object Environment Record, then
if (!(thisEnvRec instanceof _environment.ObjectEnvironmentRecord)) {
// 1. NOTE: The environment of with statements cannot contain any lexical declaration so it d