UNPKG

prepack

Version:

Execute a JS bundle, serialize global state and side effects to a snapshot that can be quickly restored.

903 lines (734 loc) 32.5 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.MaterializeImplementation = exports.LeakImplementation = void 0; var _errors = require("../errors.js"); var _environment = require("../environment.js"); var _index = require("../values/index.js"); var _index2 = require("../methods/index.js"); var t = _interopRequireWildcard(require("@babel/types")); var _traverse = _interopRequireDefault(require("@babel/traverse")); var _invariant = _interopRequireDefault(require("../invariant.js")); var _HeapInspector = require("../utils/HeapInspector.js"); var _logger = require("../utils/logger.js"); var _utils = require("../react/utils.js"); var _descriptors = require("../descriptors.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)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } /** * 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. */ /* strict-local */ function visitName(path, state, name, read, write) { // Is the name bound to some local identifier? If so, we don't need to do anything if (path.scope.hasBinding(name, /*noGlobals*/ true)) return; // Otherwise, let's record that there's an unbound identifier if (read) state.unboundReads.add(name); if (write) state.unboundWrites.add(name); } function ignorePath(path) { let parent = path.parent; return t.isLabeledStatement(parent) || t.isBreakStatement(parent) || t.isContinueStatement(parent); } let LeakedClosureRefVisitor = { ReferencedIdentifier(path, state) { if (ignorePath(path)) return; let innerName = path.node.name; if (innerName === "arguments") { return; } visitName(path, state, innerName, true, false); }, "AssignmentExpression|UpdateExpression"(path, state) { let doesRead = path.node.operator !== "="; for (let name in path.getBindingIdentifiers()) { visitName(path, state, name, doesRead, true); } } }; function getLeakedFunctionInfo(value) { // TODO: This should really be cached on a per AST basis in case we have // many uses of the same closure. It should ideally share this cache // and data with ResidualHeapVisitor. (0, _invariant.default)(value instanceof _index.ECMAScriptSourceFunctionValue); (0, _invariant.default)(value.constructor === _index.ECMAScriptSourceFunctionValue); let functionInfo = { unboundReads: new Set(), unboundWrites: new Set() }; let formalParameters = value.$FormalParameters; (0, _invariant.default)(formalParameters != null); let code = value.$ECMAScriptCode; (0, _invariant.default)(code != null); (0, _traverse.default)(t.file(t.program([t.expressionStatement(t.functionExpression(null, formalParameters, code))])), LeakedClosureRefVisitor, null, functionInfo); _traverse.default.cache.clear(); return functionInfo; } function materializeObject(realm, object, getCachingHeapInspector) { let generator = realm.generator; if (object.symbols.size > 0) { throw new _errors.FatalError("TODO: Support havocing objects with symbols"); } if (object.unknownProperty !== undefined) {// TODO: Support unknown properties, or throw FatalError. // We have repros, e.g. test/serializer/additional-functions/ArrayConcat.js. } let getHeapInspector = getCachingHeapInspector || (() => new _HeapInspector.HeapInspector(realm, new _logger.Logger(realm, /*internalDebug*/ false))); // TODO: We should emit current value and then reset value for all *internal slots*; this will require deep serializer support; or throw FatalError when we detect any non-initial values in internal slots. for (let [name, propertyBinding] of object.properties) { // ignore properties with their correct default values if (getHeapInspector().canIgnoreProperty(object, name)) continue; let descriptor = propertyBinding.descriptor; if (descriptor === undefined) { // TODO: This happens, e.g. test/serializer/pure-functions/ObjectAssign2.js // If it indeed means deleted binding, should we initialize descriptor with a deleted value? if (generator !== undefined) generator.emitPropertyDelete(object, name); } else { (0, _invariant.default)(descriptor instanceof _descriptors.PropertyDescriptor); // TODO: Deal with joined descriptors. let value = descriptor.value; (0, _invariant.default)(value === undefined || value instanceof _index.Value, "cannot be an array because we are not dealing with intrinsics here"); if (value === undefined) {// TODO: Deal with accessor properties // We have repros, e.g. test/serializer/pure-functions/AbstractPropertyObjectKeyAssignment.js } else { (0, _invariant.default)(value instanceof _index.Value); if (value instanceof _index.EmptyValue) { if (generator !== undefined) generator.emitPropertyDelete(object, name); } else { if (generator !== undefined) { let targetDescriptor = getHeapInspector().getTargetIntegrityDescriptor(object); if (!(0, _utils.isReactElement)(object)) { if (descriptor.writable !== targetDescriptor.writable || descriptor.configurable !== targetDescriptor.configurable) { generator.emitDefineProperty(object, name, descriptor); } else { generator.emitPropertyAssignment(object, name, value); } } } } } } } } class ObjectValueLeakingVisitor { // ObjectValues to visit if they're reachable. // Values that has been visited. constructor(realm, objectsTrackedForLeaks) { this.realm = realm; this.objectsTrackedForLeaks = objectsTrackedForLeaks; this.visitedValues = new Set(); } mustVisit(val) { if (val instanceof _index.ObjectValue) { // For Objects we only need to visit it if it is tracked // as a newly created object that might still be mutated. // Abstract values gets their arguments visited. if (!this.objectsTrackedForLeaks.has(val)) return false; } if (this.visitedValues.has(val)) return false; this.visitedValues.add(val); return true; } visitObjectProperty(binding) { let desc = binding.descriptor; if (desc === undefined) return; //deleted this.visitDescriptor(desc); } visitObjectProperties(obj, kind) { // visit symbol properties for (let [, propertyBindingValue] of obj.symbols) { (0, _invariant.default)(propertyBindingValue); this.visitObjectProperty(propertyBindingValue); } // visit string properties for (let [, propertyBindingValue] of obj.properties) { (0, _invariant.default)(propertyBindingValue); this.visitObjectProperty(propertyBindingValue); } // inject properties with computed names if (obj.unknownProperty !== undefined) { let desc = obj.unknownProperty.descriptor; this.visitObjectPropertiesWithComputedNamesDescriptor(desc); } // prototype this.visitObjectPrototype(obj); if ((0, _index2.TestIntegrityLevel)(this.realm, obj, "frozen")) return; // if this object wasn't already leaked, we need mark it as leaked // so that any mutation and property access get tracked after this. if (obj.mightNotBeLeakedObject()) { obj.leak(); // materialization is a common operation and needs to be invoked // whenever non-final values need to be made available at intermediate // points in a program's control flow. An object can be materialized by // calling materializeObject(). Sometimes, objects // are materialized in cohorts (such as during leaking). // In these cases, we provide a caching mechanism for HeapInspector(). let makeAndCacheHeapInspector = () => { let heapInspector = this._heapInspector; if (heapInspector !== undefined) return heapInspector;else { heapInspector = new _HeapInspector.HeapInspector(this.realm, new _logger.Logger(this.realm, /*internalDebug*/ false)); this._heapInspector = heapInspector; return heapInspector; } }; (0, _invariant.default)(this.realm.generator !== undefined); materializeObject(this.realm, obj, makeAndCacheHeapInspector); } } visitObjectPrototype(obj) { let proto = obj.$Prototype; this.visitValue(proto); } visitObjectPropertiesWithComputedNamesDescriptor(desc) { if (desc !== undefined) { if (desc instanceof _descriptors.PropertyDescriptor) { let val = desc.value; (0, _invariant.default)(val instanceof _index.AbstractValue); this.visitObjectPropertiesWithComputedNames(val); } else if (desc instanceof _descriptors.AbstractJoinedDescriptor) { this.visitValue(desc.joinCondition); this.visitObjectPropertiesWithComputedNamesDescriptor(desc.descriptor1); this.visitObjectPropertiesWithComputedNamesDescriptor(desc.descriptor2); } else { (0, _invariant.default)(false, "unknown descriptor"); } } } visitObjectPropertiesWithComputedNames(absVal) { if (absVal.kind === "widened property") return; if (absVal.kind === "template for prototype member expression") return; if (absVal.kind === "conditional") { let cond = absVal.args[0]; (0, _invariant.default)(cond instanceof _index.AbstractValue); if (cond.kind === "template for property name condition") { let P = cond.args[0]; (0, _invariant.default)(P instanceof _index.AbstractValue); let V = absVal.args[1]; let earlier_props = absVal.args[2]; if (earlier_props instanceof _index.AbstractValue) this.visitObjectPropertiesWithComputedNames(earlier_props); this.visitValue(P); this.visitValue(V); } else { // conditional assignment this.visitValue(cond); let consequent = absVal.args[1]; if (consequent instanceof _index.AbstractValue) { this.visitObjectPropertiesWithComputedNames(consequent); } let alternate = absVal.args[2]; if (alternate instanceof _index.AbstractValue) { this.visitObjectPropertiesWithComputedNames(alternate); } } } else { this.visitValue(absVal); } } visitDescriptor(desc) { if (desc === undefined) {} else if (desc instanceof _descriptors.PropertyDescriptor) { if (desc.value !== undefined) this.visitValue(desc.value); if (desc.get !== undefined) this.visitValue(desc.get); if (desc.set !== undefined) this.visitValue(desc.set); } else if (desc instanceof _descriptors.AbstractJoinedDescriptor) { this.visitValue(desc.joinCondition); if (desc.descriptor1 !== undefined) this.visitDescriptor(desc.descriptor1); if (desc.descriptor2 !== undefined) this.visitDescriptor(desc.descriptor2); } else { (0, _invariant.default)(false, "unknown descriptor"); } } visitDeclarativeEnvironmentRecordBinding(record, remainingLeakedBindings) { let bindings = record.bindings; for (let bindingName of Object.keys(bindings)) { let binding = bindings[bindingName]; // Check if this binding is referenced, and if so delete it from the set. let isRead = remainingLeakedBindings.unboundReads.delete(bindingName); let isWritten = remainingLeakedBindings.unboundWrites.delete(bindingName); if (isRead) { // If this binding can be read from the closure, its value has now leaked. let value = binding.value; if (value) { this.visitValue(value); } } if (isWritten || isRead) { // If this binding could have been mutated from the closure, then the // binding itself has now leaked, but not necessarily the value in it. // TODO: We could tag a leaked binding as read and/or write. That way // we don't have to leak values written to this binding if only the binding // has been written to. We also don't have to leak reads from this binding // if it is only read from. (0, _environment.leakBinding)(binding); } } } visitValueMap(val) { let kind = val.getKind(); let entries; if (kind === "Map") { entries = val.$MapData; } else { (0, _invariant.default)(kind === "WeakMap"); entries = val.$WeakMapData; } (0, _invariant.default)(entries !== undefined); let len = entries.length; for (let i = 0; i < len; i++) { let entry = entries[i]; let key = entry.$Key; let value = entry.$Value; if (key === undefined || value === undefined) continue; this.visitValue(key); this.visitValue(value); } } visitValueSet(val) { let kind = val.getKind(); let entries; if (kind === "Set") { entries = val.$SetData; } else { (0, _invariant.default)(kind === "WeakSet"); entries = val.$WeakSetData; } (0, _invariant.default)(entries !== undefined); let len = entries.length; for (let i = 0; i < len; i++) { let entry = entries[i]; if (entry === undefined) continue; this.visitValue(entry); } } visitValueFunction(val) { if (!val.mightNotBeLeakedObject()) { return; } this.visitObjectProperties(val); if (val instanceof _index.BoundFunctionValue) { this.visitValue(val.$BoundTargetFunction); this.visitValue(val.$BoundThis); for (let boundArg of val.$BoundArguments) this.visitValue(boundArg); return; } (0, _invariant.default)(!(val instanceof _index.NativeFunctionValue), "all native function values should have already been created outside this pure function"); let remainingLeakedBindings = getLeakedFunctionInfo(val); let environment = val.$Environment; while (environment) { let record = environment.environmentRecord; if (record instanceof _environment.ObjectEnvironmentRecord) { this.visitValue(record.object); continue; } if (record instanceof _environment.GlobalEnvironmentRecord) { break; } (0, _invariant.default)(record instanceof _environment.DeclarativeEnvironmentRecord); this.visitDeclarativeEnvironmentRecordBinding(record, remainingLeakedBindings); if (record instanceof _environment.FunctionEnvironmentRecord) { // If this is a function environment, which is not tracked for leaks, // we can bail out because its bindings should not be mutated in a // pure function. let fn = record.$FunctionObject; if (!this.objectsTrackedForLeaks.has(fn)) { break; } } environment = environment.parent; } } visitValueObject(val) { if (!val.mightNotBeLeakedObject()) { return; } let kind = val.getKind(); this.visitObjectProperties(val, kind); switch (kind) { case "RegExp": case "Number": case "String": case "Boolean": case "ReactElement": case "ArrayBuffer": case "Array": return; case "Date": let dateValue = val.$DateValue; (0, _invariant.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": let buf = val.$ViewedArrayBuffer; (0, _invariant.default)(buf !== undefined); this.visitValue(buf); return; case "Map": case "WeakMap": this.visitValueMap(val); return; case "Set": case "WeakSet": this.visitValueSet(val); return; default: (0, _invariant.default)(kind === "Object", `Object of kind ${kind} is not supported in calls to abstract functions.`); (0, _invariant.default)(val.$ParameterMap === undefined, `Arguments object is not supported in calls to abstract functions.`); return; } } visitValueProxy(val) { this.visitValue(val.$ProxyTarget); this.visitValue(val.$ProxyHandler); } visitAbstractValue(val) { if (!val.mightBeObject()) { // Only objects need to be leaked. return; } if (val.values.isTop()) { // If we don't know which object instances it might be, // then it might be one of the arguments that created // this value. See #2179. if (val.kind === "conditional") { // For a conditional, we only have to visit each case. Not the condition itself. this.visitValue(val.args[1]); this.visitValue(val.args[2]); return; } // To ensure that we don't forget to provide arguments // that can be havoced, we require at least one argument. let whitelistedKind = val.kind && (val.kind === "widened numeric property" || // TODO: Widened properties needs to be havocable. val.kind.startsWith("abstractCounted")); (0, _invariant.default)(whitelistedKind !== undefined || val.intrinsicName !== undefined || val.args.length > 0, "Havoced unknown object requires havocable arguments"); // TODO: This is overly conservative. We recursively leak all the inputs // to this operation whether or not they can possible be part of the // result value or not. for (let i = 0, n = val.args.length; i < n; i++) { this.visitValue(val.args[i]); } return; } // If we know which object this might be, then leak each of them. for (let element of val.values.getElements()) { this.visitValue(element); } } visitValue(val) { if (val instanceof _index.AbstractValue) { if (this.mustVisit(val)) this.visitAbstractValue(val); } else if (val.isIntrinsic()) { // All intrinsic values exist from the beginning of time (except arrays with widened properties)... // ...except for a few that come into existance as templates for abstract objects. if (val instanceof _index.ArrayValue && _index.ArrayValue.isIntrinsicAndHasWidenedNumericProperty(val)) { if (this.mustVisit(val)) this.visitValueObject(val); } else { this.mustVisit(val); } } else if (val instanceof _index.EmptyValue) { this.mustVisit(val); } else if (val instanceof _index.PrimitiveValue) { this.mustVisit(val); } else if (val instanceof _index.ProxyValue) { if (this.mustVisit(val)) this.visitValueProxy(val); } else if (val instanceof _index.FunctionValue) { (0, _invariant.default)(val instanceof _index.FunctionValue); if (this.mustVisit(val)) this.visitValueFunction(val); } else { (0, _invariant.default)(val instanceof _index.ObjectValue); if (this.mustVisit(val)) this.visitValueObject(val); } } } function ensureFrozenValue(realm, value, loc) { // TODO: This should really check if it is recursively immutability. if (value instanceof _index.ObjectValue && !(0, _index2.TestIntegrityLevel)(realm, value, "frozen")) { let diag = new _errors.CompilerDiagnostic("Unfrozen object leaked before end of global code", loc || realm.currentLocation, "PP0017", "RecoverableError"); if (realm.handleError(diag) !== "Recover") throw new _errors.FatalError(); } } // Ensure that a value is immutable. If it is not, set all its properties to abstract values // and all reachable bindings to abstract values. class LeakImplementation { value(realm, value, loc) { if (realm.instantRender.enabled) { // TODO: For InstantRender... // - For declarative bindings, we do want proper materialization/leaking/havocing // - For object properties, we conceptually want materialization // (however, not via statements that mutate the objects, // but only as part of the initial object literals), // but actual no leaking or leaking as there should be a way to annotate/enforce // that external/abstract functions are pure with regards to heap objects return; } let objectsTrackedForLeaks = realm.createdObjectsTrackedForLeaks; if (objectsTrackedForLeaks === undefined) { // We're not tracking a pure function. That means that we would track // everything as leaked. We'll assume that any object argument // is invalid unless it's frozen. ensureFrozenValue(realm, value, loc); } else { // If we're tracking a pure function, we can assume that only newly // created objects and bindings, within it, are mutable. Any other // object can safely be assumed to be deeply immutable as far as this // pure function is concerned. However, any mutable object needs to // be tainted as possibly having changed to anything. let visitor = new ObjectValueLeakingVisitor(realm, objectsTrackedForLeaks); visitor.visitValue(value); } } } exports.LeakImplementation = LeakImplementation; class MaterializeImplementation { // TODO: Understand relation to snapshots: #2441 materializeObject(realm, val) { if (realm.instantRender.enabled) // Materialization leads to runtime code that mutates objects // this is at best undesirable in InstantRender val.makeFinal();else materializeObject(realm, val); } // This routine materializes objects reachable from non-local bindings read // by a function. It does this for the purpose of outlining calls to that function. // // Notes: // - Locations that are only read need not materialize because their values are up-to-date // at optimization time, // - Locations that are written to are ignored, because we make the assumption, for now, // that the function being outlined is pure. // - Previously havoced locations (#2446) should be reloaded, but are currently rejected. // - Specialization depends on the assumption that the Array op will only be used once. // First, we will enforce it: #2448. Later we will relax it: #2454 computeReachableObjects(realm, rootValue) { (0, _invariant.default)(realm.isInPureScope()); let reachableObjects = new Set(); let visitedValues = new Set(); computeFromValue(rootValue); return reachableObjects; function computeFromBindings(func, nonLocalReadBindings) { (0, _invariant.default)(func instanceof _index.ECMAScriptSourceFunctionValue); let environment = func.$Environment; while (environment) { let record = environment.environmentRecord; if (record instanceof _environment.ObjectEnvironmentRecord) computeFromValue(record.object);else if (record instanceof _environment.DeclarativeEnvironmentRecord || record instanceof _environment.FunctionEnvironmentRecord) computeFromDeclarativeEnvironmentRecord(record, nonLocalReadBindings);else if (record instanceof _environment.GlobalEnvironmentRecord) { // TODO: #2484 break; } environment = environment.parent; } } function computeFromDeclarativeEnvironmentRecord(record, nonLocalReadBindings) { let environmentBindings = record.bindings; for (let bindingName of Object.keys(environmentBindings)) { let binding = environmentBindings[bindingName]; (0, _invariant.default)(binding !== undefined); let found = nonLocalReadBindings.delete(bindingName); // Check what undefined could mean here, besides absent binding // #2446 if (found && binding.value !== undefined) { computeFromValue(binding.value); } } } function computeFromAbstractValue(value) { if (value.values.isTop()) { for (let arg of value.args) { computeFromValue(arg); } } else { // If we know which object this might be, then leak each of them. for (let element of value.values.getElements()) { computeFromValue(element); } } } function computeFromProxyValue(value) { computeFromValue(value.$ProxyTarget); computeFromValue(value.$ProxyHandler); } function computeFromValue(value) { (0, _invariant.default)(value !== undefined); if (value.isIntrinsic() || value instanceof _index.EmptyValue || value instanceof _index.PrimitiveValue) { visit(value); } else if (value instanceof _index.AbstractValue) { ifNotVisited(value, computeFromAbstractValue); } else if (value instanceof _index.FunctionValue) { ifNotVisited(value, computeFromFunctionValue); } else if (value instanceof _index.ObjectValue) { ifNotVisited(value, computeFromObjectValue); } else if (value instanceof _index.ProxyValue) { ifNotVisited(value, computeFromProxyValue); } } function computeFromObjectValue(value) { (0, _invariant.default)(value instanceof _index.ObjectValue); let kind = value.getKind(); computeFromObjectProperties(value, kind); switch (kind) { case "RegExp": case "Number": case "String": case "Boolean": case "ReactElement": case "ArrayBuffer": case "Array": break; case "Date": let dateValue = value.$DateValue; (0, _invariant.default)(dateValue !== undefined); computeFromValue(dateValue); break; case "Float32Array": case "Float64Array": case "Int8Array": case "Int16Array": case "Int32Array": case "Uint8Array": case "Uint16Array": case "Uint32Array": case "Uint8ClampedArray": case "DataView": let buf = value.$ViewedArrayBuffer; (0, _invariant.default)(buf !== undefined); computeFromValue(buf); break; case "Map": case "WeakMap": ifNotVisited(value, computeFromMap); break; case "Set": case "WeakSet": ifNotVisited(value, computeFromSet); break; default: (0, _invariant.default)(kind === "Object", `Object of kind ${kind} is not supported in calls to abstract functions.`); (0, _invariant.default)(value.$ParameterMap === undefined, `Arguments object is not supported in calls to abstract functions.`); break; } if (!reachableObjects.has(value)) reachableObjects.add(value); } function computeFromDescriptor(descriptor) { if (descriptor === undefined) {} else if (descriptor instanceof _descriptors.PropertyDescriptor) { if (descriptor.value !== undefined) computeFromValue(descriptor.value); if (descriptor.get !== undefined) computeFromValue(descriptor.get); if (descriptor.set !== undefined) computeFromValue(descriptor.set); } else if (descriptor instanceof _descriptors.AbstractJoinedDescriptor) { computeFromValue(descriptor.joinCondition); if (descriptor.descriptor1 !== undefined) computeFromDescriptor(descriptor.descriptor1); if (descriptor.descriptor2 !== undefined) computeFromDescriptor(descriptor.descriptor2); } else { (0, _invariant.default)(false, "unknown descriptor"); } } function computeFromObjectPropertyBinding(binding) { let descriptor = binding.descriptor; if (descriptor === undefined) return; //deleted computeFromDescriptor(descriptor); } function computeFromObjectProperties(obj, kind) { // symbol properties for (let [, propertyBindingValue] of obj.symbols) { (0, _invariant.default)(propertyBindingValue); computeFromObjectPropertyBinding(propertyBindingValue); } // string properties for (let [, propertyBindingValue] of obj.properties) { (0, _invariant.default)(propertyBindingValue); computeFromObjectPropertyBinding(propertyBindingValue); } // inject properties with computed names if (obj.unknownProperty !== undefined) { let descriptor = obj.unknownProperty.descriptor; computeFromObjectPropertiesWithComputedNamesDescriptor(descriptor); } // prototype computeFromObjectPrototype(obj); } function computeFromObjectPrototype(obj) { if (obj.$Prototype !== undefined) computeFromValue(obj.$Prototype); } function computeFromFunctionValue(fn) { computeFromObjectProperties(fn); if (fn instanceof _index.BoundFunctionValue) { computeFromValue(fn.$BoundTargetFunction); computeFromValue(fn.$BoundThis); for (let boundArg of fn.$BoundArguments) computeFromValue(boundArg); return; } (0, _invariant.default)(!(fn instanceof _index.NativeFunctionValue), "all native function values should have already been created outside this pure function"); let nonLocalReadBindings = nonLocalReadBindingsOfFunction(fn); computeFromBindings(fn, nonLocalReadBindings); } function computeFromObjectPropertiesWithComputedNamesDescriptor(descriptor) { // TODO: #2484 notSupportedForTransitiveMaterialization(); } function computeFromMap(val) { let kind = val.getKind(); let entries; if (kind === "Map") { entries = val.$MapData; } else { (0, _invariant.default)(kind === "WeakMap"); entries = val.$WeakMapData; } (0, _invariant.default)(entries !== undefined); let len = entries.length; for (let i = 0; i < len; i++) { let entry = entries[i]; let key = entry.$Key; let value = entry.$Value; if (key === undefined || value === undefined) continue; computeFromValue(key); computeFromValue(value); } } function computeFromSet(val) { let kind = val.getKind(); let entries; if (kind === "Set") { entries = val.$SetData; } else { (0, _invariant.default)(kind === "WeakSet"); entries = val.$WeakSetData; } (0, _invariant.default)(entries !== undefined); let len = entries.length; for (let i = 0; i < len; i++) { let entry = entries[i]; if (entry === undefined) continue; computeFromValue(entry); } } function nonLocalReadBindingsOfFunction(func) { // unboundWrites is currently not used, but we leave it in place // to reuse the function closure visitor implemented for leaking let functionInfo = { unboundReads: new Set(), unboundWrites: new Set() }; (0, _invariant.default)(func instanceof _index.ECMAScriptSourceFunctionValue); let formalParameters = func.$FormalParameters; (0, _invariant.default)(formalParameters != null); let code = func.$ECMAScriptCode; (0, _invariant.default)(code != null); (0, _traverse.default)(t.file(t.program([t.expressionStatement(t.functionExpression(null, formalParameters, code))])), LeakedClosureRefVisitor, null, functionInfo); _traverse.default.cache.clear(); // TODO #2478: add invariant that there are no write bindings return functionInfo.unboundReads; } function ifNotVisited(value, computeFrom) { if (!visitedValues.has(value)) { visitedValues.add(value); computeFrom(value); } } function visit(value) { visitedValues.add(value); } function notSupportedForTransitiveMaterialization() { let error = new _errors.CompilerDiagnostic("Not supported for transitive materialization", rootValue.expressionLocation, "PP0041", "FatalError"); realm.handleError(error); throw new _errors.FatalError(); } } } exports.MaterializeImplementation = MaterializeImplementation; //# sourceMappingURL=leak.js.map