"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