UNPKG

prepack

Version:

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

268 lines (203 loc) 9.4 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.HeapInspector = void 0; var _realm = require("../realm.js"); var _index = require("../methods/index.js"); var _index2 = require("../values/index.js"); var _singletons = require("../singletons.js"); var _invariant = _interopRequireDefault(require("../invariant.js")); var _logger = require("./logger.js"); var _descriptors = require("../descriptors.js"); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } function hasAnyConfigurable(desc) { if (!desc) { return false; } if (desc instanceof _descriptors.PropertyDescriptor) { return !!desc.configurable; } if (desc instanceof _descriptors.AbstractJoinedDescriptor) { return hasAnyConfigurable(desc.descriptor1) || hasAnyConfigurable(desc.descriptor2); } (0, _invariant.default)(false, "internal slots aren't covered here"); } function hasAnyWritable(desc) { if (!desc) { return false; } if (desc instanceof _descriptors.PropertyDescriptor) { return desc.value !== undefined && !!desc.writable; } if (desc instanceof _descriptors.AbstractJoinedDescriptor) { return hasAnyWritable(desc.descriptor1) || hasAnyWritable(desc.descriptor2); } (0, _invariant.default)(false, "internal slots aren't covered here"); } class HeapInspector { constructor(realm, logger) { this.realm = realm; this.logger = logger; this.ignoredProperties = new Map(); this._targetIntegrityCommands = new Map(); } getTargetIntegrityCommand(val) { let command = this._targetIntegrityCommands.get(val); if (command === undefined) { command = ""; if (val instanceof _index2.ProxyValue) {// proxies don't participate in regular object freezing/sealing, // only their underlying proxied objects do } else { let extensible = val.$Extensible; if (!(extensible instanceof _index2.BooleanValue)) { this.logger.logError(val, "Object that might or might not be sealed or frozen are not supported in residual heap."); } else if (!extensible.value) { let anyWritable = false, anyConfigurable = false; for (let propertyBinding of val.properties.values()) { let desc = propertyBinding.descriptor; if (desc === undefined) continue; //deleted if (hasAnyConfigurable(desc)) anyConfigurable = true;else if (hasAnyWritable(desc)) anyWritable = true; } command = anyConfigurable ? "preventExtensions" : anyWritable ? "seal" : "freeze"; } } this._targetIntegrityCommands.set(val, command); } return command; } getTargetIntegrityDescriptor(val) { return HeapInspector._integrityDescriptors[this.getTargetIntegrityCommand(val)]; } static isLeaf(val) { if (val instanceof _index2.SymbolValue) { return false; } if (val instanceof _index2.AbstractValue) { if (val.hasIdentifier()) { return true; } if (val.$Realm.instantRender.enabled && val.intrinsicName !== undefined && val.intrinsicName.startsWith("__native")) { // Never factor out multiple occurrences of InstantRender's __native... abstract functions. return true; } } if (val.isIntrinsic()) { return false; } return val instanceof _index2.PrimitiveValue; } // Object properties which have the default value can be ignored by the serializer. canIgnoreProperty(val, key) { let set = this.ignoredProperties.get(val); if (!set) { this.ignoredProperties.set(val, set = this._getIgnoredProperties(val)); } return set.has(key); } _getIgnoredProperties(val) { let set = new Set(); for (let [key, propertyBinding] of val.properties) { (0, _invariant.default)(propertyBinding); let desc = propertyBinding.descriptor; if (desc === undefined) continue; //deleted if (this._canIgnoreProperty(val, key, desc)) set.add(key); } return set; } _canIgnoreProperty(val, key, desc) { if (!(desc instanceof _descriptors.PropertyDescriptor)) { // If we have a joined descriptor, there is at least one variant that isn't the same as // the target descriptor. Since the two descriptors won't be equal. return false; } let targetDescriptor = this.getTargetIntegrityDescriptor(val); if ((0, _index.IsArray)(this.realm, val)) { if (key === "length" && desc.writable === targetDescriptor.writable && desc.enumerable !== true && desc.configurable !== true) { // length property has the correct descriptor values return true; } } else if (val instanceof _index2.FunctionValue) { if (key === "length") { if (desc.value === undefined) { this.logger.logError(val, "Functions with length accessor properties are not supported in residual heap."); // Rationale: .bind() would call the accessor, which might throw, mutate state, or do whatever... } // length property will be inferred already by the amount of parameters return desc.writable !== true && desc.enumerable !== true && desc.configurable === targetDescriptor.configurable && val.hasDefaultLength(); } if (key === "name") { // TODO #474: Make sure that we retain original function names. Or set name property. // Or ensure that nothing references the name property. // NOTE: with some old runtimes notably JSC, function names are not configurable // For now don't ignore the property if it is different from the function name. // I.e. if it was set explicitly in the code, retain it. if (desc.value !== undefined && !this.realm.isCompatibleWith(this.realm.MOBILE_JSC_VERSION) && !this.realm.isCompatibleWith("mobile") && (desc.value instanceof _index2.AbstractValue || desc.value instanceof _index2.ConcreteValue && val.__originalName !== undefined && val.__originalName !== "" && _singletons.To.ToString(this.realm, desc.value) !== val.__originalName)) return false; return true; } // Properties `caller` and `arguments` are added to normal functions in non-strict mode to prevent TypeErrors. // Because they are autogenerated, they should be ignored. if (key === "arguments" || key === "caller") { (0, _invariant.default)(val instanceof _index2.ECMAScriptSourceFunctionValue); if (!val.$Strict && desc.writable === (!val.$Strict && targetDescriptor.writable) && desc.enumerable !== true && desc.configurable === targetDescriptor.configurable && desc.value instanceof _index2.UndefinedValue && val.$FunctionKind === "normal") return true; } // ignore the `prototype` property when it's the right one if (key === "prototype") { if (desc.configurable !== true && desc.enumerable !== true && desc.writable === targetDescriptor.writable && desc.value instanceof _index2.ObjectValue && desc.value.originalConstructor === val) { return true; } } } else { let kind = val.getKind(); switch (kind) { case "RegExp": if (key === "lastIndex" && desc.writable === targetDescriptor.writable && desc.enumerable !== true && desc.configurable !== true) { // length property has the correct descriptor values let v = desc.value; return v instanceof _index2.NumberValue && v.value === 0; } break; default: break; } } if (key === "constructor") { if (desc.configurable === targetDescriptor.configurable && desc.enumerable !== true && desc.writable === targetDescriptor.writable && desc.value === val.originalConstructor) return true; } return false; } static getPropertyValue(val, name) { let prototypeBinding = val.properties.get(name); if (prototypeBinding === undefined) return undefined; let prototypeDesc = prototypeBinding.descriptor; if (prototypeDesc === undefined) return undefined; (0, _invariant.default)(prototypeDesc instanceof _descriptors.PropertyDescriptor); (0, _invariant.default)(prototypeDesc.value === undefined || prototypeDesc.value instanceof _index2.Value); return prototypeDesc.value; } isDefaultPrototype(prototype) { if (prototype.symbols.size !== 0 || prototype.$Prototype !== this.realm.intrinsics.ObjectPrototype || prototype.$Extensible.mightNotBeTrue()) { return false; } let foundConstructor = false; for (let name of prototype.properties.keys()) if (name === "constructor" && HeapInspector.getPropertyValue(prototype, name) === prototype.originalConstructor) foundConstructor = true;else return false; return foundConstructor; } } exports.HeapInspector = HeapInspector; _defineProperty(HeapInspector, "_integrityDescriptors", { "": { writable: true, configurable: true }, preventExtensions: { writable: true, configurable: true }, seal: { writable: true, configurable: false }, freeze: { writable: false, configurable: false } }); //# sourceMappingURL=HeapInspector.js.map