"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.ResidualHeapSerializer = 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 _realm = require("../realm.js"); var _index = require("../methods/index.js"); var _index2 = require("../values/index.js"); var _babelTypes = require("babel-types"); var t = _interopRequireWildcard(_babelTypes); var _generator = require("../utils/generator.js"); var _invariant = require("../invariant.js"); var _invariant2 = _interopRequireDefault(_invariant); var _types = require("./types.js"); var _logger = require("./logger.js"); var _modules = require("./modules.js"); var _ResidualHeapInspector = require("./ResidualHeapInspector.js"); var _ResidualFunctions = require("./ResidualFunctions.js"); var _factorify = require("./factorify.js"); var _internalizer = require("../utils/internalizer.js"); var _Emitter = require("./Emitter.js"); var _ResidualHeapValueIdentifiers = require("./ResidualHeapValueIdentifiers.js"); var _utils = require("./utils.js"); var _hoisting = require("../react/hoisting.js"); var _singletons = require("../singletons.js"); var _ResidualReactElements = require("./ResidualReactElements.js"); var _environment = require("../environment.js"); 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"); } } function commentStatement(text) { var s = t.emptyStatement(); s.leadingComments = [{ type: "BlockComment", value: text }]; return s; } var ResidualHeapSerializer = exports.ResidualHeapSerializer = function () { function ResidualHeapSerializer(realm, logger, modules, residualHeapValueIdentifiers, residualHeapInspector, residualValues, residualFunctionInstances, residualFunctionInfos, options, referencedDeclaredValues, additionalFunctionValuesAndEffects, additionalFunctionValueInfos, declarativeEnvironmentRecordsBindings, statistics, react) { var _this = this; _classCallCheck(this, ResidualHeapSerializer); this.realm = realm; this.logger = logger; this.modules = modules; this.residualHeapValueIdentifiers = residualHeapValueIdentifiers; this.statistics = statistics; this.react = react; var realmGenerator = this.realm.generator; (0, _invariant2.default)(realmGenerator); this.generator = realmGenerator; var realmPreludeGenerator = this.realm.preludeGenerator; (0, _invariant2.default)(realmPreludeGenerator); this.preludeGenerator = realmPreludeGenerator; this.prelude = []; this._descriptors = new Map(); this.needsEmptyVar = false; this.needsAuxiliaryConstructor = false; this.descriptorNameGenerator = this.preludeGenerator.createNameGenerator("$$"); this.factoryNameGenerator = this.preludeGenerator.createNameGenerator("$_"); this.intrinsicNameGenerator = this.preludeGenerator.createNameGenerator("$i_"); this.functionNameGenerator = this.preludeGenerator.createNameGenerator("$f_"); this.requireReturns = new Map(); this.serializedValues = new Set(); this._serializedValueWithIdentifiers = new Set(); this.additionalFunctionValueNestedFunctions = new Set(); this.residualReactElements = new _ResidualReactElements.ResidualReactElements(this.realm, this); this.residualFunctions = new _ResidualFunctions.ResidualFunctions(this.realm, this.statistics, options, this.modules, this.requireReturns, { getLocation: function getLocation(value) { return _this.getSerializeObjectIdentifier(value); }, createLocation: function createLocation() { var initializeConditionNameGenerator = _this.preludeGenerator.createNameGenerator("_initialized"); var location = t.identifier(initializeConditionNameGenerator.generate()); _this.currentFunctionBody.entries.push(t.variableDeclaration("var", [t.variableDeclarator(location)])); return location; } }, this.prelude, this.preludeGenerator.createNameGenerator("__init_"), this.factoryNameGenerator, this.preludeGenerator.createNameGenerator("__scope_"), this.preludeGenerator.createNameGenerator("$"), residualFunctionInfos, residualFunctionInstances, additionalFunctionValueInfos, this.additionalFunctionValueNestedFunctions); this.emitter = new _Emitter.Emitter(this.residualFunctions); this.mainBody = this.emitter.getBody(); this.currentFunctionBody = this.mainBody; this.residualHeapInspector = residualHeapInspector; this.residualValues = residualValues; this.residualFunctionInstances = residualFunctionInstances; this.residualFunctionInfos = residualFunctionInfos; this._options = options; this.referencedDeclaredValues = referencedDeclaredValues; this.activeGeneratorBodies = new Map(); this.additionalFunctionValuesAndEffects = additionalFunctionValuesAndEffects; this.additionalFunctionValueInfos = additionalFunctionValueInfos; this.declarativeEnvironmentRecordsBindings = declarativeEnvironmentRecordsBindings; } // if we're in an additional function we need to access both mainBody and the // additional function's body which will be currentFunctionBody. // function values nested in additional functions can't delay initializations // TODO: revisit this and fix additional functions to be capable of delaying initializations _createClass(ResidualHeapSerializer, [{ key: "_emitObjectProperties", // Configures all mutable aspects of an object, in particular: // symbols, properties, prototype. // For every created object that corresponds to a value, // this function should be invoked once. // Thus, as a side effect, we gather statistics here on all emitted objects. value: function _emitObjectProperties(obj) { var properties = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : obj.properties; var _this2 = this; var objectPrototypeAlreadyEstablished = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false; var cleanupDummyProperties = arguments[3]; var _loop = function _loop(symbol, propertyBinding) { (0, _invariant2.default)(propertyBinding); var desc = propertyBinding.descriptor; if (desc === undefined) return "continue"; //deleted _this2.emitter.emitNowOrAfterWaitingForDependencies(_this2._getDescriptorValues(desc).concat([symbol, obj]), function () { (0, _invariant2.default)(desc !== undefined); return _this2._emitProperty(obj, symbol, desc); }); }; //inject symbols 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]; var _ret = _loop(symbol, propertyBinding); if (_ret === "continue") continue; } // inject properties } catch (err) { _didIteratorError = true; _iteratorError = err; } finally { try { if (!_iteratorNormalCompletion && _iterator.return) { _iterator.return(); } } finally { if (_didIteratorError) { throw _iteratorError; } } } var _loop2 = function _loop2(key, propertyBinding) { (0, _invariant2.default)(propertyBinding); if (propertyBinding.pathNode !== undefined) return "continue"; // Property is assigned to inside loop var desc = propertyBinding.descriptor; if (desc === undefined) return "continue"; //deleted if (_this2.residualHeapInspector.canIgnoreProperty(obj, key)) return "continue"; (0, _invariant2.default)(desc !== undefined); _this2.emitter.emitNowOrAfterWaitingForDependencies(_this2._getDescriptorValues(desc).concat(obj), function () { (0, _invariant2.default)(desc !== undefined); return _this2._emitProperty(obj, key, desc, cleanupDummyProperties != null && cleanupDummyProperties.has(key)); }); }; var _iteratorNormalCompletion2 = true; var _didIteratorError2 = false; var _iteratorError2 = undefined; try { for (var _iterator2 = properties[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) { var _ref3 = _step2.value; var _ref4 = _slicedToArray(_ref3, 2); var key = _ref4[0]; var propertyBinding = _ref4[1]; var _ret2 = _loop2(key, propertyBinding); if (_ret2 === "continue") continue; } // 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.emitter.emitNowOrAfterWaitingForDependencies(this._getNestedAbstractValues(val, [obj]), function () { (0, _invariant2.default)(val instanceof _index2.AbstractValue); _this2._emitPropertiesWithComputedNames(obj, val); }); } } // prototype this._emitObjectPrototype(obj, objectPrototypeAlreadyEstablished); if (obj instanceof _index2.FunctionValue) this._emitConstructorPrototype(obj); this.statistics.objects++; this.statistics.objectProperties += obj.properties.size; } }, { key: "_emitObjectPrototype", value: function _emitObjectPrototype(obj, objectPrototypeAlreadyEstablished) { var _this3 = this; var kind = obj.getKind(); var proto = obj.$Prototype; if (objectPrototypeAlreadyEstablished) { // Emitting an assertion. This can be removed in the future, or put under a DEBUG flag. this.emitter.emitNowOrAfterWaitingForDependencies([proto, obj], function () { (0, _invariant2.default)(proto); var serializedProto = _this3.serializeValue(proto); var uid = _this3.getSerializeObjectIdentifier(obj); var fetchedPrototype = _this3.realm.isCompatibleWith(_this3.realm.MOBILE_JSC_VERSION) ? t.memberExpression(uid, _internalizer.protoExpression) : t.callExpression(_this3.preludeGenerator.memoizeReference("Object.getPrototypeOf"), [uid]); var condition = t.binaryExpression("!==", fetchedPrototype, serializedProto); var throwblock = t.blockStatement([t.throwStatement(t.newExpression(t.identifier("Error"), [t.stringLiteral("unexpected prototype")]))]); _this3.emitter.emit(t.ifStatement(condition, throwblock)); }); return; } if (proto === this.realm.intrinsics[kind + "Prototype"]) return; this.emitter.emitNowOrAfterWaitingForDependencies([proto, obj], function () { (0, _invariant2.default)(proto); var serializedProto = _this3.serializeValue(proto); var uid = _this3.getSerializeObjectIdentifier(obj); if (!_this3.realm.isCompatibleWith(_this3.realm.MOBILE_JSC_VERSION)) _this3.emitter.emit(t.expressionStatement(t.callExpression(_this3.preludeGenerator.memoizeReference("Object.setPrototypeOf"), [uid, serializedProto])));else { _this3.emitter.emit(t.expressionStatement(t.assignmentExpression("=", t.memberExpression(uid, _internalizer.protoExpression), serializedProto))); } }); } }, { key: "_emitConstructorPrototype", value: function _emitConstructorPrototype(func) { var _this4 = this; // 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 && this.residualValues.has(prototype)) { this.emitter.emitNowOrAfterWaitingForDependencies([func], function () { (0, _invariant2.default)(prototype instanceof _index2.Value); _this4.serializeValue(prototype); }); } } }, { key: "_getNestedAbstractValues", value: function _getNestedAbstractValues(absVal, values) { if (absVal.kind === "widened property") return values; (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]; values.push(P); var V = absVal.args[1]; values.push(V); var W = absVal.args[2]; if (W instanceof _index2.AbstractValue) this._getNestedAbstractValues(W, values);else values.push(W); } else { // conditional assignment values.push(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._getNestedAbstractValues(consequent, values); this._getNestedAbstractValues(alternate, values); } return values; } }, { key: "_emitPropertiesWithComputedNames", value: function _emitPropertiesWithComputedNames(obj, 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._emitPropertiesWithComputedNames(obj, earlier_props); var uid = this.getSerializeObjectIdentifier(obj); var serializedP = this.serializeValue(P); var serializedV = this.serializeValue(V); this.emitter.emit(t.expressionStatement(t.assignmentExpression("=", t.memberExpression(uid, serializedP, true), serializedV))); } else { // conditional assignment var serializedCond = this.serializeValue(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); var oldBody = this.emitter.beginEmitting("consequent", { type: "ConditionalAssignmentBranch", parentBody: undefined, entries: [] }, /*isChild*/true); this._emitPropertiesWithComputedNames(obj, consequent); var consequentBody = this.emitter.endEmitting("consequent", oldBody); var consequentStatement = t.blockStatement(consequentBody.entries); oldBody = this.emitter.beginEmitting("alternate", { type: "ConditionalAssignmentBranch", parentBody: undefined, entries: [] }, /*isChild*/true); this._emitPropertiesWithComputedNames(obj, alternate); var alternateBody = this.emitter.endEmitting("alternate", oldBody); var alternateStatement = t.blockStatement(alternateBody.entries); this.emitter.emit(t.ifStatement(serializedCond, consequentStatement, alternateStatement)); } } // Overridable. }, { key: "getSerializeObjectIdentifier", value: function getSerializeObjectIdentifier(val) { return this.residualHeapValueIdentifiers.getIdentifierAndIncrementReferenceCount(val); } }, { key: "_emitProperty", value: function _emitProperty(val, key, desc) { var _this5 = this; var deleteIfMightHaveBeenDeleted = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false; // Location for the property to be assigned to var locationFunction = function locationFunction() { var serializedKey = key instanceof _index2.SymbolValue ? _this5.serializeValue(key) : _this5.generator.getAsPropertyNameExpression(key); var computed = key instanceof _index2.SymbolValue || !t.isIdentifier(serializedKey); return t.memberExpression(_this5.getSerializeObjectIdentifier(val), serializedKey, computed); }; if (desc === undefined) { this._deleteProperty(locationFunction()); } else { this.emitter.emit(this.emitDefinePropertyBody(deleteIfMightHaveBeenDeleted, locationFunction, val, key, desc)); } } }, { key: "emitDefinePropertyBody", value: function emitDefinePropertyBody(deleteIfMightHaveBeenDeleted, locationFunction, val, key, desc) { var _this6 = this; if (desc.joinCondition) { var cond = this.serializeValue(desc.joinCondition); (0, _invariant2.default)(cond !== undefined); var trueBody = void 0; var falseBody = void 0; if (desc.descriptor1) trueBody = this.emitDefinePropertyBody(deleteIfMightHaveBeenDeleted, locationFunction, val, key, desc.descriptor1); if (desc.descriptor2) falseBody = this.emitDefinePropertyBody(deleteIfMightHaveBeenDeleted, locationFunction, val, key, desc.descriptor2); if (trueBody && falseBody) return t.ifStatement(cond, trueBody, falseBody); if (trueBody) return t.ifStatement(cond, trueBody); if (falseBody) return t.ifStatement(t.unaryExpression("!", cond), falseBody); (0, _invariant2.default)(false); } if (locationFunction !== undefined && this._canEmbedProperty(val, key, desc)) { var descValue = desc.value; (0, _invariant2.default)(descValue instanceof _index2.Value); (0, _invariant2.default)(!this.emitter.getReasonToWaitForDependencies([descValue, val]), "precondition of _emitProperty"); var mightHaveBeenDeleted = descValue.mightHaveBeenDeleted(); // The only case we do not need to remove the dummy property is array index property. return this._getPropertyAssignment(locationFunction, function () { (0, _invariant2.default)(descValue instanceof _index2.Value); return _this6.serializeValue(descValue); }, mightHaveBeenDeleted, deleteIfMightHaveBeenDeleted); } var body = []; var descProps = []; var boolKeys = ["enumerable", "configurable"]; var valKeys = []; if (!desc.get && !desc.set) { boolKeys.push("writable"); valKeys.push("value"); } else { valKeys.push("set", "get"); } var descriptorsKey = []; var _iteratorNormalCompletion3 = true; var _didIteratorError3 = false; var _iteratorError3 = undefined; try { for (var _iterator3 = boolKeys[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) { var boolKey = _step3.value; if (boolKey in desc) { var b = desc[boolKey]; (0, _invariant2.default)(b !== undefined); descProps.push(t.objectProperty(t.identifier(boolKey), t.booleanLiteral(b))); descriptorsKey.push(boolKey + ":" + b.toString()); } } } catch (err) { _didIteratorError3 = true; _iteratorError3 = err; } finally { try { if (!_iteratorNormalCompletion3 && _iterator3.return) { _iterator3.return(); } } finally { if (_didIteratorError3) { throw _iteratorError3; } } } descriptorsKey = descriptorsKey.join(","); var descriptorId = this._descriptors.get(descriptorsKey); if (descriptorId === undefined) { descriptorId = t.identifier(this.descriptorNameGenerator.generate(descriptorsKey)); var declar = t.variableDeclaration("var", [t.variableDeclarator(descriptorId, t.objectExpression(descProps))]); // The descriptors are used across all scopes, and thus must be declared in the prelude. this.prelude.push(declar); this._descriptors.set(descriptorsKey, descriptorId); } (0, _invariant2.default)(descriptorId !== undefined); var _iteratorNormalCompletion4 = true; var _didIteratorError4 = false; var _iteratorError4 = undefined; try { for (var _iterator4 = valKeys[Symbol.iterator](), _step4; !(_iteratorNormalCompletion4 = (_step4 = _iterator4.next()).done); _iteratorNormalCompletion4 = true) { var descKey = _step4.value; if (descKey in desc) { var _descValue = desc[descKey]; (0, _invariant2.default)(_descValue instanceof _index2.Value); if (_descValue instanceof _index2.UndefinedValue) { this.serializeValue(_descValue); continue; } (0, _invariant2.default)(!this.emitter.getReasonToWaitForDependencies([_descValue]), "precondition of _emitProperty"); body.push(t.assignmentExpression("=", t.memberExpression(descriptorId, t.identifier(descKey)), this.serializeValue(_descValue))); } } } catch (err) { _didIteratorError4 = true; _iteratorError4 = err; } finally { try { if (!_iteratorNormalCompletion4 && _iterator4.return) { _iterator4.return(); } } finally { if (_didIteratorError4) { throw _iteratorError4; } } } var serializedKey = key instanceof _index2.SymbolValue ? this.serializeValue(key) : this.generator.getAsPropertyNameExpression(key, /*canBeIdentifier*/false); (0, _invariant2.default)(!this.emitter.getReasonToWaitForDependencies([val]), "precondition of _emitProperty"); body.push(t.callExpression(this.preludeGenerator.memoizeReference("Object.defineProperty"), [this.getSerializeObjectIdentifier(val), serializedKey, descriptorId])); return t.expressionStatement(t.sequenceExpression(body)); } }, { key: "_serializeDeclarativeEnvironmentRecordBinding", value: function _serializeDeclarativeEnvironmentRecordBinding(residualFunctionBinding) { if (!residualFunctionBinding.serializedValue) { var value = residualFunctionBinding.value; (0, _invariant2.default)(value); (0, _invariant2.default)(residualFunctionBinding.declarativeEnvironmentRecord); // Set up binding identity before starting to serialize value. This is needed in case of recursive dependencies. residualFunctionBinding.referentialized = false; residualFunctionBinding.serializedValue = this.serializeValue(value); if (value.mightBeObject()) { // Increment ref count one more time to ensure that this object will be assigned a unique id. // This ensures that only once instance is created across all possible residual function invocations. this.residualHeapValueIdentifiers.incrementReferenceCount(value); } } } // Determine whether initialization code for a value should go into the main body, or a more specific initialization body. }, { key: "_getTarget", value: function _getTarget(val) { var _this7 = this; var scopes = this.residualValues.get(val); (0, _invariant2.default)(scopes !== undefined); // All relevant values were visited in at least one scope. (0, _invariant2.default)(scopes.size >= 1); // First, let's figure out from which function and generator scopes this value is referenced. var functionValues = []; var generators = []; var _iteratorNormalCompletion5 = true; var _didIteratorError5 = false; var _iteratorError5 = undefined; try { for (var _iterator5 = scopes[Symbol.iterator](), _step5; !(_iteratorNormalCompletion5 = (_step5 = _iterator5.next()).done); _iteratorNormalCompletion5 = true) { var scope = _step5.value; if (scope instanceof _index2.FunctionValue) functionValues.push(scope);else { (0, _invariant2.default)(scope instanceof _generator.Generator); if (scope === this.realm.generator) { // This value is used from the main generator scope. This means that we need to emit the value and its // initialization code into the main body, and cannot delay initialization. return { body: this.currentFunctionBody, description: "this.realm.generator" }; } generators.push(scope); } } } catch (err) { _didIteratorError5 = true; _iteratorError5 = err; } finally { try { if (!_iteratorNormalCompletion5 && _iterator5.return) { _iterator5.return(); } } finally { if (_didIteratorError5) { throw _iteratorError5; } } } if (generators.length === 0) { // This value is only referenced from residual functions. (0, _invariant2.default)(functionValues.length > 0); var additionalFunctionValuesAndEffects = this.additionalFunctionValuesAndEffects; var numAdditionalFunctionReferences = 0; // Make sure we don't delay things referenced by additional functions or nested functions if (additionalFunctionValuesAndEffects) { // flow forces me to do this var additionalFuncValuesAndEffects = additionalFunctionValuesAndEffects; numAdditionalFunctionReferences = functionValues.filter(function (funcValue) { return additionalFuncValuesAndEffects.has(funcValue) || _this7.additionalFunctionValueNestedFunctions.has(funcValue); }).length; } if (numAdditionalFunctionReferences > 0 || !this._options.delayInitializations || this._options.simpleClosures) { // We can just emit it into the current function body. return { body: this.currentFunctionBody, usedOnlyByAdditionalFunctions: numAdditionalFunctionReferences === functionValues.length, description: "this.currentFunctionBody" }; } else { // We can delay the initialization, and move it into a conditional code block in the residual functions! var _body = this.residualFunctions.residualFunctionInitializers.registerValueOnlyReferencedByResidualFunctions(functionValues, val); return { body: _body, usedOnlyByResidualFunctions: true, description: "delay_initializer" }; } } // This value is referenced from more than one generator or function. // We can emit the initialization of this value into the body associated with their common ancestor. var commonAncestor = Array.from(scopes).reduce(function (x, y) { return (0, _utils.commonAncestorOf)(x, y); }, generators[0]); (0, _invariant2.default)(commonAncestor instanceof _generator.Generator); // every scope is either the root, or a descendant var body = void 0; while (true) { if (commonAncestor === this.generator) { body = this.currentFunctionBody; } else { body = this.activeGeneratorBodies.get(commonAncestor); } if (body !== undefined) break; commonAncestor = commonAncestor.parent; (0, _invariant2.default)(commonAncestor !== undefined); } (0, _invariant2.default)(body !== undefined); return { body: body, commonAncestor: commonAncestor }; } }, { key: "_getValueDebugName", value: function _getValueDebugName(val) { var name = void 0; if (val instanceof _index2.FunctionValue) { name = val.getName(); } else { var id = this.residualHeapValueIdentifiers.getIdentifier(val); (0, _invariant2.default)(id); name = id.name; } return name; } }, { key: "serializeBinding", value: function serializeBinding(binding) { var record = binding.environment; (0, _invariant2.default)(record instanceof _environment.DeclarativeEnvironmentRecord, "only declarative environments has bindings"); var residualFunctionBindings = this.declarativeEnvironmentRecordsBindings.get(record); (0, _invariant2.default)(residualFunctionBindings, "all bindings that create abstract values must have at least one call emitted to the generator so the function environment should have been visited"); var residualBinding = residualFunctionBindings.get(binding.name); (0, _invariant2.default)(residualBinding, "any referenced residual binding should have been visited"); if (!residualBinding.referentialized) { var additionalFunction = residualBinding.referencedOnlyFromAdditionalFunctions; (0, _invariant2.default)(additionalFunction, "residual bindings like this are only caused by leaked bindings in pure functions"); var instance = this.residualFunctionInstances.get(additionalFunction); (0, _invariant2.default)(instance, "any serialized function must exist in the scope"); this.residualFunctions.referentializer.referentializeBinding(residualBinding, binding.name, instance); } (0, _invariant2.default)(residualBinding.serializedValue); return residualBinding.serializedValue; } }, { key: "serializeValue", value: function serializeValue(val, referenceOnly, bindingType) { (0, _invariant2.default)(!val.refuseSerialization); if (val instanceof _index2.AbstractValue) { if (val.kind === "widened") { this.serializedValues.add(val); var name = val.intrinsicName; (0, _invariant2.default)(name !== undefined); return t.identifier(name); } else if (val.kind === "widened property") { this.serializedValues.add(val); return this._serializeAbstractValueHelper(val); } } if (this._serializedValueWithIdentifiers.has(val)) { return this.getSerializeObjectIdentifier(val); } this.serializedValues.add(val); if (!referenceOnly && _ResidualHeapInspector.ResidualHeapInspector.isLeaf(val)) { var res = this._serializeValue(val); (0, _invariant2.default)(res !== undefined); return res; } this._serializedValueWithIdentifiers.add(val); var target = this._getTarget(val); var oldBody = this.emitter.beginEmitting(val, target.body); var init = this._serializeValue(val); var id = this.residualHeapValueIdentifiers.getIdentifier(val); var result = id; this.residualHeapValueIdentifiers.incrementReferenceCount(val); if (this.residualHeapValueIdentifiers.needsIdentifier(val)) { if (init) { if (this._options.debugScopes) { var scopes = this.residualValues.get(val); (0, _invariant2.default)(scopes !== undefined); var scopeList = Array.from(scopes).map(function (s) { return "\"" + s.getName() + "\""; }).join(","); var comment = this._getValueDebugName(val) + " referenced from scopes [" + scopeList + "]"; if (target.commonAncestor !== undefined) comment = comment + " with common ancestor: " + target.commonAncestor.getName(); if (target.description !== undefined) comment = comment + " => " + target.description + " "; this.emitter.emit(commentStatement(comment)); } if (init !== id) { if (target.usedOnlyByResidualFunctions) { var declar = t.variableDeclaration(bindingType ? bindingType : "var", [t.variableDeclarator(id)]); this.mainBody.entries.push(declar); var assignment = t.expressionStatement(t.assignmentExpression("=", id, init)); this.emitter.emit(assignment); } else { var _declar = t.variableDeclaration(bindingType ? bindingType : "var", [t.variableDeclarator(id, init)]); this.emitter.emit(_declar); } } this.statistics.valueIds++; if (target.usedOnlyByResidualFunctions) this.statistics.delayedValues++; } } else { if (init) { this.residualHeapValueIdentifiers.deleteIdentifier(val); result = init; this.statistics.valuesInlined++; } } this.emitter.endEmitting(val, oldBody); return result; } }, { key: "_serializeValueIntrinsic", value: function _serializeValueIntrinsic(val) { var intrinsicName = val.intrinsicName; (0, _invariant2.default)(intrinsicName); if (val instanceof _index2.ObjectValue && val.intrinsicNameGenerated) { // The intrinsic was generated at a particular point in time. return this.preludeGenerator.convertStringToMember(intrinsicName); } else { // The intrinsic conceptually exists ahead of time. (0, _invariant2.default)(this.emitter.getBody() === this.currentFunctionBody); return this.preludeGenerator.memoizeReference(intrinsicName); } } }, { key: "_getDescriptorValues", value: function _getDescriptorValues(desc) { if (desc.joinCondition !== undefined) return [desc.joinCondition]; (0, _invariant2.default)(desc.value === undefined || desc.value instanceof _index2.Value); if (desc.value !== undefined) return [desc.value]; (0, _invariant2.default)(desc.get !== undefined); (0, _invariant2.default)(desc.set !== undefined); return [desc.get, desc.set]; } }, { key: "_deleteProperty", value: function _deleteProperty(location) { (0, _invariant2.default)(location.type === "MemberExpression"); this.emitter.emit(t.expressionStatement(t.unaryExpression("delete", location, true))); } }, { key: "_assignProperty", value: function _assignProperty(locationFn, valueFn, mightHaveBeenDeleted) { var deleteIfMightHaveBeenDeleted = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false; this.emitter.emit(this._getPropertyAssignment(locationFn, valueFn, mightHaveBeenDeleted, deleteIfMightHaveBeenDeleted)); } }, { key: "_getPropertyAssignment", value: function _getPropertyAssignment(locationFn, valueFn, mightHaveBeenDeleted) { var deleteIfMightHaveBeenDeleted = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false; var location = locationFn(); var value = valueFn(); var assignment = t.expressionStatement(t.assignmentExpression("=", location, value)); if (mightHaveBeenDeleted) { var condition = t.binaryExpression("!==", value, this.serializeValue(this.realm.intrinsics.empty)); var deletion = null; if (deleteIfMightHaveBeenDeleted) { (0, _invariant2.default)(location.type === "MemberExpression"); deletion = t.expressionStatement(t.unaryExpression("delete", location, true)); } return t.ifStatement(condition, assignment, deletion); } else { return assignment; } } }, { key: "_serializeArrayIndexProperties", value: function _serializeArrayIndexProperties(array, indexPropertyLength, remainingProperties) { var elems = []; for (var i = 0; i < indexPropertyLength; i++) { var key = i + ""; var propertyBinding = remainingProperties.get(key); var elem = null; // "propertyBinding === undefined" means array has a hole in the middle. if (propertyBinding !== undefined) { var descriptor = propertyBinding.descriptor; // "descriptor === undefined" means this array item has been deleted. if (descriptor !== undefined && descriptor.value !== undefined && this._canEmbedProperty(array, key, descriptor)) { var elemVal = descriptor.value; (0, _invariant2.default)(elemVal instanceof _index2.Value); var mightHaveBeenDeleted = elemVal.mightHaveBeenDeleted(); var delayReason = this.emitter.getReasonToWaitForDependencies(elemVal) || this.emitter.getReasonToWaitForActiveValue(array, mightHaveBeenDeleted); if (!delayReason) { elem = this.serializeValue(elemVal); remainingProperties.delete(key); } } } elems.push(elem); } return elems; } }, { key: "_serializeArrayLengthIfNeeded", value: function _serializeArrayLengthIfNeeded(val, numberOfIndexProperties, remainingProperties) { var _this8 = this; var realm = this.realm; var lenProperty = (0, _index.Get)(realm, val, "length"); // Need to serialize length property if: // 1. array length is abstract. // 2. array length is concrete, but different from number of index properties // we put into initialization list. if (lenProperty instanceof _index2.AbstractValue || _singletons.To.ToLength(realm, lenProperty) !== numberOfIndexProperties) { if (!(lenProperty instanceof _index2.AbstractValue) || lenProperty.kind !== "widened property") { this.emitter.emitNowOrAfterWaitingForDependencies([val], function () { _this8._assignProperty(function () { return t.memberExpression(_this8.getSerializeObjectIdentifier(val), t.identifier("length")); }, function () { return _this8.serializeValue(lenProperty); }, false /*mightHaveBeenDeleted*/ ); }); } remainingProperties.delete("length"); } } }, { key: "_serializeValueArray", value: function _serializeValueArray(val) { var remainingProperties = new Map(val.properties); var indexPropertyLength = (0, _utils.getSuggestedArrayLiteralLength)(this.realm, val); // Use the serialized index properties as array initialization list. var initProperties = this._serializeArrayIndexProperties(val, indexPropertyLength, remainingProperties); this._serializeArrayLengthIfNeeded(val, indexPropertyLength, remainingProperties); this._emitObjectProperties(val, remainingProperties); return t.arrayExpression(initProperties); } }, { key: "_serializeValueMap", value: function _serializeValueMap(val) { var _this9 = this; var kind = val.getKind(); var elems = []; 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; var mapConstructorDoesntTakeArguments = this.realm.isCompatibleWith(this.realm.MOBILE_JSC_VERSION); var _loop3 = function _loop3(i) { var entry = entries[i]; var key = entry.$Key; var value = entry.$Value; if (key === undefined || value === undefined) return "continue"; var mightHaveBeenDeleted = key.mightHaveBeenDeleted(); var delayReason = _this9.emitter.getReasonToWaitForDependencies(key) || _this9.emitter.getReasonToWaitForDependencies(value) || _this9.emitter.getReasonToWaitForActiveValue(val, mightHaveBeenDeleted || mapConstructorDoesntTakeArguments); if (delayReason) { _this9.emitter.emitAfterWaiting(delayReason, [key, value, val], function () { (0, _invariant2.default)(key !== undefined); (0, _invariant2.default)(value !== undefined); _this9.emitter.emit(t.expressionStatement(t.callExpression(t.memberExpression(_this9.residualHeapValueIdentifiers.getIdentifierAndIncrementReferenceCount(val), t.identifier("set")), [_this9.serializeValue(key), _this9.serializeValue(value)]))); }); } else { var serializedKey = _this9.serializeValue(key); var serializedValue = _this9.serializeValue(value); var elem = t.arrayExpression([serializedKey, serializedValue]); elems.push(elem); } }; for (var i = 0; i < len; i++) { var _ret3 = _loop3(i); if (_ret3 === "continue") continue; } this._emitObjectProperties(val); var args = elems.length > 0 ? [t.arrayExpression(elems)] : []; return t.newExpression(this.preludeGenerator.memoizeReference(kind), args); } }, { key: "_serializeValueSet", value: function _serializeValueSet(val) { var _this10 = this; var kind = val.getKind(); var elems = []; 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; var setConstructorDoesntTakeArguments = this.realm.isCompatibleWith(this.realm.MOBILE_JSC_VERSION); var _loop4 = function _loop4(i) { var entry = entries[i]; if (entry === undefined) return "continue"; var mightHaveBeenDeleted = entry.mightHaveBeenDeleted(); var delayReason = _this10.emitter.getReasonToWaitForDependencies(entry) || _this10.emitter.getReasonToWaitForActiveValue(val, mightHaveBeenDeleted || setConstructorDoesntTakeArguments); if (delayReason) { _this10.emitter.emitAfterWaiting(delayReason, [entry, val], function () { (0, _invariant2.default)(entry !== undefined); _this10.emitter.emit(t.expressionStatement(t.callExpression(t.memberExpression(_this10.residualHeapValueIdentifiers.getIdentifierAndIncrementReferenceCount(val), t.identifier("add")), [_this10.serializeValue(entry)]))); }); } else { var elem = _this10.serializeValue(entry); elems.push(elem); } }; for (var i = 0; i < len; i++) { var _ret4 = _loop4(i); if (_ret4 === "continue") continue; } this._emitObjectProperties(val); var args = elems.length > 0 ? [t.arrayExpression(elems)] : []; return t.newExpression(this.preludeGenerator.memoizeReference(kind), args); } }, { key: "_serializeValueTypedArrayOrDataView", value: function _serializeValueTypedArrayOrDataView(val) { var buf = val.$ViewedArrayBuffer; (0, _invariant2.default)(buf !== undefined); var outlinedArrayBuffer = this.serializeValue(buf, true); this._emitObjectProperties(val); return t.newExpression(this.preludeGenerator.memoizeReference(val.getKind()), [outlinedArrayBuffer]); } }, { key: "_serializeValueArrayBuffer", value: function _serializeValueArrayBuffer(val) { var elems = []; var len = val.$ArrayBufferByteLength; var db = val.$ArrayBufferData; (0, _invariant2.default)(len !== undefined); (0, _invariant2.default)(db); var allzero = true; for (var i = 0; i < len; i++) { if (db[i] !== 0) { allzero = false; } var elem = t.numericLiteral(db[i]); elems.push(elem); } this._emitObjectProperties(val); if (allzero) { // if they're all zero, just emit the array buffer constructor return t.newExpression(this.preludeGenerator.memoizeReference(val.getKind()), [t.numericLiteral(len)]); } else { // initialize from a byte array otherwise var arrayValue = t.arrayExpression(elems); var consExpr = t.newExpression(this.preludeGenerator.memoizeReference("Uint8Array"), [arrayValue]); // access the Uint8Array.buffer property to extract the created buffer return t.memberExpression(consExpr, t.identifier("buffer")); } } }, { key: "_serializeValueFunction", value: function _serializeValueFunction(val) { var _this11 = this; if (val instanceof _index2.BoundFunctionValue) { this._emitObjectProperties(val); return t.callExpression(t.memberExpression(this.serializeValue(val.$BoundTargetFunction), t.identifier("bind")), [].concat(this.serializeValue(val.$BoundThis), val.$BoundArguments.map(function (boundArg, i) { return _this11.serializeValue(boundArg); }))); } (0, _invariant2.default)(!(val instanceof _index2.NativeFunctionValue), "all native function values should be intrinsics"); (0, _invariant2.default)(val instanceof _index2.ECMAScriptSourceFunctionValue); var instance = this.residualFunctionInstances.get(val); (0, _invariant2.default)(instance); var residualBindings = instance.residualFunctionBindings; var inAdditionalFunction = this.currentFunctionBody !== this.mainBody; if (inAdditionalFunction) instance.containingAdditionalFunction = this.currentAdditionalFunction; var delayed = 1; var undelay = function undelay() { if (--delayed === 0) { (0, _invariant2.default)(instance); // hoist if we are in an additionalFunction if (_this11.currentFunctionBody !== _this11.mainBody && (0, _hoisting.canHoistFunction)(_this11.realm, val)) { instance.insertionPoint = new _types.BodyReference(_this11.mainBody, _this11.mainBody.entries.length); instance.containingAdditionalFunction = undefined; } else { instance.insertionPoint = _this11.emitter.getBodyReference(); } } }; var _loop5 = function _loop5(boundName, residualBinding) { var referencedValues = []; var serializeBindingFunc = void 0; if (!residualBinding.declarativeEnvironmentRecord) { serializeBindingFunc = function serializeBindingFunc() { return _this11._serializeGlobalBinding(boundName, residualBinding); }; } else { serializeBindingFunc = function serializeBindingFunc() { return _this11._serializeDeclarativeEnvironmentRecordBinding(residualBinding); }; var bindingValue = residualBinding.value; (0, _invariant2.default)(bindingValue !== undefined); referencedValues.push(bindingValue); if (inAdditionalFunction) { var _getTarget2 = _this11._getTarget(bindingValue), _usedOnlyByAdditionalFunctions = _getTarget2.usedOnlyByAdditionalFunctions; if (_usedOnlyByAdditionalFunctions) residualBinding.referencedOnlyFromAdditionalFunctions = _this11.currentAdditionalFunction; } } delayed++; _this11.emitter.emitNowOrAfterWaitingForDependencies(referencedValues, function () { serializeBindingFunc(); undelay(); }); }; var _iteratorNormalCompletion6 = true; var _didIteratorError6 = false; var _iteratorError6 = undefined; try { for (var _iterator6 = residualBindings[Symbol.iterator](), _step6; !(_iteratorNormalCompletion6 = (_step6 = _iterator6.next()).done); _iteratorNormalCompletion6 = true) { var _ref5 = _step6.value; var _ref6 = _slicedToArray(_ref5, 2); var boundName = _ref6[0]; var residualBinding = _ref6[1]; _loop5(boundName, residualBinding); } } catch (err) { _didIteratorError6 = true; _iteratorError6 = err; } finally { try { if (!_iteratorNormalCompletion6 && _iterator6.return) { _iterator6.return(); } } finally { if (_didIteratorError6) { throw _iteratorError6; } } } undelay(); this._emitObjectProperties(val); } // Checks whether a property can be defined via simple assignment, or using object literal syntax. }, { key: "_canEmbedProperty", value: function _canEmbedProperty(obj, key, prop) { if (prop.joinCondition !== undefined) return false; if (obj instanceof _index2.FunctionValue && key === "prototype" || obj.getKind() === "RegExp" && key === "lastIndex") return !!prop.writable && !prop.configurable && !prop.enumerable && !prop.set && !prop.get;else if (!!prop.writable && !!prop.configurable && !!prop.enumerable && !prop.set && !prop.get) { return !(prop.value instanceof _index2.AbstractValue && prop.value.kind === "widened property"); } else { return false; } } }, { key: "_findLastObjectPrototype", value: function _findLastObjectPrototype(obj) { while (obj.$Prototype instanceof _index2.ObjectValue) { obj = obj.$Prototype; }return obj; } }, { key: "_serializeValueRegExpObject", value: function _serializeValueRegExpObject(val) { var source = val.$OriginalSource; var flags = val.$OriginalFlags; (0, _invariant2.default)(typeof source === "string"); (0, _invariant2.default)(typeof flags === "string"); this._emitObjectProperties(val); source = new RegExp(source).source; // add escapes as per 21.2.3.2.4 return t.regExpLiteral(source, flags); } // Overridable. }, { key: "serializeValueRawObject", value: function serializeValueRawObject(val) { var remainingProperties = new Map(val.properties); var dummyProperties = new Set(); var props = []; var _iteratorNormalCompletion7 = true; var _didIteratorError7 = false; var _iteratorError7 = undefined; try { for (var _iterator7 = val.properties[Symbol.iterator](), _step7; !(_iteratorNormalCompletion7 = (_step7 = _iterator7.next()).done); _iteratorNormalCompletion7 = true) { var _ref7 = _step7.value; var _ref8 = _slicedToArray(_ref7, 2); var _key = _ref8[0]; var propertyBinding = _ref8[1]; if (propertyBinding.pathNode !== undefined) continue; // written to inside loop var descriptor = propertyBinding.descriptor; if (descriptor === undefined || descriptor.value === undefined) continue; // deleted if (this._canEmbedProperty(val, _key, descriptor)) { var propValue = descriptor.value; (0, _invariant2.default)(propValue instanceof _index2.Value); if (this.residualHeapInspector.canIgnoreProperty(val, _key)) continue; var mightHaveBeenDeleted = propValue.mightHaveBeenDeleted(); var serializedKey = this.generator.getAsPropertyNameExpression(_key); var _delayReason = this.emitter.getReasonToWaitForDependencies(propValue) || this.emitter.getReasonToWaitForActiveValue(val, mightHaveBeenDeleted); // Although the property needs to be delayed, we still want to emit dummy "undefined" // value as part of the object literal to ensure a consistent property ordering. var serializedValue = _internalizer.voidExpression; if (_delayReason) { // May need to be cleaned up later. dummyProperties.add(_key); } else { remainingProperties.delete(_key); serializedValue = this.serializeValue(propValue); } props.push(t.objectProperty(serializedKey, serializedValue)); } else if (descriptor.value instanceof _index2.Value && descriptor.value.mightHaveBeenDeleted()) { dummyProperties.add(_key); var _serializedKey = this.generator.getAsPropertyNameExpression(_key); props.push(t.objectProperty(_serializedKey, _internalizer.voidExpression)); } } } catch (err) { _didIteratorError7 = true; _iteratorError7 = err; } finally { try { if (!_iteratorNormalCompletion7 && _iterator7.return) { _iterator7.return(); } } finally { if (_didIteratorError7) { throw _iteratorError7; } } } this._emitObjectProperties(val, remainingProperties, /*objectPrototypeAlreadyEstablished*/false, dummyProperties); return t.objectExpression(props); } }, { key: "_serializeValueObjectViaConstructor", value: function _serializeValueObjectViaConstructor(val) { this._emitObjectProperties(val, val.properties, /*objectPrototypeAlreadyEstablished*/true); this.needsAuxiliaryConstructor = true; var serializedProto = this.serializeValue(val.$Prototype); return t.sequenceExpression([t.assignmentExpression("=", t.memberExpression(_internalizer.constructorExpression, t.identifier("prototype")), serializedProto), t.newExpression(_internalizer.constructorExpression, [])]); } }, { key: "serializeValueObject", value: function serializeValueObject(val) { var _this12 = this; // 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) { var prototypeId = this.residualHeapValueIdentifiers.getIdentifier(val); this.emitter.emitNowOrAfterWaitingForDependencies([constructor], function () { (0, _invariant2.default)(constructor !== undefined); (0, _invariant2.default)(prototypeId !== undefined); _this12.serializeValue(constructor); _this12._emitObjectProperties(val); (0, _invariant2.default)(prototypeId.type === "Identifier"); _this12.residualFunctions.setFunctionPrototype(constructor, prototypeId); }); return prototypeId; } var kind = val.getKind(); switch (kind) { case "RegExp": return this._serializeValueRegExpObject(val); case "Number": var numberData = val.$NumberData; (0, _invariant2.default)(numberData !== undefined); numberData.throwIfNotConcreteNumber(); (0, _invariant2.default)(numberData instanceof _index2.NumberValue, "expected number data internal slot to be a number value"); this._emitObjectProperties(val); return t.newExpression(this.preludeGenerator.memoizeReference("Number"), [t.numericLiteral(numberData.value)]); case "String": var stringData = val.$StringData; (0, _invariant2.default)(stringData !== undefined); stringData.throwIfNotConcreteString(); (0, _invariant2.default)(stringData instanceof _index2.StringValue, "expected string data internal slot to be a string value"); this._emitObjectProperties(val); return t.newExpression(this.preludeGenerator.memoizeReference("String"), [t.stringLiteral(stringData.value)]); case "Boolean": var booleanData = val.$BooleanData; (0, _invariant2.default)(booleanData !== undefined); booleanData.throwIfNotConcreteBoolean(); (0, _invariant2.default)(booleanData instanceof _index2.BooleanValue, "expected boolean data internal slot to be a boolean value"); this._emitObjectProperties(val); return t.newExpression(this.preludeGenerator.memoizeReference("Boolean"), [t.booleanLiteral(booleanData.value)]); case "Date": var dateValue = val.$DateValue; (0, _invariant2.default)(dateValue !== undefined); var serializedDateValue = this.serializeValue(dateValue); this._emitObjectProperties(val); return t.newExpression(this.preludeGenerator.memoizeReference("Date"), [serializedDateValue]); case "Float32Array": case "Float64Array": case "Int8Array": case "Int16Array": case "Int32Array": case "Uint8Array": case "Uint16Array": case "Uint32Array": case "Uint8ClampedArray": case "DataView": return this._serializeValueTypedArrayOrDataView(val); case "ArrayBuffer": return this._serializeValueArrayBuffer(val); case "ReactElement": this.residualReactElements.serializeReactElement(val); return; case "Map": case "WeakMap": return this._serializeValueMap(val); case "Set": case "WeakSet": return this._serializeValueSet(val); default: (0, _invariant2.default)(kind === "Object", "invariant established by visitor"); (0, _invariant2.default)(this.$ParameterMap === undefined, "invariant established by visitor"); var proto = val.$Prototype; var createViaAuxiliaryConstructor = proto !== this.realm.intrinsics.ObjectPrototype && this._findLastObjectPrototype(val) === this.realm.intrinsics.ObjectPrototype && proto instanceof _index2.ObjectValue; return createViaAuxiliaryConstructor ? this._serializeValueObjectViaConstructor(val) : this.serializeValueRawObject(val); } } }, { key: "_serializeValueSymbol", value: function _serializeValueSymbol(val) { var args = []; if (val.$Description instanceof _index2.Value) { var serializedArg = this.serializeValue(val.$Description); (0, _invariant2.default)(serializedArg); args.push(serializedArg); } // check if symbol value exists in the global symbol map, in that case we emit an invocation of System.for // to look it up var globalReg = this.realm.globalSymbolRegistry.find(function (e) { return e.$Symbol === val; }) !== undefined; if (globalReg) { return t.callExpression(this.preludeGenerator.memoizeReference("Symbol.for"), args); } else { return t.callExpression(this.preludeGenerator.memoizeReference("Symbol"), args); } } }, { key: "_serializeValueProxy", value: function _serializeValueProxy(val) { return t.newExpression(this.preludeGenerator.memoizeReference("Proxy"), [this.serializeValue(val.$ProxyTarget), this.serializeValue(val.$ProxyHandler)]); } }, { key: "_serializeAbstractValueHelper", value: function _serializeAbstractValueHelper(val) { var _this13 = this; var serializedArgs = val.args.map(function (abstractArg, i) { return _this13.serializeValue(abstractArg); }); if (val.kind === "abstractConcreteUnion") { var abstractIndex = val.args.findIndex(function (v) { return v instanceof _index2.AbstractValue; }); (0, _invariant2.default)(abstractIndex >= 0 && abstractIndex < val.args.length); return serializedArgs[abstractIndex]; } var serializedValue = val.buildNode(serializedArgs); if (serializedValue.type === "Identifier") { var id = serializedValue; (0, _invariant2.default)(!this.preludeGenerator.derivedIds.has(id.name) || this.emitter.hasBeenDeclared(val)); } return serializedValue; } }, { key: "_serializeAbstractValue", value: function _serializeAbstractValue(val) { var _this14 = this; (0, _invariant2.default)(val.kind !== "sentinel member expression", "invariant established by visitor"); if (val.hasIdentifier()) { return this._serializeAbstractValueHelper(val); } else { // This abstract value's dependencies should all be declared // but still need to check them again in case their serialized bodies are in different generator scope. this.emitter.emitNowOrAfterWaitingForDependencies(val.args, function () { var serializedValue = _this14._serializeAbstractValueHelper(val); var uid = _this14.getSerializeObjectIdentifier(val); var declar = t.variableDeclaration("var", [t.variableDeclarator(uid, serializedValue)]); _this14.emitter.emit(declar); }); } } }, { key: "_serializeValue", value: function _serializeValue(val) { if (val instanceof _index2.AbstractValue) { return this._serializeAbstractValue(val); } else if (val.isIntrinsic()) { return this._serializeValueIntrinsic(val); } else if (val instanceof _index2.EmptyValue) { this.needsEmptyVar = true; return _internalizer.emptyExpression; } else if (val instanceof _index2.UndefinedValue) { return _internalizer.voidExpression; } else if (_ResidualHeapInspector.ResidualHeapInspector.isLeaf(val)) { return t.valueToNode(val.serialize()); } else if ((0, _index.IsArray)(this.realm, val)) { (0, _invariant2.default)(val instanceof _index2.ObjectValue); return this._serializeValueArray(val); } else if (val instanceof _index2.ProxyValue) { return this._serializeValueProxy(val); } else if (val instanceof _index2.FunctionValue) { return this._serializeValueFunction(val); } else if (val instanceof _index2.SymbolValue) { return this._serializeValueSymbol(val); } else { (0, _invariant2.default)(val instanceof _index2.ObjectValue); return this.serializeValueObject(val); } } }, { key: "_serializeGlobalBinding", value: function _serializeGlobalBinding(boundName, residualFunctionBinding) { (0, _invariant2.default)(!residualFunctionBinding.declarativeEnvironmentRecord); if (!residualFunctionBinding.serializedValue) { residualFunctionBinding.referentialized = true; if (boundName === "undefined") { residualFunctionBinding.serializedValue = _internalizer.voidExpression; } else { var _value = this.realm.getGlobalLetBinding(boundName); // Check for let binding vs global property if (_value) { var rval = residualFunctionBinding.value; (0, _invariant2.default)(rval !== undefined && _value.equals(rval)); var id = this.serializeValue(rval, true, "let"); // increment ref count one more time as the value has been // referentialized (stored in a variable) by serializeValue this.residualHeapValueIdentifiers.incrementReferenceCount(rval); residualFunctionBinding.serializedValue = id; } else { residualFunctionBinding.serializedValue = this.preludeGenerator.globalReference(boundName); } } } } }, { key: "_withGeneratorScope", value: function _withGeneratorScope(generator, callback) { var newBody = { type: "Generator", parentBody: undefined, entries: [] }; var oldBody = this.emitter.beginEmitting(generator, newBody, /*isChild*/true); this.activeGeneratorBodies.set(generator, newBody); callback(newBody); this.activeGeneratorBodies.delete(generator); var statements = this.emitter.endEmitting(generator, oldBody).entries; if (this._options.debugScopes) { var comment = "generator \"" + generator.getName() + "\""; if (generator.parent !== undefined) { comment = comment + " with parent \"" + generator.parent.getName() + "\""; } statements.unshift(commentStatement("begin " + comment)); statements.push(commentStatement("end " + comment)); } return statements; } }, { key: "_getContext", value: function _getContext() { var _this15 = this; // TODO #482: Values serialized by nested generators would currently only get defined // along the code of the nested generator; their definitions need to get hoisted // or repeated so that they are accessible and defined from all using scopes var context = { serializeValue: this.serializeValue.bind(this), serializeBinding: this.serializeBinding.bind(this), serializeGenerator: function serializeGenerator(generator) { return _this15._withGeneratorScope(generator, function () { return generator.serialize(context); }); }, emit: function emit(statement) { _this15.emitter.emit(statement); }, emitDefinePropertyBody: this.emitDefinePropertyBody.bind(this, false, undefined), canOmit: function canOmit(value) { return !_this15.referencedDeclaredValues.has(value); }, declare: function declare(value) { _this15.emitter.declare(value); } }; return context; } }, { key: "_serializeAdditionalFunction", value: function _serializeAdditionalFunction(generator, postGeneratorCallback) { var _this16 = this; var context = this._getContext(); return this._withGeneratorScope(generator, function (newBody) { var oldCurBody = _this16.currentFunctionBody; _this16.currentFunctionBody = newBody; generator.serialize(context); if (postGeneratorCallback) postGeneratorCallback(); _this16.currentFunctionBody = oldCurBody; }); } }, { key: "_shouldBeWrapped", value: function _shouldBeWrapped(body) { for (var i = 0; i < body.length; i++) { var item = body[i]; if (item.type === "ExpressionStatement") { continue; } else if (item.type === "VariableDeclaration" || item.type === "FunctionDeclaration") { return true; } else if (item.type === "BlockStatement") { if (this._shouldBeWrapped(item.body)) { return true; } } else if (item.type === "IfStatement") { if (item.alternate) { if (this._shouldBeWrapped(item.alternate.body)) { return true; } } if (item.consequent) { if (this._shouldBeWrapped(item.consequent.body)) { return true; } } } } return false; } }, { key: "processAdditionalFunctionValues", value: function processAdditionalFunctionValues() { var _this17 = this; var rewrittenAdditionalFunctions = new Map(); var shouldEmitLog = !this.residualHeapValueIdentifiers.collectValToRefCountOnly; var processAdditionalFunctionValuesFn = function processAdditionalFunctionValuesFn() { var additionalFVEffects = _this17.additionalFunctionValuesAndEffects; if (additionalFVEffects) { var _loop6 = function _loop6(additionalFunctionValue, effects, transforms) { var _effects = _slicedToArray(effects, 5), result = _effects[0], generator = _effects[1], modifiedBindings = _effects[2], modifiedProperties = _effects[3], createdObjects = _effects[4]; var nestedFunctions = new Set([].concat(_toConsumableArray(createdObjects)).filter(function (object) { return object instanceof _index2.FunctionValue; })); // result -- ignore TODO: return the result from the function somehow // Generator -- visit all entries // Bindings -- only need to serialize bindings if they're captured by some nested 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 -- visit any property bindings that aren't to createdobjects // CreatedObjects -- should take care of itself _this17.realm.applyEffects([result, new _generator.Generator(_this17.realm), modifiedBindings, modifiedProperties, createdObjects]); // Allows us to emit function declarations etc. inside of this additional // function instead of adding them at global scope // TODO: make sure this generator isn't getting mutated oddly nestedFunctions.forEach(function (val) { return _this17.additionalFunctionValueNestedFunctions.add(val); }); var serializePropertiesAndBindings = function serializePropertiesAndBindings() { var _iteratorNormalCompletion9 = true; var _didIteratorError9 = false; var _iteratorError9 = undefined; try { for (var _iterator9 = modifiedProperties.keys()[Symbol.iterator](), _step9; !(_iteratorNormalCompletion9 = (_step9 = _iterator9.next()).done); _iteratorNormalCompletion9 = true) { var propertyBinding = _step9.value; var binding = propertyBinding; var object = binding.object; if (object instanceof _index2.ObjectValue && createdObjects.has(object)) continue; if (object.refuseSerialization) continue; if (object.isIntrinsic()) continue; (0, _invariant2.default)(object instanceof _index2.ObjectValue); _this17._emitProperty(object, binding.key, binding.descriptor, true); } } catch (err) { _didIteratorError9 = true; _iteratorError9 = err; } finally { try { if (!_iteratorNormalCompletion9 && _iterator9.return) { _iterator9.return(); } } finally { if (_didIteratorError9) { throw _iteratorError9; } } } (0, _invariant2.default)(result instanceof _index2.Value); // Handle ModifiedBindings var additionalFunctionValueInfo = _this17.additionalFunctionValueInfos.get(additionalFunctionValue); (0, _invariant2.default)(additionalFunctionValueInfo); var _iteratorNormalCompletion10 = true; var _didIteratorError10 = false; var _iteratorError10 = undefined; try { for (var _iterator10 = additionalFunctionValueInfo.modifiedBindings[Symbol.iterator](), _step10; !(_iteratorNormalCompletion10 = (_step10 = _iterator10.next()).done); _iteratorNormalCompletion10 = true) { var _ref11 = _step10.value; var _ref12 = _slicedToArray(_ref11, 2); var modifiedBinding = _ref12[0]; var residualBinding = _ref12[1]; var newVal = modifiedBinding.value; (0, _invariant2.default)(newVal); residualBinding.additionalValueSerialized = _this17.serializeValue(newVal); } } catch (err) { _didIteratorError10 = true; _iteratorError10 = err; } finally { try { if (!_iteratorNormalCompletion10 && _iterator10.return) { _iterator10.return(); } } finally { if (_didIteratorError10) { throw _iteratorError10; } } } if (!(result instanceof _index2.UndefinedValue)) _this17.emitter.emit(t.returnStatement(_this17.serializeValue(result))); var lazyHoistedReactNodes = _this17.residualReactElements.serializeLazyHoistedNodes(); Array.prototype.push.apply(_this17.mainBody.entries, lazyHoistedReactNodes); }; _this17.currentAdditionalFunction = additionalFunctionValue; var body = _this17._serializeAdditionalFunction(generator, serializePropertiesAndBindings); (0, _invariant2.default)(additionalFunctionValue instanceof _index2.ECMAScriptSourceFunctionValue); var _iteratorNormalCompletion11 = true; var _didIteratorError11 = false; var _iteratorError11 = undefined; try { for (var _iterator11 = transforms[Symbol.iterator](), _step11; !(_iteratorNormalCompletion11 = (_step11 = _iterator11.next()).done); _iteratorNormalCompletion11 = true) { var transform = _step11.value; transform(body); } } catch (err) { _didIteratorError11 = true; _iteratorError11 = err; } finally { try { if (!_iteratorNormalCompletion11 && _iterator11.return) { _iterator11.return(); } } finally { if (_didIteratorError11) { throw _iteratorError11; } } } rewrittenAdditionalFunctions.set(additionalFunctionValue, body); // re-resolve initialized modules to include things from additional functions _this17.modules.resolveInitializedModules(); if (shouldEmitLog && _this17.modules.moduleIds.size > 0) console.log("=== " + _this17.modules.initializedModules.size + " of " + _this17.modules.moduleIds.size + " modules initialized after additional function " + (additionalFunctionValue.intrinsicName ? additionalFunctionValue.intrinsicName : "")); // These don't restore themselves properly otherwise. _this17.realm.restoreBindings(modifiedBindings); _this17.realm.restoreProperties(modifiedProperties); }; var _iteratorNormalCompletion8 = true; var _didIteratorError8 = false; var _iteratorError8 = undefined; try { for (var _iterator8 = additionalFVEffects.entries()[Symbol.iterator](), _step8; !(_iteratorNormalCompletion8 = (_step8 = _iterator8.next()).done); _iteratorNormalCompletion8 = true) { var _ref9 = _step8.value; var _ref10 = _slicedToArray(_ref9, 2); var additionalFunctionValue = _ref10[0]; var _ref10$ = _ref10[1]; var effects = _ref10$.effects; var transforms = _ref10$.transforms; _loop6(additionalFunctionValue, effects, transforms); } } catch (err) { _didIteratorError8 = true; _iteratorError8 = err; } finally { try { if (!_iteratorNormalCompletion8 && _iterator8.return) { _iterator8.return(); } } finally { if (_didIteratorError8) { throw _iteratorError8; } } } } return _this17.realm.intrinsics.undefined; }; this.realm.evaluateAndRevertInGlobalEnv(processAdditionalFunctionValuesFn); return rewrittenAdditionalFunctions; } // Hook point for any serialization needs to be done after generator serialization is complete. }, { key: "postGeneratorSerialization", value: function postGeneratorSerialization() { // For overriding only. } }, { key: "serialize", value: function serialize() { this.generator.serialize(this._getContext()); (0, _invariant2.default)(this.emitter._declaredAbstractValues.size <= this.preludeGenerator.derivedIds.size); this.postGeneratorSerialization(); Array.prototype.push.apply(this.prelude, this.preludeGenerator.prelude); // TODO #20: add timers // TODO #21: add event listeners var _iteratorNormalCompletion12 = true; var _didIteratorError12 = false; var _iteratorError12 = undefined; try { for (var _iterator12 = this.modules.initializedModules[Symbol.iterator](), _step12; !(_iteratorNormalCompletion12 = (_step12 = _iterator12.next()).done); _iteratorNormalCompletion12 = true) { var _ref13 = _step12.value; var _ref14 = _slicedToArray(_ref13, 2); var moduleId = _ref14[0]; var moduleValue = _ref14[1]; this.requireReturns.set(moduleId, this.serializeValue(moduleValue)); } // Make sure additional functions get serialized. } catch (err) { _didIteratorError12 = true; _iteratorError12 = err; } finally { try { if (!_iteratorNormalCompletion12 && _iterator12.return) { _iterator12.return(); } } finally { if (_didIteratorError12) { throw _iteratorError12; } } } var rewrittenAdditionalFunctions = this.processAdditionalFunctionValues(); this.modules.resolveInitializedModules(); this.emitter.finalize(); this.residualFunctions.residualFunctionInitializers.factorifyInitializers(this.factoryNameGenerator); var _residualFunctions$sp = this.residualFunctions.spliceFunctions(rewrittenAdditionalFunctions), unstrictFunctionBodies = _residualFunctions$sp.unstrictFunctionBodies, strictFunctionBodies = _residualFunctions$sp.strictFunctionBodies, requireStatistics = _residualFunctions$sp.requireStatistics; if (this.modules.moduleIds.size > 0 && !this.residualHeapValueIdentifiers.collectValToRefCountOnly) { console.log("=== " + this.modules.initializedModules.size + " of " + this.modules.moduleIds.size + " modules initialized, " + requireStatistics.replaced + " of " + requireStatistics.count + " require calls inlined."); } // add strict modes var strictDirective = t.directive(t.directiveLiteral("use strict")); var globalDirectives = []; if (!this.realm.isStrict && !unstrictFunctionBodies.length && strictFunctionBodies.length) { // no unstrict functions, only strict ones globalDirectives.push(strictDirective); } else if (unstrictFunctionBodies.length && strictFunctionBodies.length) { // strict and unstrict functions var _iteratorNormalCompletion13 = true; var _didIteratorError13 = false; var _iteratorError13 = undefined; try { funcLoop: for (var _iterator13 = strictFunctionBodies[Symbol.iterator](), _step13; !(_iteratorNormalCompletion13 = (_step13 = _iterator13.next()).done); _iteratorNormalCompletion13 = true) { var func = _step13.value; if (func.body.directives) { var _iteratorNormalCompletion14 = true; var _didIteratorError14 = false; var _iteratorError14 = undefined; try { for (var _iterator14 = func.body.directives[Symbol.iterator](), _step14; !(_iteratorNormalCompletion14 = (_step14 = _iterator14.next()).done); _iteratorNormalCompletion14 = true) { var directive = _step14.value; if (directive.value.value === "use strict") { // already have a use strict directive continue funcLoop; } } } catch (err) { _didIteratorError14 = true; _iteratorError14 = err; } finally { try { if (!_iteratorNormalCompletion14 && _iterator14.return) { _iterator14.return(); } } finally { if (_didIteratorError14) { throw _iteratorError14; } } } } else func.body.directives = []; func.body.directives.unshift(strictDirective); } } catch (err) { _didIteratorError13 = true; _iteratorError13 = err; } finally { try { if (!_iteratorNormalCompletion13 && _iterator13.return) { _iterator13.return(); } } finally { if (_didIteratorError13) { throw _iteratorError13; } } } } // build ast if (this.needsEmptyVar) { this.prelude.push(t.variableDeclaration("var", [t.variableDeclarator(_internalizer.emptyExpression, t.objectExpression([]))])); } if (this.needsAuxiliaryConstructor) { this.prelude.push(t.variableDeclaration("var", [t.variableDeclarator(_internalizer.constructorExpression, t.functionExpression(null, [], t.blockStatement([])))])); } var body = this.prelude.concat(this.emitter.getBody().entries); (0, _factorify.factorifyObjects)(body, this.factoryNameGenerator); var ast_body = []; if (this.preludeGenerator.declaredGlobals.size > 0) ast_body.push(t.variableDeclaration("var", Array.from(this.preludeGenerator.declaredGlobals).map(function (key) { return t.variableDeclarator(t.identifier(key)); }))); if (body.length) { if (this.realm.isCompatibleWith("node-source-maps")) { ast_body.push(t.expressionStatement(t.callExpression(t.memberExpression(t.callExpression(t.identifier("require"), [t.stringLiteral("source-map-support")]), t.identifier("install")), []))); } if (this._shouldBeWrapped(body)) { var globalExpression = this.realm.isCompatibleWith("node-cli") ? t.identifier("global") : t.thisExpression(); var functionExpression = t.functionExpression(null, [], t.blockStatement(body, globalDirectives)); var callExpression = this.preludeGenerator.usesThis ? t.callExpression(t.memberExpression(functionExpression, t.identifier("call")), [globalExpression]) : t.callExpression(functionExpression, []); ast_body.push(t.expressionStatement(callExpression)); } else { ast_body = body; } } // Make sure that the visitor visited as many values as the serializer (0, _invariant2.default)(this.serializedValues.size === this.residualValues.size, "serialized " + this.serializedValues.size + " of " + this.residualValues.size); // TODO: find better way to do this? // revert changes to functionInstances in case we do multiple serialization passes var _iteratorNormalCompletion15 = true; var _didIteratorError15 = false; var _iteratorError15 = undefined; try { for (var _iterator15 = this.residualFunctionInstances.values()[Symbol.iterator](), _step15; !(_iteratorNormalCompletion15 = (_step15 = _iterator15.next()).done); _iteratorNormalCompletion15 = true) { var instance = _step15.value; var _iteratorNormalCompletion16 = true; var _didIteratorError16 = false; var _iteratorError16 = undefined; try { for (var _iterator16 = instance.residualFunctionBindings.values()[Symbol.iterator](), _step16; !(_iteratorNormalCompletion16 = (_step16 = _iterator16.next()).done); _iteratorNormalCompletion16 = true) { var binding = _step16.value; var b = binding; delete b.serializedValue; delete b.referentialized; } } catch (err) { _didIteratorError16 = true; _iteratorError16 = err; } finally { try { if (!_iteratorNormalCompletion16 && _iterator16.return) { _iterator16.return(); } } finally { if (_didIteratorError16) { throw _iteratorError16; } } } } } catch (err) { _didIteratorError15 = true; _iteratorError15 = err; } finally { try { if (!_iteratorNormalCompletion15 && _iterator15.return) { _iterator15.return(); } } finally { if (_didIteratorError15) { throw _iteratorError15; } } } var program_directives = []; if (this.realm.isStrict) program_directives.push(strictDirective); return t.file(t.program(ast_body, program_directives)); } }]); return ResidualHeapSerializer; }(); //# sourceMappingURL=ResidualHeapSerializer.js.map