1924 lines
88 KiB
JavaScript
1924 lines
88 KiB
JavaScript
"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
|