Files
2023-08-01 13:49:46 +02:00

1114 lines
49 KiB
JavaScript

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