UNPKG

prepack

Version:

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

205 lines (177 loc) 8.33 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.ResidualHeapInspector = undefined; var _realm = require("../realm.js"); var _index = require("../methods/index.js"); var _index2 = require("../values/index.js"); var _invariant = require("../invariant.js"); var _invariant2 = _interopRequireDefault(_invariant); var _logger = require("../utils/logger.js"); var _utils = require("../react/utils.js"); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } /** * 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. */ class ResidualHeapInspector { 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 (desc.configurable) anyConfigurable = true;else if (desc.value !== undefined && desc.writable) anyWritable = true; } command = anyConfigurable ? "preventExtensions" : anyWritable ? "seal" : "freeze"; } } this._targetIntegrityCommands.set(val, command); } return command; } getTargetIntegrityDescriptor(val) { return ResidualHeapInspector._integrityDescriptors[this.getTargetIntegrityCommand(val)]; } static isLeaf(val) { if (val instanceof _index2.SymbolValue) { return false; } if (val instanceof _index2.AbstractValue && val.hasIdentifier()) { 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, _invariant2.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) { let targetDescriptor = this.getTargetIntegrityDescriptor(val); if ((0, _index.IsArray)(this.realm, val)) { if (key === "length" && desc.writable === targetDescriptor.writable && !desc.enumerable && !desc.configurable) { // length property has the correct descriptor values return true; } } else if ((0, _utils.isReactElement)(val)) { // we don't want to visit/serialize $$typeof, _owner and _store properties // as these are all internals of JSX/createElement if (key === "$$typeof" || key === "_owner" || key === "_store") { return true; } if ((key === "ref" || key === "key") && desc.value === this.realm.intrinsics.null) { 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 && !desc.enumerable && 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 || val.__originalName && val.__originalName !== "" && desc.value.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, _invariant2.default)(val instanceof _index2.ECMAScriptSourceFunctionValue); if (!val.$Strict && desc.writable === (!val.$Strict && targetDescriptor.writable) && !desc.enumerable && 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 && !desc.enumerable && 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 && !desc.configurable) { // 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 && 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, _invariant2.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" && ResidualHeapInspector.getPropertyValue(prototype, name) === prototype.originalConstructor) foundConstructor = true;else return false; return foundConstructor; } } exports.ResidualHeapInspector = ResidualHeapInspector; ResidualHeapInspector._integrityDescriptors = { "": { writable: true, configurable: true }, preventExtensions: { writable: true, configurable: true }, seal: { writable: true, configurable: false }, freeze: { writable: false, configurable: false } }; //# sourceMappingURL=ResidualHeapInspector.js.map