1363 lines
54 KiB
JavaScript
1363 lines
54 KiB
JavaScript
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", {
|
|
value: true
|
|
});
|
|
exports.Realm = exports.ExecutionContext = exports.Tracer = undefined;
|
|
|
|
var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }();
|
|
|
|
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); /**
|
|
* 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.
|
|
*/
|
|
|
|
exports.construct_empty_effects = construct_empty_effects;
|
|
|
|
var _errors = require("./errors.js");
|
|
|
|
var _index = require("./values/index.js");
|
|
|
|
var _environment = require("./environment.js");
|
|
|
|
var _index2 = require("./methods/index.js");
|
|
|
|
var _completions = require("./completions.js");
|
|
|
|
var _invariant = require("./invariant.js");
|
|
|
|
var _invariant2 = _interopRequireDefault(_invariant);
|
|
|
|
var _seedrandom = require("seedrandom");
|
|
|
|
var _seedrandom2 = _interopRequireDefault(_seedrandom);
|
|
|
|
var _generator = require("./utils/generator.js");
|
|
|
|
var _internalizer = require("./utils/internalizer.js");
|
|
|
|
var _singletons = require("./singletons.js");
|
|
|
|
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 _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
|
|
|
|
var Tracer = exports.Tracer = function () {
|
|
function Tracer() {
|
|
_classCallCheck(this, Tracer);
|
|
}
|
|
|
|
_createClass(Tracer, [{
|
|
key: "beginEvaluateForEffects",
|
|
value: function beginEvaluateForEffects(state) {}
|
|
}, {
|
|
key: "endEvaluateForEffects",
|
|
value: function endEvaluateForEffects(state, effects) {}
|
|
}, {
|
|
key: "detourCall",
|
|
value: function detourCall(F, thisArgument, argumentsList, newTarget, performCall) {}
|
|
}, {
|
|
key: "beforeCall",
|
|
value: function beforeCall(F, thisArgument, argumentsList, newTarget) {}
|
|
}, {
|
|
key: "afterCall",
|
|
value: function afterCall(F, thisArgument, argumentsList, newTarget, result) {}
|
|
}]);
|
|
|
|
return Tracer;
|
|
}();
|
|
|
|
var ExecutionContext = exports.ExecutionContext = function () {
|
|
function ExecutionContext() {
|
|
_classCallCheck(this, ExecutionContext);
|
|
}
|
|
|
|
_createClass(ExecutionContext, [{
|
|
key: "setCaller",
|
|
value: function setCaller(context) {
|
|
this.caller = context;
|
|
}
|
|
}, {
|
|
key: "setFunction",
|
|
value: function setFunction(F) {
|
|
if (F instanceof _index.ECMAScriptSourceFunctionValue) this.isStrict = F.$Strict;
|
|
this.function = F;
|
|
}
|
|
}, {
|
|
key: "setLocation",
|
|
value: function setLocation(loc) {
|
|
if (!loc) return;
|
|
this.loc = loc;
|
|
}
|
|
}, {
|
|
key: "setRealm",
|
|
value: function setRealm(realm) {
|
|
this.realm = realm;
|
|
}
|
|
|
|
/*
|
|
Read-only envs disallow:
|
|
- creating bindings in their scope
|
|
- creating or modifying objects when they are current running context
|
|
*/
|
|
|
|
}, {
|
|
key: "setReadOnly",
|
|
value: function setReadOnly(value) {
|
|
var oldReadOnly = this.isReadOnly;
|
|
if (this.variableEnvironment) this.variableEnvironment.environmentRecord.isReadOnly = value;
|
|
if (this.lexicalEnvironment) this.lexicalEnvironment.environmentRecord.isReadOnly = value;
|
|
this.isReadOnly = value;
|
|
return oldReadOnly;
|
|
}
|
|
}, {
|
|
key: "suspend",
|
|
value: function suspend() {
|
|
// TODO #712: suspend
|
|
}
|
|
}, {
|
|
key: "resume",
|
|
value: function resume() {
|
|
// TODO #712: resume
|
|
return this.realm.intrinsics.undefined;
|
|
}
|
|
}]);
|
|
|
|
return ExecutionContext;
|
|
}();
|
|
|
|
function construct_empty_effects(realm) {
|
|
return [realm.intrinsics.empty, new _generator.Generator(realm), new Map(), new Map(), new Set()];
|
|
}
|
|
|
|
var Realm = exports.Realm = function () {
|
|
function Realm(opts) {
|
|
_classCallCheck(this, Realm);
|
|
|
|
this.contextStack = [];
|
|
this.MOBILE_JSC_VERSION = "jsc-600-1-4-17";
|
|
this.objectCount = 0;
|
|
this.symbolCount = 867501803871088;
|
|
this.functionBodyUniqueTagSeed = 1;
|
|
this.nextGeneratorId = 0;
|
|
|
|
this.isReadOnly = false;
|
|
this.useAbstractInterpretation = !!opts.serialize || !!opts.residual;
|
|
this.trackLeaks = !!opts.abstractEffectsInAdditionalFunctions;
|
|
if (opts.mathRandomSeed !== undefined) {
|
|
this.mathRandomGenerator = (0, _seedrandom2.default)(opts.mathRandomSeed);
|
|
}
|
|
this.strictlyMonotonicDateNow = !!opts.strictlyMonotonicDateNow;
|
|
|
|
this.timeout = opts.timeout;
|
|
if (this.timeout) {
|
|
// We'll call Date.now for every this.timeoutCounterThreshold'th AST node.
|
|
// The threshold is there to reduce the cost of the surprisingly expensive Date.now call.
|
|
this.timeoutCounter = this.timeoutCounterThreshold = 1024;
|
|
}
|
|
|
|
this.start = Date.now();
|
|
this.compatibility = opts.compatibility || "browser";
|
|
this.maxStackDepth = opts.maxStackDepth || 225;
|
|
this.omitInvariants = !!opts.omitInvariants;
|
|
|
|
this.$TemplateMap = [];
|
|
|
|
if (this.useAbstractInterpretation) {
|
|
this.preludeGenerator = new _generator.PreludeGenerator(opts.debugNames, opts.uniqueSuffix);
|
|
this.pathConditions = [];
|
|
_index.ObjectValue.setupTrackedPropertyAccessors(_index.ObjectValue.trackedPropertyNames);
|
|
_index.ObjectValue.setupTrackedPropertyAccessors(_index.NativeFunctionValue.trackedPropertyNames);
|
|
_index.ObjectValue.setupTrackedPropertyAccessors(_index.ProxyValue.trackedPropertyNames);
|
|
}
|
|
|
|
this.tracers = [];
|
|
|
|
// These get initialized in construct_realm to avoid the dependency
|
|
this.intrinsics = {};
|
|
this.$GlobalObject = {};
|
|
this.evaluators = Object.create(null);
|
|
this.partialEvaluators = Object.create(null);
|
|
this.$GlobalEnv = undefined;
|
|
|
|
this.react = {
|
|
enabled: opts.reactEnabled || false,
|
|
output: opts.reactOutput || "create-element",
|
|
flowRequired: true,
|
|
symbols: new Map(),
|
|
currentOwner: undefined,
|
|
reactLibraryObject: undefined,
|
|
hoistableReactElements: new WeakMap(),
|
|
hoistableFunctions: new WeakMap()
|
|
};
|
|
|
|
this.errorHandler = opts.errorHandler;
|
|
|
|
this.globalSymbolRegistry = [];
|
|
this.activeLexicalEnvironments = new Set();
|
|
this._abstractValuesDefined = new Set(); // A set of nameStrings to ensure abstract values have unique names
|
|
this.debugNames = opts.debugNames;
|
|
}
|
|
|
|
// A list of abstract conditions that are known to be true in the current execution path.
|
|
// For example, the abstract condition of an if statement is known to be true inside its true branch.
|
|
|
|
// Unique tag for identifying function body ast node. It is neeeded
|
|
// instead of ast node itself because we may perform ast tree deep clone
|
|
// during serialization which changes the ast identity.
|
|
|
|
|
|
_createClass(Realm, [{
|
|
key: "isCompatibleWith",
|
|
|
|
|
|
// to force flow to type the annotations
|
|
value: function isCompatibleWith(compatibility) {
|
|
return compatibility === this.compatibility;
|
|
}
|
|
|
|
// Checks if there is a let binding at global scope with the given name
|
|
// returning it if so
|
|
|
|
}, {
|
|
key: "getGlobalLetBinding",
|
|
value: function getGlobalLetBinding(key) {
|
|
var globrec = this.$GlobalEnv.environmentRecord;
|
|
// GlobalEnv should have a GlobalEnvironmentRecord
|
|
(0, _invariant2.default)(globrec instanceof _environment.GlobalEnvironmentRecord);
|
|
var dclrec = globrec.$DeclarativeRecord;
|
|
|
|
try {
|
|
return dclrec.HasBinding(key) ? dclrec.GetBindingValue(key, false) : undefined;
|
|
} catch (e) {
|
|
if (e instanceof _errors.FatalError) return undefined;
|
|
throw e;
|
|
}
|
|
}
|
|
|
|
/*
|
|
Read only realms disallow:
|
|
- using console.log
|
|
- creating bindings in any existing scopes
|
|
- modifying object properties in any existing scopes
|
|
Setting a realm read-only sets all contained environments to read-only, but
|
|
all new environments (e.g. new ExecutionContexts) will be writeable.
|
|
*/
|
|
|
|
}, {
|
|
key: "setReadOnly",
|
|
value: function setReadOnly(readOnlyValue) {
|
|
this.isReadOnly = readOnlyValue;
|
|
this.$GlobalEnv.environmentRecord.isReadOnly = readOnlyValue;
|
|
this.contextStack.forEach(function (ctx) {
|
|
ctx.setReadOnly(readOnlyValue);
|
|
});
|
|
}
|
|
}, {
|
|
key: "testTimeout",
|
|
value: function testTimeout() {
|
|
var timeout = this.timeout;
|
|
if (timeout && ! --this.timeoutCounter) {
|
|
this.timeoutCounter = this.timeoutCounterThreshold;
|
|
var total = Date.now() - this.start;
|
|
if (total > timeout) {
|
|
throw new _errors.FatalError("Timed out");
|
|
}
|
|
}
|
|
}
|
|
}, {
|
|
key: "hasRunningContext",
|
|
value: function hasRunningContext() {
|
|
return this.contextStack.length !== 0;
|
|
}
|
|
}, {
|
|
key: "getRunningContext",
|
|
value: function getRunningContext() {
|
|
var context = this.contextStack[this.contextStack.length - 1];
|
|
(0, _invariant2.default)(context, "There's no running execution context");
|
|
return context;
|
|
}
|
|
|
|
// Call when a scope falls out of scope and should be destroyed.
|
|
// Clears the Bindings corresponding to the disappearing Scope from ModifiedBindings
|
|
|
|
}, {
|
|
key: "onDestroyScope",
|
|
value: function onDestroyScope(lexicalEnvironment) {
|
|
(0, _invariant2.default)(this.activeLexicalEnvironments.has(lexicalEnvironment));
|
|
var modifiedBindings = this.modifiedBindings;
|
|
if (modifiedBindings) {
|
|
// Don't undo things to global scope because it's needed past its destruction point (for serialization)
|
|
var environmentRecord = lexicalEnvironment.environmentRecord;
|
|
if (environmentRecord instanceof _environment.DeclarativeEnvironmentRecord) {
|
|
var _iteratorNormalCompletion = true;
|
|
var _didIteratorError = false;
|
|
var _iteratorError = undefined;
|
|
|
|
try {
|
|
for (var _iterator = modifiedBindings.keys()[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
|
|
var b = _step.value;
|
|
|
|
if (environmentRecord.bindings[b.name] && environmentRecord.bindings[b.name] === b) modifiedBindings.delete(b);
|
|
}
|
|
} catch (err) {
|
|
_didIteratorError = true;
|
|
_iteratorError = err;
|
|
} finally {
|
|
try {
|
|
if (!_iteratorNormalCompletion && _iterator.return) {
|
|
_iterator.return();
|
|
}
|
|
} finally {
|
|
if (_didIteratorError) {
|
|
throw _iteratorError;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Ensures if we call onDestroyScope too early, there will be a failure.
|
|
this.activeLexicalEnvironments.delete(lexicalEnvironment);
|
|
lexicalEnvironment.destroy();
|
|
}
|
|
}, {
|
|
key: "pushContext",
|
|
value: function pushContext(context) {
|
|
if (this.contextStack.length >= this.maxStackDepth) {
|
|
throw new _errors.FatalError("Maximum stack depth exceeded");
|
|
}
|
|
this.contextStack.push(context);
|
|
}
|
|
}, {
|
|
key: "popContext",
|
|
value: function popContext(context) {
|
|
if (context.function !== undefined) {
|
|
var modifiedBindings = this.modifiedBindings;
|
|
if (modifiedBindings !== undefined) {
|
|
var _iteratorNormalCompletion2 = true;
|
|
var _didIteratorError2 = false;
|
|
var _iteratorError2 = undefined;
|
|
|
|
try {
|
|
for (var _iterator2 = modifiedBindings.keys()[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
|
|
var b = _step2.value;
|
|
|
|
if (b.environment.$FunctionObject === context.function) modifiedBindings.delete(b);
|
|
}
|
|
} catch (err) {
|
|
_didIteratorError2 = true;
|
|
_iteratorError2 = err;
|
|
} finally {
|
|
try {
|
|
if (!_iteratorNormalCompletion2 && _iterator2.return) {
|
|
_iterator2.return();
|
|
}
|
|
} finally {
|
|
if (_didIteratorError2) {
|
|
throw _iteratorError2;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
var c = this.contextStack.pop();
|
|
(0, _invariant2.default)(c === context);
|
|
}
|
|
}, {
|
|
key: "wrapInGlobalEnv",
|
|
value: function wrapInGlobalEnv(callback) {
|
|
var context = new ExecutionContext();
|
|
context.isStrict = this.isStrict;
|
|
context.lexicalEnvironment = this.$GlobalEnv;
|
|
context.variableEnvironment = this.$GlobalEnv;
|
|
context.realm = this;
|
|
|
|
this.pushContext(context);
|
|
try {
|
|
return callback();
|
|
} finally {
|
|
this.popContext(context);
|
|
}
|
|
}
|
|
}, {
|
|
key: "assignToGlobal",
|
|
value: function assignToGlobal(name, value) {
|
|
var _this = this;
|
|
|
|
this.wrapInGlobalEnv(function () {
|
|
return _this.$GlobalEnv.assignToGlobal(name, value);
|
|
});
|
|
}
|
|
}, {
|
|
key: "deleteGlobalBinding",
|
|
value: function deleteGlobalBinding(name) {
|
|
this.$GlobalEnv.environmentRecord.DeleteBinding(name);
|
|
}
|
|
|
|
// Evaluate a context as if it won't have any side-effects outside of any objects
|
|
// that it created itself. This promises that any abstract functions inside of it
|
|
// also won't have effects on any objects or bindings that weren't created in this
|
|
// call.
|
|
|
|
}, {
|
|
key: "evaluatePure",
|
|
value: function evaluatePure(f) {
|
|
if (!this.trackLeaks) {
|
|
return f();
|
|
}
|
|
var saved_createdObjectsTrackedForLeaks = this.createdObjectsTrackedForLeaks;
|
|
// Track all objects (including function closures) created during
|
|
// this call. This will be used to make the assumption that every
|
|
// *other* object is unchanged (pure). These objects are marked
|
|
// as leaked if they're passed to abstract functions.
|
|
this.createdObjectsTrackedForLeaks = new Set();
|
|
try {
|
|
return f();
|
|
} finally {
|
|
this.createdObjectsTrackedForLeaks = saved_createdObjectsTrackedForLeaks;
|
|
}
|
|
}
|
|
|
|
// Evaluate the given ast in a sandbox and return the evaluation results
|
|
// in the form of a completion, a code generator, a map of changed variable
|
|
// bindings and a map of changed property bindings.
|
|
|
|
}, {
|
|
key: "evaluateNodeForEffects",
|
|
value: function evaluateNodeForEffects(ast, strictCode, env, state, generatorName) {
|
|
return this.evaluateForEffects(function () {
|
|
return env.evaluateCompletionDeref(ast, strictCode);
|
|
}, state, generatorName);
|
|
}
|
|
}, {
|
|
key: "evaluateAndRevertInGlobalEnv",
|
|
value: function evaluateAndRevertInGlobalEnv(func) {
|
|
var _this2 = this;
|
|
|
|
this.wrapInGlobalEnv(function () {
|
|
return _this2.evaluateForEffects(func);
|
|
});
|
|
}
|
|
}, {
|
|
key: "evaluateNodeForEffectsInGlobalEnv",
|
|
value: function evaluateNodeForEffectsInGlobalEnv(node, state, generatorName) {
|
|
var _this3 = this;
|
|
|
|
return this.wrapInGlobalEnv(function () {
|
|
return _this3.evaluateNodeForEffects(node, false, _this3.$GlobalEnv, state, generatorName);
|
|
});
|
|
}
|
|
}, {
|
|
key: "partiallyEvaluateNodeForEffects",
|
|
value: function partiallyEvaluateNodeForEffects(ast, strictCode, env) {
|
|
var nodeAst = void 0,
|
|
nodeIO = void 0;
|
|
function partialEval() {
|
|
var result = void 0;
|
|
|
|
var _env$partiallyEvaluat = env.partiallyEvaluateCompletionDeref(ast, strictCode);
|
|
|
|
var _env$partiallyEvaluat2 = _slicedToArray(_env$partiallyEvaluat, 3);
|
|
|
|
result = _env$partiallyEvaluat2[0];
|
|
nodeAst = _env$partiallyEvaluat2[1];
|
|
nodeIO = _env$partiallyEvaluat2[2];
|
|
|
|
return result;
|
|
}
|
|
var effects = this.evaluateForEffects(partialEval);
|
|
(0, _invariant2.default)(nodeAst !== undefined && nodeIO !== undefined);
|
|
return [effects, nodeAst, nodeIO];
|
|
}
|
|
}, {
|
|
key: "evaluateForEffects",
|
|
value: function evaluateForEffects(f, state, generatorName) {
|
|
// Save old state and set up empty state for ast
|
|
var _getAndResetModifiedM = this.getAndResetModifiedMaps(),
|
|
_getAndResetModifiedM2 = _slicedToArray(_getAndResetModifiedM, 2),
|
|
savedBindings = _getAndResetModifiedM2[0],
|
|
savedProperties = _getAndResetModifiedM2[1];
|
|
|
|
var saved_generator = this.generator;
|
|
var saved_createdObjects = this.createdObjects;
|
|
var saved_completion = this.savedCompletion;
|
|
this.generator = new _generator.Generator(this, generatorName);
|
|
this.createdObjects = new Set();
|
|
this.savedCompletion = undefined; // while in this call, we only explore the normal path.
|
|
|
|
var result = void 0;
|
|
try {
|
|
var _iteratorNormalCompletion3 = true;
|
|
var _didIteratorError3 = false;
|
|
var _iteratorError3 = undefined;
|
|
|
|
try {
|
|
for (var _iterator3 = this.tracers[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) {
|
|
var t1 = _step3.value;
|
|
t1.beginEvaluateForEffects(state);
|
|
}
|
|
} catch (err) {
|
|
_didIteratorError3 = true;
|
|
_iteratorError3 = err;
|
|
} finally {
|
|
try {
|
|
if (!_iteratorNormalCompletion3 && _iterator3.return) {
|
|
_iterator3.return();
|
|
}
|
|
} finally {
|
|
if (_didIteratorError3) {
|
|
throw _iteratorError3;
|
|
}
|
|
}
|
|
}
|
|
|
|
var c = void 0;
|
|
try {
|
|
try {
|
|
c = f();
|
|
if (c instanceof _environment.Reference) c = _singletons.Environment.GetValue(this, c);
|
|
} catch (e) {
|
|
if (e instanceof _completions.AbruptCompletion) c = e;else throw e;
|
|
}
|
|
// This is a join point for the normal branch of a PossiblyNormalCompletion.
|
|
if (c instanceof _index.Value || c instanceof _completions.AbruptCompletion) c = _singletons.Functions.incorporateSavedCompletion(this, c);
|
|
(0, _invariant2.default)(c !== undefined);
|
|
if (c instanceof _completions.PossiblyNormalCompletion) {
|
|
// The current state may have advanced since the time control forked into the various paths recorded in c.
|
|
// Update the normal path and restore the global state to what it was at the time of the fork.
|
|
var subsequentEffects = this.getCapturedEffects(c, c.value);
|
|
(0, _invariant2.default)(subsequentEffects !== undefined);
|
|
this.stopEffectCaptureAndUndoEffects(c);
|
|
_singletons.Join.updatePossiblyNormalCompletionWithSubsequentEffects(this, c, subsequentEffects);
|
|
this.savedCompletion = undefined;
|
|
}
|
|
|
|
(0, _invariant2.default)(this.generator !== undefined);
|
|
(0, _invariant2.default)(this.modifiedBindings !== undefined);
|
|
(0, _invariant2.default)(this.modifiedProperties !== undefined);
|
|
(0, _invariant2.default)(this.createdObjects !== undefined);
|
|
var astGenerator = this.generator;
|
|
var astBindings = this.modifiedBindings;
|
|
var astProperties = this.modifiedProperties;
|
|
var astCreatedObjects = this.createdObjects;
|
|
|
|
// Return the captured state changes and evaluation result
|
|
result = [c, astGenerator, astBindings, astProperties, astCreatedObjects];
|
|
return result;
|
|
} finally {
|
|
// Roll back the state changes
|
|
if (this.savedCompletion !== undefined) this.stopEffectCaptureAndUndoEffects(this.savedCompletion);
|
|
if (result !== undefined) {
|
|
this.restoreBindings(result[2]);
|
|
this.restoreProperties(result[3]);
|
|
} else {
|
|
this.restoreBindings(this.modifiedBindings);
|
|
this.restoreProperties(this.modifiedProperties);
|
|
}
|
|
this.generator = saved_generator;
|
|
this.modifiedBindings = savedBindings;
|
|
this.modifiedProperties = savedProperties;
|
|
this.createdObjects = saved_createdObjects;
|
|
this.savedCompletion = saved_completion;
|
|
}
|
|
} finally {
|
|
var _iteratorNormalCompletion4 = true;
|
|
var _didIteratorError4 = false;
|
|
var _iteratorError4 = undefined;
|
|
|
|
try {
|
|
for (var _iterator4 = this.tracers[Symbol.iterator](), _step4; !(_iteratorNormalCompletion4 = (_step4 = _iterator4.next()).done); _iteratorNormalCompletion4 = true) {
|
|
var t2 = _step4.value;
|
|
t2.endEvaluateForEffects(state, result);
|
|
}
|
|
} catch (err) {
|
|
_didIteratorError4 = true;
|
|
_iteratorError4 = err;
|
|
} finally {
|
|
try {
|
|
if (!_iteratorNormalCompletion4 && _iterator4.return) {
|
|
_iterator4.return();
|
|
}
|
|
} finally {
|
|
if (_didIteratorError4) {
|
|
throw _iteratorError4;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}, {
|
|
key: "evaluateWithUndoForDiagnostic",
|
|
value: function evaluateWithUndoForDiagnostic(f) {
|
|
if (!this.useAbstractInterpretation) return f();
|
|
var savedHandler = this.errorHandler;
|
|
var diagnostic = void 0;
|
|
try {
|
|
this.errorHandler = function (d) {
|
|
diagnostic = d;
|
|
return "Fail";
|
|
};
|
|
var effects = this.evaluateForEffects(f);
|
|
this.applyEffects(effects);
|
|
var resultVal = effects[0];
|
|
if (resultVal instanceof _completions.AbruptCompletion) throw resultVal;
|
|
if (resultVal instanceof _completions.PossiblyNormalCompletion) {
|
|
// in this case one of the branches may complete abruptly, which means that
|
|
// not all control flow branches join into one flow at this point.
|
|
// Consequently we have to continue tracking changes until the point where
|
|
// all the branches come together into one.
|
|
resultVal = this.composeWithSavedCompletion(resultVal);
|
|
}
|
|
(0, _invariant2.default)(resultVal instanceof _index.Value);
|
|
return resultVal;
|
|
} catch (e) {
|
|
if (diagnostic !== undefined) return diagnostic;
|
|
throw e;
|
|
} finally {
|
|
this.errorHandler = savedHandler;
|
|
}
|
|
}
|
|
}, {
|
|
key: "evaluateForFixpointEffects",
|
|
value: function evaluateForFixpointEffects(loopContinueTest, loopBody) {
|
|
try {
|
|
var effects1 = this.evaluateForEffects(loopBody);
|
|
while (true) {
|
|
this.restoreBindings(effects1[2]);
|
|
this.restoreProperties(effects1[3]);
|
|
var effects2 = this.evaluateForEffects(function () {
|
|
var test = loopContinueTest();
|
|
if (!(test instanceof _index.AbstractValue)) throw new _errors.FatalError("loop terminates before fixed point");
|
|
return loopBody();
|
|
});
|
|
this.restoreBindings(effects1[2]);
|
|
this.restoreProperties(effects1[3]);
|
|
if (_singletons.Widen.containsEffects(effects1, effects2)) {
|
|
// effects1 includes every value present in effects2, so doing another iteration using effects2 will not
|
|
// result in any more values being added to abstract domains and hence a fixpoint has been reached.
|
|
var _effects = _slicedToArray(effects2, 4),
|
|
gen = _effects[1],
|
|
bindings2 = _effects[2],
|
|
pbindings2 = _effects[3];
|
|
|
|
this._emitPropertAssignments(gen, pbindings2);
|
|
this._emitLocalAssignments(gen, bindings2);
|
|
return [effects1, effects2];
|
|
}
|
|
effects1 = _singletons.Widen.widenEffects(this, effects1, effects2);
|
|
}
|
|
} catch (e) {
|
|
return undefined;
|
|
}
|
|
}
|
|
|
|
// populate the loop body generator with assignments that will update the phiNodes
|
|
|
|
}, {
|
|
key: "_emitLocalAssignments",
|
|
value: function _emitLocalAssignments(gen, bindings) {
|
|
var tvalFor = new Map();
|
|
bindings.forEach(function (binding, key, map) {
|
|
var val = binding.value;
|
|
if (val instanceof _index.AbstractValue) {
|
|
(0, _invariant2.default)(val._buildNode !== undefined);
|
|
var tval = gen.derive(val.types, val.values, [val], function (_ref) {
|
|
var _ref2 = _slicedToArray(_ref, 1),
|
|
n = _ref2[0];
|
|
|
|
return n;
|
|
}, {
|
|
skipInvariant: true
|
|
});
|
|
tvalFor.set(key, tval);
|
|
}
|
|
});
|
|
bindings.forEach(function (binding, key, map) {
|
|
var val = binding.value;
|
|
if (val instanceof _index.AbstractValue) {
|
|
var phiNode = key.phiNode;
|
|
var tval = tvalFor.get(key);
|
|
(0, _invariant2.default)(tval !== undefined);
|
|
gen.emitStatement([tval], function (_ref3) {
|
|
var _ref4 = _slicedToArray(_ref3, 1),
|
|
v = _ref4[0];
|
|
|
|
(0, _invariant2.default)(phiNode !== undefined);
|
|
var id = phiNode.buildNode([]);
|
|
return t.expressionStatement(t.assignmentExpression("=", id, v));
|
|
});
|
|
}
|
|
});
|
|
}
|
|
|
|
// populate the loop body generator with assignments that will update properties modified inside the loop
|
|
|
|
}, {
|
|
key: "_emitPropertAssignments",
|
|
value: function _emitPropertAssignments(gen, pbindings) {
|
|
var _this4 = this;
|
|
|
|
function isSelfReferential(value, pathNode) {
|
|
if (value === pathNode) return true;
|
|
if (value instanceof _index.AbstractValue && pathNode !== undefined) {
|
|
var _iteratorNormalCompletion5 = true;
|
|
var _didIteratorError5 = false;
|
|
var _iteratorError5 = undefined;
|
|
|
|
try {
|
|
for (var _iterator5 = value.args[Symbol.iterator](), _step5; !(_iteratorNormalCompletion5 = (_step5 = _iterator5.next()).done); _iteratorNormalCompletion5 = true) {
|
|
var v = _step5.value;
|
|
|
|
if (isSelfReferential(v, pathNode)) return true;
|
|
}
|
|
} catch (err) {
|
|
_didIteratorError5 = true;
|
|
_iteratorError5 = err;
|
|
} finally {
|
|
try {
|
|
if (!_iteratorNormalCompletion5 && _iterator5.return) {
|
|
_iterator5.return();
|
|
}
|
|
} finally {
|
|
if (_didIteratorError5) {
|
|
throw _iteratorError5;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
var tvalFor = new Map();
|
|
pbindings.forEach(function (val, key, map) {
|
|
var value = val && val.value;
|
|
if (value instanceof _index.AbstractValue) {
|
|
(0, _invariant2.default)(value._buildNode !== undefined);
|
|
var tval = gen.derive(value.types, value.values, [key.object, value], function (_ref5) {
|
|
var _ref6 = _slicedToArray(_ref5, 2),
|
|
o = _ref6[0],
|
|
n = _ref6[1];
|
|
|
|
(0, _invariant2.default)(value instanceof _index.Value);
|
|
if (typeof key.key === "string" && value.mightHaveBeenDeleted() && isSelfReferential(value, key.pathNode)) {
|
|
var inTest = t.binaryExpression("in", t.stringLiteral(key.key), o);
|
|
var addEmpty = t.conditionalExpression(inTest, n, _internalizer.emptyExpression);
|
|
n = t.logicalExpression("||", n, addEmpty);
|
|
}
|
|
return n;
|
|
}, {
|
|
skipInvariant: true
|
|
});
|
|
tvalFor.set(key, tval);
|
|
}
|
|
});
|
|
pbindings.forEach(function (val, key, map) {
|
|
var path = key.pathNode;
|
|
var tval = tvalFor.get(key);
|
|
(0, _invariant2.default)(val !== undefined);
|
|
var value = val.value;
|
|
(0, _invariant2.default)(value instanceof _index.Value);
|
|
var mightHaveBeenDeleted = value.mightHaveBeenDeleted();
|
|
var mightBeUndefined = value.mightBeUndefined();
|
|
if (typeof key.key === "string") {
|
|
gen.emitStatement([key.object, tval || value, _this4.intrinsics.empty], function (_ref7) {
|
|
var _ref8 = _slicedToArray(_ref7, 3),
|
|
o = _ref8[0],
|
|
v = _ref8[1],
|
|
e = _ref8[2];
|
|
|
|
(0, _invariant2.default)(path !== undefined);
|
|
var lh = path.buildNode([o, t.identifier(key.key)]);
|
|
var r = t.expressionStatement(t.assignmentExpression("=", lh, v));
|
|
if (mightHaveBeenDeleted) {
|
|
// If v === __empty || (v === undefined && !(key.key in o)) then delete it
|
|
var emptyTest = t.binaryExpression("===", v, e);
|
|
var undefinedTest = t.binaryExpression("===", v, _internalizer.voidExpression);
|
|
var inTest = t.unaryExpression("!", t.binaryExpression("in", t.stringLiteral(key.key), o));
|
|
var guard = t.logicalExpression("||", emptyTest, t.logicalExpression("&&", undefinedTest, inTest));
|
|
var deleteIt = t.expressionStatement(t.unaryExpression("delete", lh));
|
|
return t.ifStatement(mightBeUndefined ? emptyTest : guard, deleteIt, r);
|
|
}
|
|
return r;
|
|
});
|
|
} else {
|
|
gen.emitStatement([key.object, key.key, tval || value, _this4.intrinsics.empty], function (_ref9) {
|
|
var _ref10 = _slicedToArray(_ref9, 4),
|
|
o = _ref10[0],
|
|
p = _ref10[1],
|
|
v = _ref10[2],
|
|
e = _ref10[3];
|
|
|
|
(0, _invariant2.default)(path !== undefined);
|
|
var lh = path.buildNode([o, p]);
|
|
return t.expressionStatement(t.assignmentExpression("=", lh, v));
|
|
});
|
|
}
|
|
});
|
|
}
|
|
}, {
|
|
key: "composeEffects",
|
|
value: function composeEffects(priorEffects, subsequentEffects) {
|
|
var _priorEffects = _slicedToArray(priorEffects, 5),
|
|
pg = _priorEffects[1],
|
|
pb = _priorEffects[2],
|
|
pp = _priorEffects[3],
|
|
po = _priorEffects[4];
|
|
|
|
var _subsequentEffects = _slicedToArray(subsequentEffects, 5),
|
|
sc = _subsequentEffects[0],
|
|
sg = _subsequentEffects[1],
|
|
sb = _subsequentEffects[2],
|
|
sp = _subsequentEffects[3],
|
|
so = _subsequentEffects[4];
|
|
|
|
var result = construct_empty_effects(this);
|
|
|
|
var _result = _slicedToArray(result, 5),
|
|
rb = _result[2],
|
|
rp = _result[3],
|
|
ro = _result[4];
|
|
|
|
result[0] = sc;
|
|
|
|
result[1] = _singletons.Join.composeGenerators(this, pg || result[1], sg);
|
|
|
|
if (pb) {
|
|
pb.forEach(function (val, key, m) {
|
|
return rb.set(key, val);
|
|
});
|
|
}
|
|
sb.forEach(function (val, key, m) {
|
|
return rb.set(key, val);
|
|
});
|
|
|
|
if (pp) {
|
|
pp.forEach(function (desc, propertyBinding, m) {
|
|
return rp.set(propertyBinding, desc);
|
|
});
|
|
}
|
|
sp.forEach(function (val, key, m) {
|
|
return rp.set(key, val);
|
|
});
|
|
|
|
if (po) {
|
|
po.forEach(function (ob, a) {
|
|
return ro.add(ob);
|
|
});
|
|
}
|
|
so.forEach(function (ob, a) {
|
|
return ro.add(ob);
|
|
});
|
|
|
|
return result;
|
|
}
|
|
}, {
|
|
key: "updateAbruptCompletions",
|
|
value: function updateAbruptCompletions(priorEffects, c) {
|
|
if (c.consequent instanceof _completions.AbruptCompletion) {
|
|
c.consequentEffects = this.composeEffects(priorEffects, c.consequentEffects);
|
|
var alternate = c.alternate;
|
|
if (alternate instanceof _completions.PossiblyNormalCompletion) this.updateAbruptCompletions(priorEffects, alternate);
|
|
} else {
|
|
(0, _invariant2.default)(c.alternate instanceof _completions.AbruptCompletion);
|
|
c.alternateEffects = this.composeEffects(priorEffects, c.alternateEffects);
|
|
var consequent = c.consequent;
|
|
if (consequent instanceof _completions.PossiblyNormalCompletion) this.updateAbruptCompletions(priorEffects, consequent);
|
|
}
|
|
}
|
|
}, {
|
|
key: "composeWithSavedCompletion",
|
|
value: function composeWithSavedCompletion(completion) {
|
|
if (this.savedCompletion === undefined) {
|
|
this.savedCompletion = completion;
|
|
this.savedCompletion.savedPathConditions = this.pathConditions;
|
|
this.captureEffects(completion);
|
|
} else {
|
|
this.savedCompletion = _singletons.Join.composePossiblyNormalCompletions(this, this.savedCompletion, completion);
|
|
}
|
|
if (completion.consequent instanceof _completions.AbruptCompletion) {
|
|
_singletons.Path.pushInverseAndRefine(completion.joinCondition);
|
|
if (completion.alternate instanceof _completions.PossiblyNormalCompletion) {
|
|
completion.alternate.pathConditions.forEach(_singletons.Path.pushAndRefine);
|
|
}
|
|
} else if (completion.alternate instanceof _completions.AbruptCompletion) {
|
|
_singletons.Path.pushAndRefine(completion.joinCondition);
|
|
if (completion.consequent instanceof _completions.PossiblyNormalCompletion) {
|
|
completion.consequent.pathConditions.forEach(_singletons.Path.pushAndRefine);
|
|
}
|
|
}
|
|
return completion.value;
|
|
}
|
|
}, {
|
|
key: "incorporatePriorSavedCompletion",
|
|
value: function incorporatePriorSavedCompletion(priorCompletion) {
|
|
if (priorCompletion === undefined) return;
|
|
if (this.savedCompletion === undefined) {
|
|
this.savedCompletion = priorCompletion;
|
|
this.captureEffects(priorCompletion);
|
|
} else {
|
|
this.savedCompletion = _singletons.Join.composePossiblyNormalCompletions(this, priorCompletion, this.savedCompletion);
|
|
}
|
|
}
|
|
}, {
|
|
key: "captureEffects",
|
|
value: function captureEffects(completion) {
|
|
if (completion.savedEffects !== undefined) {
|
|
// Already called captureEffects, just carry on
|
|
return;
|
|
}
|
|
completion.savedEffects = [this.intrinsics.undefined, this.generator, this.modifiedBindings, this.modifiedProperties, this.createdObjects];
|
|
this.generator = new _generator.Generator(this);
|
|
this.modifiedBindings = new Map();
|
|
this.modifiedProperties = new Map();
|
|
this.createdObjects = new Set();
|
|
}
|
|
}, {
|
|
key: "getCapturedEffects",
|
|
value: function getCapturedEffects(completion, v) {
|
|
if (completion.savedEffects === undefined) return undefined;
|
|
if (v === undefined) v = this.intrinsics.undefined;
|
|
(0, _invariant2.default)(this.generator !== undefined);
|
|
(0, _invariant2.default)(this.modifiedBindings !== undefined);
|
|
(0, _invariant2.default)(this.modifiedProperties !== undefined);
|
|
(0, _invariant2.default)(this.createdObjects !== undefined);
|
|
return [v, this.generator, this.modifiedBindings, this.modifiedProperties, this.createdObjects];
|
|
}
|
|
}, {
|
|
key: "stopEffectCapture",
|
|
value: function stopEffectCapture(completion) {
|
|
var e = this.getCapturedEffects(completion);
|
|
if (e !== undefined) {
|
|
this.stopEffectCaptureAndUndoEffects(completion);
|
|
this.applyEffects(e);
|
|
}
|
|
}
|
|
}, {
|
|
key: "stopEffectCaptureAndUndoEffects",
|
|
value: function stopEffectCaptureAndUndoEffects(completion) {
|
|
// Roll back the state changes
|
|
this.restoreBindings(this.modifiedBindings);
|
|
this.restoreProperties(this.modifiedProperties);
|
|
|
|
// Restore saved state
|
|
if (completion.savedEffects !== undefined) {
|
|
var _completion$savedEffe = _slicedToArray(completion.savedEffects, 5),
|
|
c = _completion$savedEffe[0],
|
|
g = _completion$savedEffe[1],
|
|
b = _completion$savedEffe[2],
|
|
p = _completion$savedEffe[3],
|
|
o = _completion$savedEffe[4];
|
|
|
|
c;
|
|
completion.savedEffects = undefined;
|
|
this.generator = g;
|
|
this.modifiedBindings = b;
|
|
this.modifiedProperties = p;
|
|
this.createdObjects = o;
|
|
} else {
|
|
(0, _invariant2.default)(false);
|
|
}
|
|
}
|
|
|
|
// Apply the given effects to the global state
|
|
|
|
}, {
|
|
key: "applyEffects",
|
|
value: function applyEffects(effects) {
|
|
var leadingComment = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : "";
|
|
|
|
var _effects2 = _slicedToArray(effects, 5),
|
|
generator = _effects2[1],
|
|
bindings = _effects2[2],
|
|
properties = _effects2[3],
|
|
createdObjects = _effects2[4];
|
|
|
|
// Add generated code for property modifications
|
|
|
|
|
|
this.appendGenerator(generator, leadingComment);
|
|
|
|
// Restore bindings
|
|
this.restoreBindings(bindings);
|
|
this.restoreProperties(properties);
|
|
|
|
// track bindings
|
|
var realmModifiedBindings = this.modifiedBindings;
|
|
if (realmModifiedBindings !== undefined) {
|
|
bindings.forEach(function (val, key, m) {
|
|
(0, _invariant2.default)(realmModifiedBindings !== undefined);
|
|
if (!realmModifiedBindings.has(key)) {
|
|
realmModifiedBindings.set(key, val);
|
|
}
|
|
});
|
|
}
|
|
var realmModifiedProperties = this.modifiedProperties;
|
|
if (realmModifiedProperties !== undefined) {
|
|
properties.forEach(function (desc, propertyBinding, m) {
|
|
(0, _invariant2.default)(realmModifiedProperties !== undefined);
|
|
if (!realmModifiedProperties.has(propertyBinding)) {
|
|
realmModifiedProperties.set(propertyBinding, desc);
|
|
}
|
|
});
|
|
}
|
|
|
|
// add created objects
|
|
if (createdObjects.size > 0) {
|
|
var realmCreatedObjects = this.createdObjects;
|
|
if (realmCreatedObjects === undefined) this.createdObjects = new Set(createdObjects);else {
|
|
createdObjects.forEach(function (ob, a) {
|
|
(0, _invariant2.default)(realmCreatedObjects !== undefined);
|
|
realmCreatedObjects.add(ob);
|
|
});
|
|
}
|
|
}
|
|
}
|
|
}, {
|
|
key: "outputToConsole",
|
|
value: function outputToConsole(method, args) {
|
|
if (this.isReadOnly) {
|
|
// This only happens during speculative execution and is reported elsewhere
|
|
throw new _errors.FatalError("Trying to create console output in read-only realm");
|
|
}
|
|
if (this.useAbstractInterpretation) {
|
|
(0, _invariant2.default)(this.generator !== undefined);
|
|
this.generator.emitConsoleLog(method, args);
|
|
} else {
|
|
console[method](getString(this, args));
|
|
}
|
|
|
|
function getString(realm, values) {
|
|
var res = "";
|
|
while (values.length) {
|
|
var next = values.shift();
|
|
var nextString = _singletons.To.ToString(realm, next);
|
|
res += nextString;
|
|
}
|
|
return res;
|
|
}
|
|
}
|
|
|
|
// Record the current value of binding in this.modifiedBindings unless
|
|
// there is already an entry for binding.
|
|
|
|
}, {
|
|
key: "recordModifiedBinding",
|
|
value: function recordModifiedBinding(binding) {
|
|
if (binding.environment.isReadOnly) {
|
|
// This only happens during speculative execution and is reported elsewhere
|
|
throw new _errors.FatalError("Trying to modify a binding in read-only realm");
|
|
}
|
|
if (this.modifiedBindings !== undefined && !this.modifiedBindings.has(binding)) this.modifiedBindings.set(binding, {
|
|
hasLeaked: binding.hasLeaked,
|
|
value: binding.value
|
|
});
|
|
return binding;
|
|
}
|
|
}, {
|
|
key: "callReportObjectGetOwnProperties",
|
|
value: function callReportObjectGetOwnProperties(ob) {
|
|
if (this.reportObjectGetOwnProperties !== undefined) {
|
|
this.reportObjectGetOwnProperties(ob);
|
|
}
|
|
}
|
|
}, {
|
|
key: "callReportPropertyAccess",
|
|
value: function callReportPropertyAccess(binding) {
|
|
if (this.reportPropertyAccess !== undefined) {
|
|
this.reportPropertyAccess(binding);
|
|
}
|
|
}
|
|
|
|
// Record the current value of binding in this.modifiedProperties unless
|
|
// there is already an entry for binding.
|
|
|
|
}, {
|
|
key: "recordModifiedProperty",
|
|
value: function recordModifiedProperty(binding) {
|
|
if (binding === undefined) return;
|
|
if (this.isReadOnly && (this.getRunningContext().isReadOnly || !this.isNewObject(binding.object))) {
|
|
// This only happens during speculative execution and is reported elsewhere
|
|
throw new _errors.FatalError("Trying to modify a property in read-only realm");
|
|
}
|
|
this.callReportPropertyAccess(binding);
|
|
if (this.modifiedProperties !== undefined && !this.modifiedProperties.has(binding)) {
|
|
this.modifiedProperties.set(binding, (0, _index2.cloneDescriptor)(binding.descriptor));
|
|
}
|
|
}
|
|
}, {
|
|
key: "isNewObject",
|
|
value: function isNewObject(object) {
|
|
if (object instanceof _index.AbstractObjectValue) return false;
|
|
return this.createdObjects === undefined || this.createdObjects.has(object);
|
|
}
|
|
}, {
|
|
key: "recordNewObject",
|
|
value: function recordNewObject(object) {
|
|
if (this.createdObjects !== undefined) {
|
|
this.createdObjects.add(object);
|
|
}
|
|
if (this.createdObjectsTrackedForLeaks !== undefined) {
|
|
this.createdObjectsTrackedForLeaks.add(object);
|
|
}
|
|
}
|
|
|
|
// Returns the current values of modifiedBindings and modifiedProperties
|
|
// and then assigns new empty maps to them.
|
|
|
|
}, {
|
|
key: "getAndResetModifiedMaps",
|
|
value: function getAndResetModifiedMaps() {
|
|
var result = [this.modifiedBindings, this.modifiedProperties];
|
|
this.modifiedBindings = new Map();
|
|
this.modifiedProperties = new Map();
|
|
return result;
|
|
}
|
|
|
|
// Restores each Binding in the given map to the value it
|
|
// had when it was entered into the map and updates the map to record
|
|
// the value the Binding had just before the call to this method.
|
|
|
|
}, {
|
|
key: "restoreBindings",
|
|
value: function restoreBindings(modifiedBindings) {
|
|
if (modifiedBindings === undefined) return;
|
|
modifiedBindings.forEach(function (_ref11, binding, m) {
|
|
var hasLeaked = _ref11.hasLeaked,
|
|
value = _ref11.value;
|
|
|
|
var l = binding.hasLeaked;
|
|
var v = binding.value;
|
|
binding.hasLeaked = hasLeaked;
|
|
binding.value = value;
|
|
m.set(binding, {
|
|
hasLeaked: l,
|
|
value: v
|
|
});
|
|
});
|
|
}
|
|
|
|
// Restores each PropertyBinding in the given map to the value it
|
|
// had when it was entered into the map and updates the map to record
|
|
// the value the Binding had just before the call to this method.
|
|
|
|
}, {
|
|
key: "restoreProperties",
|
|
value: function restoreProperties(modifiedProperties) {
|
|
if (modifiedProperties === undefined) return;
|
|
modifiedProperties.forEach(function (desc, propertyBinding, m) {
|
|
var d = propertyBinding.descriptor;
|
|
propertyBinding.descriptor = desc;
|
|
m.set(propertyBinding, d);
|
|
});
|
|
}
|
|
|
|
// Provide the realm with maps in which to track modifications.
|
|
// A map can be set to undefined if no tracking is required.
|
|
|
|
}, {
|
|
key: "setModifiedMaps",
|
|
value: function setModifiedMaps(modifiedBindings, modifiedProperties) {
|
|
this.modifiedBindings = modifiedBindings;
|
|
this.modifiedProperties = modifiedProperties;
|
|
}
|
|
}, {
|
|
key: "rebuildObjectProperty",
|
|
value: function rebuildObjectProperty(object, key, propertyValue, path) {
|
|
if (!(propertyValue instanceof _index.AbstractValue)) return;
|
|
if (!propertyValue.isIntrinsic()) {
|
|
propertyValue.intrinsicName = path + "." + key;
|
|
propertyValue.kind = "rebuiltProperty";
|
|
propertyValue.args = [object];
|
|
propertyValue._buildNode = function (_ref12) {
|
|
var _ref13 = _slicedToArray(_ref12, 1),
|
|
node = _ref13[0];
|
|
|
|
return t.memberExpression(node, t.identifier(key));
|
|
};
|
|
this.rebuildNestedProperties(propertyValue, propertyValue.intrinsicName);
|
|
}
|
|
}
|
|
}, {
|
|
key: "rebuildNestedProperties",
|
|
value: function rebuildNestedProperties(abstractValue, path) {
|
|
if (!(abstractValue instanceof _index.AbstractObjectValue)) return;
|
|
if (abstractValue.values.isTop()) return;
|
|
var template = abstractValue.getTemplate();
|
|
(0, _invariant2.default)(!template.intrinsicName || template.intrinsicName === path);
|
|
// TODO #882: We are using the concept of "intrinsic values" to mark the template
|
|
// object as intrinsic, so that we'll never emit code that creates it, as it instead is used
|
|
// to refer to an unknown but existing object.
|
|
// However, it's not really an intrinsic object, and it might not exist ahead of time, but only starting
|
|
// from this point on, which might be tied to some nested generator.
|
|
// Which we currently don't track, and that needs to get fixed.
|
|
// For now, we use intrinsicNameGenerated to mark this case.
|
|
template.intrinsicName = path;
|
|
template.intrinsicNameGenerated = true;
|
|
var _iteratorNormalCompletion6 = true;
|
|
var _didIteratorError6 = false;
|
|
var _iteratorError6 = undefined;
|
|
|
|
try {
|
|
for (var _iterator6 = template.properties[Symbol.iterator](), _step6; !(_iteratorNormalCompletion6 = (_step6 = _iterator6.next()).done); _iteratorNormalCompletion6 = true) {
|
|
var _ref14 = _step6.value;
|
|
|
|
var _ref15 = _slicedToArray(_ref14, 2);
|
|
|
|
var _key = _ref15[0];
|
|
var binding = _ref15[1];
|
|
|
|
if (binding === undefined || binding.descriptor === undefined) continue; // deleted
|
|
(0, _invariant2.default)(binding.descriptor !== undefined);
|
|
var _value = binding.descriptor.value;
|
|
_singletons.Properties.ThrowIfMightHaveBeenDeleted(_value);
|
|
if (_value === undefined) {
|
|
_index.AbstractValue.reportIntrospectionError(abstractValue, _key);
|
|
throw new _errors.FatalError();
|
|
}
|
|
(0, _invariant2.default)(_value instanceof _index.Value);
|
|
this.rebuildObjectProperty(abstractValue, _key, _value, path);
|
|
}
|
|
} catch (err) {
|
|
_didIteratorError6 = true;
|
|
_iteratorError6 = err;
|
|
} finally {
|
|
try {
|
|
if (!_iteratorNormalCompletion6 && _iterator6.return) {
|
|
_iterator6.return();
|
|
}
|
|
} finally {
|
|
if (_didIteratorError6) {
|
|
throw _iteratorError6;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}, {
|
|
key: "createExecutionContext",
|
|
value: function createExecutionContext() {
|
|
var context = new ExecutionContext();
|
|
|
|
var loc = this.nextContextLocation;
|
|
if (loc) {
|
|
context.setLocation(loc);
|
|
this.nextContextLocation = null;
|
|
}
|
|
|
|
return context;
|
|
}
|
|
}, {
|
|
key: "setNextExecutionContextLocation",
|
|
value: function setNextExecutionContextLocation(loc) {
|
|
if (!loc) return;
|
|
|
|
//if (this.nextContextLocation) {
|
|
// throw new ThrowCompletion(
|
|
// Construct(this, this.intrinsics.TypeError, [new StringValue(this, "Already have a context location that we haven't used yet")])
|
|
// );
|
|
//} else {
|
|
this.nextContextLocation = loc;
|
|
//}
|
|
}
|
|
}, {
|
|
key: "reportIntrospectionError",
|
|
value: function reportIntrospectionError(message) {
|
|
if (message === undefined) message = "";
|
|
if (typeof message === "string") message = new _index.StringValue(this, message);
|
|
(0, _invariant2.default)(message instanceof _index.StringValue);
|
|
this.nextContextLocation = this.currentLocation;
|
|
var error = new _errors.CompilerDiagnostic(message.value, this.currentLocation, "PP0001", "FatalError");
|
|
this.handleError(error);
|
|
}
|
|
}, {
|
|
key: "createErrorThrowCompletion",
|
|
value: function createErrorThrowCompletion(type, message) {
|
|
(0, _invariant2.default)(type !== this.intrinsics.__IntrospectionError);
|
|
if (message === undefined) message = "";
|
|
if (typeof message === "string") message = new _index.StringValue(this, message);
|
|
(0, _invariant2.default)(message instanceof _index.StringValue);
|
|
this.nextContextLocation = this.currentLocation;
|
|
return new _completions.ThrowCompletion((0, _index2.Construct)(this, type, [message]), this.currentLocation);
|
|
}
|
|
}, {
|
|
key: "appendGenerator",
|
|
value: function appendGenerator(generator) {
|
|
var leadingComment = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : "";
|
|
|
|
var realmGenerator = this.generator;
|
|
if (realmGenerator === undefined) {
|
|
(0, _invariant2.default)(generator.empty());
|
|
return;
|
|
}
|
|
realmGenerator.appendGenerator(generator, leadingComment);
|
|
}
|
|
|
|
// Pass the error to the realm's error-handler
|
|
// Return value indicates whether the caller should try to recover from the
|
|
// error or not ('true' means recover if possible).
|
|
|
|
}, {
|
|
key: "handleError",
|
|
value: function handleError(diagnostic) {
|
|
if (!diagnostic.callStack && this.contextStack.length > 0) {
|
|
var error = (0, _index2.Construct)(this, this.intrinsics.Error);
|
|
var stack = error.$Get("stack", error);
|
|
if (stack instanceof _index.StringValue) diagnostic.callStack = stack.value;
|
|
}
|
|
// Default behaviour is to bail on the first error
|
|
var errorHandler = this.errorHandler;
|
|
if (!errorHandler) {
|
|
var msg = diagnostic.errorCode + ": " + diagnostic.message;
|
|
if (diagnostic.location) {
|
|
var loc_start = diagnostic.location.start;
|
|
var loc_end = diagnostic.location.end;
|
|
msg += " at " + loc_start.line + ":" + loc_start.column + " to " + loc_end.line + ":" + loc_end.column;
|
|
}
|
|
try {
|
|
switch (diagnostic.severity) {
|
|
case "Information":
|
|
console.log("Info: " + msg);
|
|
return "Recover";
|
|
case "Warning":
|
|
console.warn("Warn: " + msg);
|
|
return "Recover";
|
|
case "RecoverableError":
|
|
console.error("Error: " + msg);
|
|
return "Fail";
|
|
case "FatalError":
|
|
console.error("Fatal Error: " + msg);
|
|
return "Fail";
|
|
default:
|
|
(0, _invariant2.default)(false, "Unexpected error type");
|
|
}
|
|
} finally {
|
|
console.log(diagnostic.callStack);
|
|
}
|
|
}
|
|
return errorHandler(diagnostic);
|
|
}
|
|
}, {
|
|
key: "saveNameString",
|
|
value: function saveNameString(nameString) {
|
|
this._abstractValuesDefined.add(nameString);
|
|
}
|
|
}, {
|
|
key: "isNameStringUnique",
|
|
value: function isNameStringUnique(nameString) {
|
|
return !this._abstractValuesDefined.has(nameString);
|
|
}
|
|
}]);
|
|
|
|
return Realm;
|
|
}();
|
|
//# sourceMappingURL=realm.js.map
|