"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.ResidualHeapVisitor = 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. */ var _environment = require("../environment.js"); var _errors = require("../errors.js"); var _realm = require("../realm.js"); var _index = require("../methods/index.js"); var _index2 = require("../values/index.js"); var _Error = require("../intrinsics/ecma262/Error.js"); var _babelTypes = require("babel-types"); var t = _interopRequireWildcard(_babelTypes); var _generator = require("../utils/generator.js"); var _babelTraverse = require("babel-traverse"); var _babelTraverse2 = _interopRequireDefault(_babelTraverse); var _invariant = require("../invariant.js"); var _invariant2 = _interopRequireDefault(_invariant); var _visitors = require("./visitors.js"); var _logger = require("./logger.js"); var _modules = require("./modules.js"); var _ResidualHeapInspector = require("./ResidualHeapInspector.js"); var _utils = require("./utils.js"); var _singletons = require("../singletons.js"); var _utils2 = require("../react/utils.js"); var _hoisting = require("../react/hoisting.js"); var _ReactElementSet = require("../react/ReactElementSet.js"); var _ReactElementSet2 = _interopRequireDefault(_ReactElementSet); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 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 _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } /* This class visits all values that are reachable in the residual heap. In particular, this "filters out" values that are: - captured by a DeclarativeEnvironmentRecord, but not actually used by any closure. - Unmodified prototype objects TODO #680: Figure out minimal set of values that need to be kept alive for WeakSet and WeakMap instances. */ var ResidualHeapVisitor = exports.ResidualHeapVisitor = function () { function ResidualHeapVisitor(realm, logger, modules, additionalFunctionValuesAndEffects) { _classCallCheck(this, ResidualHeapVisitor); (0, _invariant2.default)(realm.useAbstractInterpretation); this.realm = realm; this.logger = logger; this.modules = modules; this.declarativeEnvironmentRecordsBindings = new Map(); this.globalBindings = new Map(); this.functionInfos = new Map(); this.functionInstances = new Map(); this.values = new Map(); var generator = this.realm.generator; (0, _invariant2.default)(generator); this.scope = this.commonScope = generator; this.inspector = new _ResidualHeapInspector.ResidualHeapInspector(realm, logger); this.referencedDeclaredValues = new Set(); this.delayedVisitGeneratorEntries = []; this.shouldVisitReactLibrary = false; this.additionalFunctionValuesAndEffects = additionalFunctionValuesAndEffects; this.equivalenceSet = new _index.HashSet(); this.reactElementEquivalenceSet = new _ReactElementSet2.default(realm, this.equivalenceSet); this.additionalFunctionValueInfos = new Map(); this.inAdditionalFunction = false; this.additionalRoots = new Set(); } // Caches that ensure one ResidualFunctionBinding exists per (record, name) pair // Either the realm's generator or the FunctionValue of an additional function to serialize // We only want to add to additionalRoots when we're in an additional function // Tracks objects + functions that were visited from inside additional functions that need to be serialized in a // parent scope of the additional function (e.g. functions/objects only used from additional functions that were // declared outside the additional function need to be serialized in the additional function's parent scope for // identity to work). _createClass(ResidualHeapVisitor, [{ key: "_withScope", value: function _withScope(scope, f) { var oldScope = this.scope; this.scope = scope; f(); this.scope = oldScope; } }, { key: "visitObjectProperty", value: function visitObjectProperty(binding) { var desc = binding.descriptor; if (desc === undefined) return; //deleted var obj = binding.object; if (obj instanceof _index2.AbstractObjectValue || !this.inspector.canIgnoreProperty(obj, binding.key)) { this.visitDescriptor(desc); } } }, { key: "visitObjectProperties", value: function visitObjectProperties(obj, kind) { // visit properties if (kind !== "ReactElement") { var _iteratorNormalCompletion = true; var _didIteratorError = false; var _iteratorError = undefined; try { for (var _iterator = obj.symbols[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { var _ref = _step.value; var _ref2 = _slicedToArray(_ref, 2); var symbol = _ref2[0]; var propertyBinding = _ref2[1]; (0, _invariant2.default)(propertyBinding); var desc = propertyBinding.descriptor; if (desc === undefined) continue; //deleted this.visitDescriptor(desc); this.visitValue(symbol); } } catch (err) { _didIteratorError = true; _iteratorError = err; } finally { try { if (!_iteratorNormalCompletion && _iterator.return) { _iterator.return(); } } finally { if (_didIteratorError) { throw _iteratorError; } } } } // visit properties var _iteratorNormalCompletion2 = true; var _didIteratorError2 = false; var _iteratorError2 = undefined; try { for (var _iterator2 = obj.properties[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) { var _ref3 = _step2.value; var _ref4 = _slicedToArray(_ref3, 2); var propertyBindingKey = _ref4[0]; var propertyBindingValue = _ref4[1]; // we don't want to the $$typeof or _owner/_store properties // as this is contained within the JSXElement, otherwise // they we be need to be emitted during serialization if (kind === "ReactElement" && (propertyBindingKey === "$$typeof" || propertyBindingKey === "_owner" || propertyBindingKey === "_store")) { continue; } if (propertyBindingKey.pathNode !== undefined) continue; // property is written to inside a loop (0, _invariant2.default)(propertyBindingValue); this.visitObjectProperty(propertyBindingValue); } // inject properties with computed names } catch (err) { _didIteratorError2 = true; _iteratorError2 = err; } finally { try { if (!_iteratorNormalCompletion2 && _iterator2.return) { _iterator2.return(); } } finally { if (_didIteratorError2) { throw _iteratorError2; } } } if (obj.unknownProperty !== undefined) { var _desc = obj.unknownProperty.descriptor; if (_desc !== undefined) { var val = _desc.value; (0, _invariant2.default)(val instanceof _index2.AbstractValue); this.visitObjectPropertiesWithComputedNames(val); } } // prototype if (kind !== "ReactElement") { // we don't want to the ReactElement prototype visited // as this is contained within the JSXElement, otherwise // they we be need to be emitted during serialization this.visitObjectPrototype(obj); } if (obj instanceof _index2.FunctionValue) this.visitConstructorPrototype(obj); } }, { key: "visitObjectPrototype", value: function visitObjectPrototype(obj) { var proto = obj.$Prototype; var kind = obj.getKind(); if (proto === this.realm.intrinsics[kind + "Prototype"]) return; this.visitValue(proto); } }, { key: "visitConstructorPrototype", value: function visitConstructorPrototype(func) { // If the original prototype object was mutated, // request its serialization here as this might be observable by // residual code. var prototype = _ResidualHeapInspector.ResidualHeapInspector.getPropertyValue(func, "prototype"); if (prototype instanceof _index2.ObjectValue && prototype.originalConstructor === func && !this.inspector.isDefaultPrototype(prototype)) { this.visitValue(prototype); } } }, { key: "visitObjectPropertiesWithComputedNames", value: function visitObjectPropertiesWithComputedNames(absVal) { if (absVal.kind === "widened property") return; (0, _invariant2.default)(absVal.args.length === 3); var cond = absVal.args[0]; (0, _invariant2.default)(cond instanceof _index2.AbstractValue); if (cond.kind === "template for property name condition") { var P = cond.args[0]; (0, _invariant2.default)(P instanceof _index2.AbstractValue); var V = absVal.args[1]; var earlier_props = absVal.args[2]; if (earlier_props instanceof _index2.AbstractValue) this.visitObjectPropertiesWithComputedNames(earlier_props); this.visitValue(P); this.visitValue(V); } else { // conditional assignment absVal.args[0] = this.visitEquivalentValue(cond); var consequent = absVal.args[1]; (0, _invariant2.default)(consequent instanceof _index2.AbstractValue); var alternate = absVal.args[2]; (0, _invariant2.default)(alternate instanceof _index2.AbstractValue); this.visitObjectPropertiesWithComputedNames(consequent); this.visitObjectPropertiesWithComputedNames(alternate); } } }, { key: "visitDescriptor", value: function visitDescriptor(desc) { (0, _invariant2.default)(desc.value === undefined || desc.value instanceof _index2.Value); if (desc.joinCondition !== undefined) { desc.joinCondition = this.visitEquivalentValue(desc.joinCondition); if (desc.descriptor1 !== undefined) this.visitDescriptor(desc.descriptor1); if (desc.descriptor2 !== undefined) this.visitDescriptor(desc.descriptor2); return; } if (desc.value !== undefined) desc.value = this.visitEquivalentValue(desc.value); if (desc.get !== undefined) this.visitValue(desc.get); if (desc.set !== undefined) this.visitValue(desc.set); } }, { key: "visitValueArray", value: function visitValueArray(val) { this.visitObjectProperties(val); var realm = this.realm; var lenProperty = (0, _index.Get)(realm, val, "length"); if (lenProperty instanceof _index2.AbstractValue || _singletons.To.ToLength(realm, lenProperty) !== (0, _utils.getSuggestedArrayLiteralLength)(realm, val)) { this.visitValue(lenProperty); } } }, { key: "visitValueMap", value: function visitValueMap(val) { var kind = val.getKind(); var entries = void 0; if (kind === "Map") { entries = val.$MapData; } else { (0, _invariant2.default)(kind === "WeakMap"); entries = val.$WeakMapData; } (0, _invariant2.default)(entries !== undefined); var len = entries.length; for (var i = 0; i < len; i++) { var _entry = entries[i]; var key = _entry.$Key; var value = _entry.$Value; if (key === undefined || value === undefined) continue; this.visitValue(key); this.visitValue(value); } } }, { key: "visitValueSet", value: function visitValueSet(val) { var kind = val.getKind(); var entries = void 0; if (kind === "Set") { entries = val.$SetData; } else { (0, _invariant2.default)(kind === "WeakSet"); entries = val.$WeakSetData; } (0, _invariant2.default)(entries !== undefined); var len = entries.length; for (var i = 0; i < len; i++) { var _entry2 = entries[i]; if (_entry2 === undefined) continue; this.visitValue(_entry2); } } }, { key: "visitValueFunction", value: function visitValueFunction(val, parentScope) { var _this = this; if (this.inAdditionalFunction) this.additionalRoots.add(val); this.visitObjectProperties(val); if (val instanceof _index2.BoundFunctionValue) { this.visitValue(val.$BoundTargetFunction); this.visitValue(val.$BoundThis); var _iteratorNormalCompletion3 = true; var _didIteratorError3 = false; var _iteratorError3 = undefined; try { for (var _iterator3 = val.$BoundArguments[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) { var boundArg = _step3.value; this.visitValue(boundArg); } } catch (err) { _didIteratorError3 = true; _iteratorError3 = err; } finally { try { if (!_iteratorNormalCompletion3 && _iterator3.return) { _iterator3.return(); } } finally { if (_didIteratorError3) { throw _iteratorError3; } } } return; } (0, _invariant2.default)(!(val instanceof _index2.NativeFunctionValue), "all native function values should be intrinsics"); (0, _invariant2.default)(val instanceof _index2.ECMAScriptSourceFunctionValue); (0, _invariant2.default)(val.constructor === _index2.ECMAScriptSourceFunctionValue); var formalParameters = val.$FormalParameters; var code = val.$ECMAScriptCode; var functionInfo = this.functionInfos.get(code); var residualFunctionBindings = new Map(); this.functionInstances.set(val, { residualFunctionBindings: residualFunctionBindings, initializationStatements: [], functionValue: val, scopeInstances: new Map() }); if (!functionInfo) { functionInfo = { unbound: new Set(), modified: new Set(), usesArguments: false, usesThis: false }; var state = { tryQuery: this.logger.tryQuery.bind(this.logger), val: val, functionInfo: functionInfo, realm: this.realm }; (0, _babelTraverse2.default)(t.file(t.program([t.expressionStatement(t.functionExpression(null, formalParameters, code))])), _visitors.ClosureRefVisitor, null, state); this.functionInfos.set(code, functionInfo); if (val.isResidual && functionInfo.unbound.size) { if (!val.isUnsafeResidual) { this.logger.logError(val, "residual function " + ((0, _Error.describeLocation)(this.realm, val, undefined, code.loc) || "(unknown)") + " refers to the following identifiers defined outside of the local scope: " + Object.keys(functionInfo.unbound).join(", ")); } } } var additionalFunctionEffects = this.additionalFunctionValuesAndEffects.get(val); if (additionalFunctionEffects) { this._visitAdditionalFunction(val, additionalFunctionEffects, parentScope); } else { this._withScope(val, function () { (0, _invariant2.default)(functionInfo); var _iteratorNormalCompletion4 = true; var _didIteratorError4 = false; var _iteratorError4 = undefined; try { for (var _iterator4 = functionInfo.unbound[Symbol.iterator](), _step4; !(_iteratorNormalCompletion4 = (_step4 = _iterator4.next()).done); _iteratorNormalCompletion4 = true) { var innerName = _step4.value; var residualBinding = _this.visitBinding(val, innerName); (0, _invariant2.default)(residualBinding !== undefined); residualFunctionBindings.set(innerName, residualBinding); if (functionInfo.modified.has(innerName)) residualBinding.modified = true; } } catch (err) { _didIteratorError4 = true; _iteratorError4 = err; } finally { try { if (!_iteratorNormalCompletion4 && _iterator4.return) { _iterator4.return(); } } finally { if (_didIteratorError4) { throw _iteratorError4; } } } }); } } // Visits a binding, if createBinding is true, will always return a ResidualFunctionBinding // otherwise visits + returns the binding only if one already exists. }, { key: "visitBinding", value: function visitBinding(val, name) { var _this2 = this; var createBinding = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true; var residualFunctionBinding = void 0; var doesNotMatter = true; var reference = this.logger.tryQuery(function () { return _singletons.Environment.ResolveBinding(_this2.realm, name, doesNotMatter, val.$Environment); }, undefined, false /* The only reason `ResolveBinding` might fail is because the global object is partial. But in that case, we know that we are dealing with the common scope. */ ); var getFromMap = createBinding ? _utils.getOrDefault : function (map, key, defaultFn) { return map.get(key); }; if (reference === undefined || _singletons.Environment.IsUnresolvableReference(this.realm, reference) || reference.base instanceof _environment.GlobalEnvironmentRecord) { // Global Binding residualFunctionBinding = getFromMap(this.globalBindings, name, function () { return { value: _this2.realm.getGlobalLetBinding(name), modified: true, declarativeEnvironmentRecord: null }; }); } else { // DeclarativeEnvironmentRecord binding (0, _invariant2.default)(!_singletons.Environment.IsUnresolvableReference(this.realm, reference)); var referencedBase = reference.base; var referencedName = reference.referencedName; if (typeof referencedName !== "string") { throw new _errors.FatalError("TODO: do not know how to visit reference with symbol"); } (0, _invariant2.default)(referencedBase instanceof _environment.DeclarativeEnvironmentRecord); var residualFunctionBindings = (0, _utils.getOrDefault)(this.declarativeEnvironmentRecordsBindings, referencedBase, function () { return new Map(); }); residualFunctionBinding = getFromMap(residualFunctionBindings, referencedName, function () { (0, _invariant2.default)(referencedBase instanceof _environment.DeclarativeEnvironmentRecord); var binding = referencedBase.bindings[referencedName]; (0, _invariant2.default)(!binding.deletable); return { value: binding.initialized && binding.value || _this2.realm.intrinsics.undefined, modified: false, declarativeEnvironmentRecord: referencedBase }; }); } if (residualFunctionBinding && residualFunctionBinding.value) residualFunctionBinding.value = this.visitEquivalentValue(residualFunctionBinding.value); return residualFunctionBinding; } }, { key: "visitValueObject", value: function visitValueObject(val) { if (this.inAdditionalFunction) this.additionalRoots.add(val); var kind = val.getKind(); this.visitObjectProperties(val, kind); // If this object is a prototype object that was implicitly created by the runtime // for a constructor, then we can obtain a reference to this object // in a special way that's handled alongside function serialization. var constructor = val.originalConstructor; if (constructor !== undefined) { this.visitValue(constructor); return; } switch (kind) { case "RegExp": case "Number": case "String": case "Boolean": case "ArrayBuffer": return; case "ReactElement": this.shouldVisitReactLibrary = true; // check we can hoist a React Element (0, _hoisting.canHoistReactElement)(this.realm, val, this); return; case "Date": var dateValue = val.$DateValue; (0, _invariant2.default)(dateValue !== undefined); this.visitValue(dateValue); return; case "Float32Array": case "Float64Array": case "Int8Array": case "Int16Array": case "Int32Array": case "Uint8Array": case "Uint16Array": case "Uint32Array": case "Uint8ClampedArray": case "DataView": var buf = val.$ViewedArrayBuffer; (0, _invariant2.default)(buf !== undefined); this.visitValue(buf); return; case "Map": case "WeakMap": this.visitValueMap(val); return; case "Set": case "WeakSet": this.visitValueSet(val); return; default: if (kind !== "Object") this.logger.logError(val, "Object of kind " + kind + " is not supported in residual heap."); if (this.$ParameterMap !== undefined) { this.logger.logError(val, "Arguments object is not supported in residual heap."); } if (this.realm.react.enabled && (0, _utils2.valueIsReactLibraryObject)(this.realm, val, this.logger)) { this.realm.react.reactLibraryObject = val; } return; } } }, { key: "visitValueSymbol", value: function visitValueSymbol(val) { if (val.$Description) this.visitValue(val.$Description); } }, { key: "visitValueProxy", value: function visitValueProxy(val) { this.visitValue(val.$ProxyTarget); this.visitValue(val.$ProxyHandler); } }, { key: "visitAbstractValue", value: function visitAbstractValue(val) { if (val.kind === "sentinel member expression") this.logger.logError(val, "expressions of type o[p] are not yet supported for partially known o and unknown p"); for (var i = 0, n = val.args.length; i < n; i++) { val.args[i] = this.visitEquivalentValue(val.args[i]); } } // Overridable hook for pre-visiting the value. // Return false will tell visitor to skip visiting children of this node. }, { key: "preProcessValue", value: function preProcessValue(val) { return this._mark(val); } // Overridable hook for post-visiting the value. }, { key: "postProcessValue", value: function postProcessValue(val) {} }, { key: "_mark", value: function _mark(val) { var scopes = this.values.get(val); if (scopes === undefined) this.values.set(val, scopes = new Set()); if (scopes.has(this.scope)) return false; scopes.add(this.scope); return true; } }, { key: "visitEquivalentValue", value: function visitEquivalentValue(val) { if (val instanceof _index2.AbstractValue) { var equivalentValue = this.equivalenceSet.add(val); if (this.preProcessValue(equivalentValue)) this.visitAbstractValue(equivalentValue); this.postProcessValue(equivalentValue); return equivalentValue; } if (val instanceof _index2.ObjectValue && (0, _utils2.isReactElement)(val)) { var equivalentReactElementValue = this.reactElementEquivalenceSet.add(val); if (this._mark(equivalentReactElementValue)) this.visitValueObject(equivalentReactElementValue); return equivalentReactElementValue; } this.visitValue(val); return val; } }, { key: "visitValue", value: function visitValue(val) { var _this3 = this; (0, _invariant2.default)(!val.refuseSerialization); if (val instanceof _index2.AbstractValue) { if (this.preProcessValue(val)) this.visitAbstractValue(val); } else if (val.isIntrinsic()) { // All intrinsic values exist from the beginning of time... // ...except for a few that come into existence as templates for abstract objects (TODO #882). if (val.isTemplate) this.preProcessValue(val);else this._withScope(this.commonScope, function () { _this3.preProcessValue(val); }); } else if (val instanceof _index2.EmptyValue) { this.preProcessValue(val); } else if (_ResidualHeapInspector.ResidualHeapInspector.isLeaf(val)) { this.preProcessValue(val); } else if ((0, _index.IsArray)(this.realm, val)) { (0, _invariant2.default)(val instanceof _index2.ObjectValue); if (this.preProcessValue(val)) this.visitValueArray(val); } else if (val instanceof _index2.ProxyValue) { if (this.preProcessValue(val)) this.visitValueProxy(val); } else if (val instanceof _index2.FunctionValue) { // Function declarations should get hoisted in common scope so that instances only get allocated once var parentScope = this.scope; this._withScope(this.commonScope, function () { (0, _invariant2.default)(val instanceof _index2.FunctionValue); if (_this3.preProcessValue(val)) _this3.visitValueFunction(val, parentScope); }); } else if (val instanceof _index2.SymbolValue) { if (this.preProcessValue(val)) this.visitValueSymbol(val); } else { (0, _invariant2.default)(val instanceof _index2.ObjectValue); // Prototypes are reachable via function declarations, and those get hoisted, so we need to move // prototype initialization to the common scope code as well. if (val.originalConstructor !== undefined) { this._withScope(this.commonScope, function () { (0, _invariant2.default)(val instanceof _index2.ObjectValue); if (_this3.preProcessValue(val)) _this3.visitValueObject(val); }); } else { if (this.preProcessValue(val)) this.visitValueObject(val); } } this.postProcessValue(val); } }, { key: "createGeneratorVisitCallbacks", value: function createGeneratorVisitCallbacks(generator, commonScope) { var _this4 = this; return { visitValues: function visitValues(values) { for (var i = 0, n = values.length; i < n; i++) { values[i] = _this4.visitEquivalentValue(values[i]); } }, visitGenerator: this.visitGenerator.bind(this), canSkip: function canSkip(value) { return !_this4.referencedDeclaredValues.has(value) && !_this4.values.has(value); }, recordDeclaration: function recordDeclaration(value) { _this4.referencedDeclaredValues.add(value); }, recordDelayedEntry: function recordDelayedEntry(entry) { _this4.delayedVisitGeneratorEntries.push({ commonScope: commonScope, generator: generator, entry: entry }); } }; } }, { key: "visitGenerator", value: function visitGenerator(generator) { var _this5 = this; this._withScope(generator, function () { generator.visit(_this5.createGeneratorVisitCallbacks(generator, _this5.commonScope)); }); } }, { key: "_visitAdditionalFunction", value: function _visitAdditionalFunction(functionValue, additionalEffects, parentScope) { var _this6 = this; // Get Instance + Info (0, _invariant2.default)(functionValue instanceof _index2.ECMAScriptSourceFunctionValue); var code = functionValue.$ECMAScriptCode; var functionInfo = this.functionInfos.get(code); (0, _invariant2.default)(functionInfo !== undefined); var funcInstance = this.functionInstances.get(functionValue); (0, _invariant2.default)(funcInstance !== undefined); // Set Visitor state // Allows us to emit function declarations etc. inside of this additional // function instead of adding them at global scope var prevCommonScope = this.commonScope; this.commonScope = functionValue; var oldReactElementEquivalenceSet = this.reactElementEquivalenceSet; this.reactElementEquivalenceSet = new _ReactElementSet2.default(this.realm, this.equivalenceSet); var oldInAdditionalFunction = this.inAdditionalFunction; this.inAdditionalFunction = true; var prevReVisit = this.additionalRoots; this.additionalRoots = new Set(); var _visitAdditionalFunctionEffects = function _visitAdditionalFunctionEffects() { var effects = additionalEffects.effects; var _effects = _slicedToArray(effects, 5), result = _effects[0], generator = _effects[1], modifiedBindings = _effects[2], modifiedProperties = _effects[3], createdObjects = _effects[4]; // Need to do this fixup because otherwise we will skip over this function's // generator in the _getTarget scope lookup generator.parent = functionValue.parent; functionValue.parent = generator; // result -- ignore TODO: return the result from the function somehow // Generator -- visit all entries // Bindings -- (modifications to named variables) only need to serialize bindings if they're // captured by a residual function // -- need to apply them and maybe need to revisit functions in ancestors to make sure // we don't overwrite anything they capture // -- TODO: deal with these properly // PropertyBindings -- (property modifications) visit any property bindings to pre-existing objects // CreatedObjects -- should take care of itself _this6.realm.applyEffects([result, new _generator.Generator(_this6.realm), modifiedBindings, modifiedProperties, createdObjects]); var modifiedBindingInfo = new Map(); var visitPropertiesAndBindings = function visitPropertiesAndBindings() { var _iteratorNormalCompletion5 = true; var _didIteratorError5 = false; var _iteratorError5 = undefined; try { for (var _iterator5 = modifiedProperties.keys()[Symbol.iterator](), _step5; !(_iteratorNormalCompletion5 = (_step5 = _iterator5.next()).done); _iteratorNormalCompletion5 = true) { var propertyBinding = _step5.value; var binding = propertyBinding; var object = binding.object; if (object instanceof _index2.ObjectValue && createdObjects.has(object)) continue; // Created Object's binding if (object.refuseSerialization) continue; // modification to internal state if (object.intrinsicName === "global") continue; // Avoid double-counting _this6.visitObjectProperty(binding); } // Handing of ModifiedBindings } catch (err) { _didIteratorError5 = true; _iteratorError5 = err; } finally { try { if (!_iteratorNormalCompletion5 && _iterator5.return) { _iterator5.return(); } } finally { if (_didIteratorError5) { throw _iteratorError5; } } } var _loop = function _loop(additionalBinding, previousValue) { var modifiedBinding = additionalBinding; var residualBinding = void 0; _this6._withScope(functionValue, function () { // Also visit the original value of the binding residualBinding = _this6.visitBinding(functionValue, modifiedBinding.name); (0, _invariant2.default)(residualBinding !== undefined); // Fixup the binding to have the correct value // No previousValue means this is a binding for a nested function if (previousValue && previousValue.value) residualBinding.value = _this6.visitEquivalentValue(previousValue.value); (0, _invariant2.default)(functionInfo !== undefined); if (functionInfo.modified.has(modifiedBinding.name)) residualBinding.modified; }); (0, _invariant2.default)(residualBinding !== undefined); (0, _invariant2.default)(funcInstance !== undefined); funcInstance.residualFunctionBindings.set(modifiedBinding.name, residualBinding); var newValue = modifiedBinding.value; (0, _invariant2.default)(newValue); _this6.visitValue(newValue); residualBinding.modified = true; // This should be enforced by checkThatFunctionsAreIndependent (0, _invariant2.default)(!residualBinding.additionalFunctionOverridesValue, "We should only have one additional function value modifying any given residual binding"); if (previousValue && previousValue.value) residualBinding.additionalFunctionOverridesValue = functionValue; modifiedBindingInfo.set(modifiedBinding, residualBinding); }; var _iteratorNormalCompletion6 = true; var _didIteratorError6 = false; var _iteratorError6 = undefined; try { for (var _iterator6 = modifiedBindings[Symbol.iterator](), _step6; !(_iteratorNormalCompletion6 = (_step6 = _iterator6.next()).done); _iteratorNormalCompletion6 = true) { var _ref5 = _step6.value; var _ref6 = _slicedToArray(_ref5, 2); var additionalBinding = _ref6[0]; var previousValue = _ref6[1]; _loop(additionalBinding, previousValue); } } catch (err) { _didIteratorError6 = true; _iteratorError6 = err; } finally { try { if (!_iteratorNormalCompletion6 && _iterator6.return) { _iterator6.return(); } } finally { if (_didIteratorError6) { throw _iteratorError6; } } } (0, _invariant2.default)(result instanceof _index2.Value); if (!(result instanceof _index2.UndefinedValue)) _this6.visitValue(result); }; (0, _invariant2.default)(funcInstance !== undefined); (0, _invariant2.default)(functionInfo !== undefined); _this6.additionalFunctionValueInfos.set(functionValue, { functionValue: functionValue, captures: functionInfo.unbound, modifiedBindings: modifiedBindingInfo, instance: funcInstance }); _this6.visitGenerator(generator); // All modified properties and bindings should be accessible // from its containing additional function scope. _this6._withScope(functionValue, visitPropertiesAndBindings); // Remove any modifications to CreatedObjects -- these are fine being serialized inside the additional function _this6.additionalRoots = new Set([].concat(_toConsumableArray(_this6.additionalRoots)).filter(function (x) { return !createdObjects.has(x); })); _this6.realm.restoreBindings(modifiedBindings); _this6.realm.restoreProperties(modifiedProperties); return _this6.realm.intrinsics.undefined; }; this.realm.evaluateAndRevertInGlobalEnv(_visitAdditionalFunctionEffects); // Cleanup this.commonScope = prevCommonScope; this.reactElementEquivalenceSet = oldReactElementEquivalenceSet; this._withScope(parentScope, // Re-visit any bindings corresponding to unbound values or values closed over from outside additional function // they're serialized in the correct scope function () { (0, _invariant2.default)(functionInfo !== undefined); (0, _invariant2.default)(funcInstance !== undefined); var _iteratorNormalCompletion7 = true; var _didIteratorError7 = false; var _iteratorError7 = undefined; try { for (var _iterator7 = _this6.additionalRoots[Symbol.iterator](), _step7; !(_iteratorNormalCompletion7 = (_step7 = _iterator7.next()).done); _iteratorNormalCompletion7 = true) { var value = _step7.value; // Populate old additionalRoots because we switched them out prevReVisit.add(value); _this6.visitValue(value); } } catch (err) { _didIteratorError7 = true; _iteratorError7 = err; } finally { try { if (!_iteratorNormalCompletion7 && _iterator7.return) { _iterator7.return(); } } finally { if (_didIteratorError7) { throw _iteratorError7; } } } var _iteratorNormalCompletion8 = true; var _didIteratorError8 = false; var _iteratorError8 = undefined; try { for (var _iterator8 = functionInfo.unbound[Symbol.iterator](), _step8; !(_iteratorNormalCompletion8 = (_step8 = _iterator8.next()).done); _iteratorNormalCompletion8 = true) { var innerName = _step8.value; var _residualBinding = _this6.visitBinding(functionValue, innerName, false); if (_residualBinding) { _residualBinding.modified = true; funcInstance.residualFunctionBindings.set(innerName, _residualBinding); } } } catch (err) { _didIteratorError8 = true; _iteratorError8 = err; } finally { try { if (!_iteratorNormalCompletion8 && _iterator8.return) { _iterator8.return(); } } finally { if (_didIteratorError8) { throw _iteratorError8; } } } _this6.additionalRoots = prevReVisit; }); this.inAdditionalFunction = oldInAdditionalFunction; } }, { key: "visitRoots", value: function visitRoots() { var _this7 = this; var generator = this.realm.generator; (0, _invariant2.default)(generator); this.visitGenerator(generator); var _iteratorNormalCompletion9 = true; var _didIteratorError9 = false; var _iteratorError9 = undefined; try { for (var _iterator9 = this.modules.initializedModules.values()[Symbol.iterator](), _step9; !(_iteratorNormalCompletion9 = (_step9 = _iterator9.next()).done); _iteratorNormalCompletion9 = true) { var moduleValue = _step9.value; this.visitValue(moduleValue); } } catch (err) { _didIteratorError9 = true; _iteratorError9 = err; } finally { try { if (!_iteratorNormalCompletion9 && _iterator9.return) { _iterator9.return(); } } finally { if (_didIteratorError9) { throw _iteratorError9; } } } if (this.realm.react.enabled && this.shouldVisitReactLibrary) { this._visitReactLibrary(); } // Do a fixpoint over all pure generator entries to make sure that we visit // arguments of only BodyEntries that are required by some other residual value var oldDelayedEntries = []; while (oldDelayedEntries.length !== this.delayedVisitGeneratorEntries.length) { oldDelayedEntries = this.delayedVisitGeneratorEntries; this.delayedVisitGeneratorEntries = []; var _loop2 = function _loop2(_ref7) { var commonScope = _ref7.commonScope, entryGenerator = _ref7.generator, entry = _ref7.entry; _this7.commonScope = commonScope; _this7._withScope(entryGenerator, function () { entryGenerator.visitEntry(entry, _this7.createGeneratorVisitCallbacks(entryGenerator, commonScope)); }); }; var _iteratorNormalCompletion10 = true; var _didIteratorError10 = false; var _iteratorError10 = undefined; try { for (var _iterator10 = oldDelayedEntries[Symbol.iterator](), _step10; !(_iteratorNormalCompletion10 = (_step10 = _iterator10.next()).done); _iteratorNormalCompletion10 = true) { var _ref7 = _step10.value; _loop2(_ref7); } } catch (err) { _didIteratorError10 = true; _iteratorError10 = err; } finally { try { if (!_iteratorNormalCompletion10 && _iterator10.return) { _iterator10.return(); } } finally { if (_didIteratorError10) { throw _iteratorError10; } } } } // Artificially add additionalRoots to generators so that they can get serialized in parent scopes of additionalFunctions // if necessary. var _iteratorNormalCompletion11 = true; var _didIteratorError11 = false; var _iteratorError11 = undefined; try { for (var _iterator11 = this.additionalRoots[Symbol.iterator](), _step11; !(_iteratorNormalCompletion11 = (_step11 = _iterator11.next()).done); _iteratorNormalCompletion11 = true) { var value = _step11.value; var scopes = this.values.get(value); (0, _invariant2.default)(scopes); scopes = [].concat(_toConsumableArray(scopes)); (0, _invariant2.default)(scopes.length > 0); (0, _invariant2.default)(scopes[0]); var firstGenerator = scopes[0] instanceof _generator.Generator ? scopes[0] : scopes[0].getParent(); var commonAncestor = scopes.reduce(function (x, y) { return (0, _utils.commonAncestorOf)(x, y); }, firstGenerator); (0, _invariant2.default)(commonAncestor instanceof _generator.Generator); // every scope is either the root, or a descendant commonAncestor.appendRoots([value]); } } catch (err) { _didIteratorError11 = true; _iteratorError11 = err; } finally { try { if (!_iteratorNormalCompletion11 && _iterator11.return) { _iterator11.return(); } } finally { if (_didIteratorError11) { throw _iteratorError11; } } } } }, { key: "_visitReactLibrary", value: function _visitReactLibrary() { // find and visit the React library var reactLibraryObject = this.realm.react.reactLibraryObject; if (this.realm.react.output === "jsx") { // React might not be defined in scope, i.e. another library is using JSX // we don't throw an error as we should support JSX stand-alone if (reactLibraryObject !== undefined) { this.visitValue(reactLibraryObject); } } else if (this.realm.react.output === "create-element") { var throwError = function throwError() { throw new _errors.FatalError("unable to visit createElement due to React not being referenced in scope"); }; // createElement output needs React in scope if (reactLibraryObject === undefined) { throwError(); } (0, _invariant2.default)(reactLibraryObject instanceof _index2.ObjectValue); var createElement = reactLibraryObject.properties.get("createElement"); if (createElement === undefined || createElement.descriptor === undefined) { throwError(); } var reactCreateElement = (0, _index.Get)(this.realm, reactLibraryObject, "createElement"); this.visitValue(reactCreateElement); } } }]); return ResidualHeapVisitor; }(); //# sourceMappingURL=ResidualHeapVisitor.js.map