UNPKG

prepack

Version:

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

530 lines (407 loc) 18.4 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; var _index = require("../domains/index.js"); var _errors = require("../errors.js"); var _index2 = require("./index.js"); var _utils = require("../react/utils.js"); var _index3 = require("../methods/index.js"); var _singletons = require("../singletons.js"); var _invariant = _interopRequireDefault(require("../invariant.js")); var _generator = require("../utils/generator.js"); var _descriptors = require("../descriptors.js"); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; var ownKeys = Object.keys(source); if (typeof Object.getOwnPropertySymbols === 'function') { ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function (sym) { return Object.getOwnPropertyDescriptor(source, sym).enumerable; })); } ownKeys.forEach(function (key) { _defineProperty(target, key, source[key]); }); } return target; } 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; } class ObjectValue extends _index2.ConcreteValue { constructor(realm, proto, intrinsicName, refuseSerialization = false) { super(realm, intrinsicName); realm.recordNewObject(this); if (realm.useAbstractInterpretation) this.setupBindings(this.getTrackedPropertyNames()); this.$Prototype = proto || realm.intrinsics.null; this.$Extensible = realm.intrinsics.true; this._isPartial = realm.intrinsics.false; this._isLeaked = realm.intrinsics.false; this._isSimple = realm.intrinsics.false; this._simplicityIsTransitive = realm.intrinsics.false; this._isFinal = realm.intrinsics.false; this.properties = new Map(); this.symbols = new Map(); this.refuseSerialization = refuseSerialization; // this.$IsClassPrototype should be the last thing that gets initialized, // as other code checks whether this.$IsClassPrototype === undefined // as a proxy for whether initialization is still ongoing. this.$IsClassPrototype = false; } getTrackedPropertyNames() { return ObjectValue.trackedPropertyNames; } setupBindings(propertyNames) { for (let propName of propertyNames) { let propBindingName = ObjectValue.trackedPropertyBindingNames.get(propName); (0, _invariant.default)(propBindingName !== undefined); this[propBindingName] = undefined; } } static setupTrackedPropertyAccessors(propertyNames) { for (let propName of propertyNames) { let propBindingName = ObjectValue.trackedPropertyBindingNames.get(propName); if (propBindingName === undefined) ObjectValue.trackedPropertyBindingNames.set(propName, propBindingName = propName + "_binding"); Object.defineProperty(ObjectValue.prototype, propName, { configurable: true, get: function () { let binding = this[propBindingName]; (0, _invariant.default)(binding === undefined || binding.descriptor instanceof _descriptors.InternalSlotDescriptor); return binding === undefined ? undefined : binding.descriptor.value; }, set: function (v) { // Let's make sure that the object is not leaked. // To that end, we'd like to call this.isLeakedObject(). // However, while the object is still being initialized, // properties may be set, but this.isLeakedObject() may not be called yet. // To check if we are still initializing, guard the call by looking at // whether this.$IsClassPrototype has been initialized as a proxy for // object initialization in general. (0, _invariant.default)( // We're still initializing so we can set a property. this.$IsClassPrototype === undefined || // It's not leaked so we can set a property. this.mightNotBeLeakedObject() || // Object.assign() implementation needs to temporarily // make potentially leaked objects non-partial and back. // We don't gain anything from checking whether it's leaked // before calling makePartial() so we'll whitelist this property. propBindingName === "_isPartial_binding", "cannot mutate a leaked object"); let binding = this[propBindingName]; if (binding === undefined) { let desc = new _descriptors.InternalSlotDescriptor(undefined); this[propBindingName] = binding = { descriptor: desc, object: this, key: propName, internalSlot: true }; } this.$Realm.recordModifiedProperty(binding); binding.descriptor.value = v; } }); } } // Checks whether effects are properly applied. isValid() { return this._isPartial !== undefined; } equals(x) { return this === x; } getHash() { if (!this.hashValue) { this.hashValue = ++this.$Realm.objectCount; } return this.hashValue; } get temporalAlias() { return this._temporalAlias; } set temporalAlias(value) { this._temporalAlias = value; } hasStringOrSymbolProperties() { for (let prop of this.properties.values()) { if (prop.descriptor === undefined) continue; return true; } for (let prop of this.symbols.values()) { if (prop.descriptor === undefined) continue; return true; } return false; } mightBeFalse() { return false; } mightNotBeObject() { return false; } throwIfNotObject() { return this; } makePartial() { this._isPartial = this.$Realm.intrinsics.true; } makeSimple(option) { this._isSimple = this.$Realm.intrinsics.true; this._simplicityIsTransitive = new _index2.BooleanValue(this.$Realm, option === "transitive" || option instanceof _index2.StringValue && option.value === "transitive"); } makeFinal() { this._isFinal = this.$Realm.intrinsics.true; } makeNotFinal() { this._isFinal = this.$Realm.intrinsics.false; } isPartialObject() { return this._isPartial.mightBeTrue(); } // When this object was created in an evaluateForEffects context and the effects have not been applied, the // value is not valid (and we shouldn't try to access any properties on it). isPartial should always be set // except when reverted by effects. isValid() { return this._isPartial !== undefined; } mightBeFinalObject() { return this._isFinal.mightBeTrue(); } mightNotBeFinalObject() { return this._isFinal.mightNotBeTrue(); } leak() { this._isLeaked = this.$Realm.intrinsics.true; } mightBeLeakedObject() { return this._isLeaked.mightBeTrue(); } mightNotBeLeakedObject() { return this._isLeaked.mightNotBeTrue(); } isSimpleObject() { if (this === this.$Realm.intrinsics.ObjectPrototype) return true; if (!this._isSimple.mightNotBeTrue()) return true; if (this.isPartialObject()) return false; if (this.symbols.size > 0) return false; for (let propertyBinding of this.properties.values()) { let desc = propertyBinding.descriptor; if (desc === undefined) continue; // deleted if (!(0, _index3.IsDataDescriptor)(this.$Realm, desc)) return false; if (!desc.writable) return false; } if (this.$Prototype instanceof _index2.NullValue) return true; (0, _invariant.default)(this.$Prototype); return this.$Prototype.isSimpleObject(); } isTransitivelySimple() { return !this._simplicityIsTransitive.mightNotBeTrue(); } getExtensible() { return this.$Extensible.throwIfNotConcreteBoolean().value; } setExtensible(v) { this.$Extensible = v ? this.$Realm.intrinsics.true : this.$Realm.intrinsics.false; } getKind() { // we can deduce the natural prototype by checking whether the following internal slots are present if (this.$SymbolData !== undefined) return "Symbol"; if (this.$StringData !== undefined) return "String"; if (this.$NumberData !== undefined) return "Number"; if (this.$BooleanData !== undefined) return "Boolean"; if (this.$DateValue !== undefined) return "Date"; if (this.$RegExpMatcher !== undefined) return "RegExp"; if (this.$SetData !== undefined) return "Set"; if (this.$MapData !== undefined) return "Map"; if (this.$DataView !== undefined) return "DataView"; if (this.$ArrayBufferData !== undefined) return "ArrayBuffer"; if (this.$WeakMapData !== undefined) return "WeakMap"; if (this.$WeakSetData !== undefined) return "WeakSet"; if ((0, _utils.isReactElement)(this) && this.$Realm.react.enabled) return "ReactElement"; if (this.$TypedArrayName !== undefined) return this.$TypedArrayName; // TODO #26 #712: Promises. All kinds of iterators. Generators. return "Object"; } defineNativeMethod(name, length, callback, desc) { let intrinsicName; if (typeof name === "string") { if (this.intrinsicName) intrinsicName = `${this.intrinsicName}.${name}`; } else if (name instanceof _index2.SymbolValue) { if (this.intrinsicName && name.intrinsicName) intrinsicName = `${this.intrinsicName}[${name.intrinsicName}]`; } else { (0, _invariant.default)(false); } let fnValue = new _index2.NativeFunctionValue(this.$Realm, intrinsicName, name, length, callback, false); this.defineNativeProperty(name, fnValue, desc); return fnValue; } defineNativeProperty(name, value, desc) { (0, _invariant.default)(!value || value instanceof _index2.Value); this.$DefineOwnProperty(name, new _descriptors.PropertyDescriptor(_objectSpread({ value, writable: true, enumerable: false, configurable: true }, desc))); } defineNativeGetter(name, callback, desc) { let intrinsicName, funcName; if (typeof name === "string") { funcName = `get ${name}`; if (this.intrinsicName) intrinsicName = `${this.intrinsicName}.${name}`; } else if (name instanceof _index2.SymbolValue) { funcName = name.$Description instanceof _index2.Value ? `get [${name.$Description.throwIfNotConcreteString().value}]` : `get [${"?"}]`; if (this.intrinsicName && name.intrinsicName) intrinsicName = `${this.intrinsicName}[${name.intrinsicName}]`; } else { (0, _invariant.default)(false); } let func = new _index2.NativeFunctionValue(this.$Realm, intrinsicName, funcName, 0, callback); this.$DefineOwnProperty(name, new _descriptors.PropertyDescriptor(_objectSpread({ get: func, set: this.$Realm.intrinsics.undefined, enumerable: false, configurable: true }, desc))); } defineNativeConstant(name, value, desc) { (0, _invariant.default)(!value || value instanceof _index2.Value); this.$DefineOwnProperty(name, new _descriptors.PropertyDescriptor(_objectSpread({ value, writable: false, enumerable: false, configurable: false }, desc))); } // Note that internal properties will not be copied to the snapshot, nor will they be removed. getSnapshot(options) { try { if (this.temporalAlias !== undefined) return this.temporalAlias; let realm = this.$Realm; let template = new ObjectValue(this.$Realm, this.$Realm.intrinsics.ObjectPrototype); let keys = _singletons.Properties.GetOwnPropertyKeysArray(realm, this, false, true); this.copyKeys(keys, this, template); // The snapshot is an immutable object snapshot template.makeFinal(); // The original object might be a React props object, thus // if it is, we need to ensure we mark it with the same rules if (realm.react.enabled && realm.react.reactProps.has(this)) { realm.react.reactProps.add(template); } let operationDescriptor = (0, _generator.createOperationDescriptor)("SINGLE_ARG"); let result = _index2.AbstractValue.createTemporalFromBuildFunction(this.$Realm, ObjectValue, [template], operationDescriptor, { skipInvariant: true, isPure: true }); (0, _invariant.default)(result instanceof _index2.AbstractObjectValue); result.values = new _index.ValuesDomain(template); return result; } finally { if (options && options.removeProperties) { this.properties = new Map(); this.symbols = new Map(); this.unknownProperty = undefined; } } } copyKeys(keys, from, to) { // c. Repeat for each element nextKey of keys in List order, for (let nextKey of keys) { // i. Let desc be ? from.[[GetOwnProperty]](nextKey). let desc = from.$GetOwnProperty(nextKey); // ii. If desc is not undefined and desc.[[Enumerable]] is true, then if (desc && desc.throwIfNotConcrete(this.$Realm).enumerable) { _singletons.Properties.ThrowIfMightHaveBeenDeleted(desc); // 1. Let propValue be ? Get(from, nextKey). let propValue = (0, _index3.Get)(this.$Realm, from, nextKey); // 2. Perform ? Set(to, nextKey, propValue, true). _singletons.Properties.Set(this.$Realm, to, nextKey, propValue, true); } } } _serialize(set, stack) { let obj = set({}); for (let [key, propertyBinding] of this.properties) { let desc = propertyBinding.descriptor; if (desc === undefined) continue; // deleted _singletons.Properties.ThrowIfMightHaveBeenDeleted(desc); desc = desc.throwIfNotConcrete(this.$Realm); let serializedDesc = { enumerable: desc.enumerable, configurable: desc.configurable }; if (desc.value) { serializedDesc.writable = desc.writable; (0, _invariant.default)(desc.value instanceof _index2.Value); serializedDesc.value = desc.value.serialize(stack); } else { (0, _invariant.default)(desc.get !== undefined); serializedDesc.get = desc.get.serialize(stack); (0, _invariant.default)(desc.set !== undefined); serializedDesc.set = desc.set.serialize(stack); } Object.defineProperty(obj, key, serializedDesc); } return obj; } // Whether [[{Get,Set}PrototypeOf]] delegate to Ordinary{Get,Set}PrototypeOf. // E.g. ProxyValue overrides this to return false. // See ECMA262 9.1.2.1 for an algorithm where this is relevant usesOrdinaryObjectInternalPrototypeMethods() { return true; } // ECMA262 9.1.1 $GetPrototypeOf() { return this.$Prototype; } // ECMA262 9.1.2 $SetPrototypeOf(V) { // 1. Return ! OrdinarySetPrototypeOf(O, V). return _singletons.Properties.OrdinarySetPrototypeOf(this.$Realm, this, V); } // ECMA262 9.1.3 $IsExtensible() { // 1. Return ! OrdinaryIsExtensible(O). return (0, _index3.OrdinaryIsExtensible)(this.$Realm, this); } // ECMA262 9.1.4 $PreventExtensions() { // 1. Return ! OrdinaryPreventExtensions(O). return (0, _index3.OrdinaryPreventExtensions)(this.$Realm, this); } // ECMA262 9.1.5 $GetOwnProperty(P) { // 1. Return ! OrdinaryGetOwnProperty(O, P). return _singletons.Properties.OrdinaryGetOwnProperty(this.$Realm, this, P); } // ECMA262 9.1.6 $DefineOwnProperty(P, Desc) { // 1. Return ? OrdinaryDefineOwnProperty(O, P, Desc). return _singletons.Properties.OrdinaryDefineOwnProperty(this.$Realm, this, P, Desc); } // ECMA262 9.1.7 $HasProperty(P) { if (this.unknownProperty !== undefined && this.$GetOwnProperty(P) === undefined) { _index2.AbstractValue.reportIntrospectionError(this, P); throw new _errors.FatalError(); } return (0, _index3.OrdinaryHasProperty)(this.$Realm, this, P); } // ECMA262 9.1.8 $Get(P, Receiver) { // 1. Return ? OrdinaryGet(O, P, Receiver). return (0, _index3.OrdinaryGet)(this.$Realm, this, P, Receiver); } _SafeGetDataPropertyValue(P) { let savedInvariantLevel = this.$Realm.invariantLevel; try { this.$Realm.invariantLevel = 0; let desc = this.$GetOwnProperty(P); if (desc === undefined) { return this.$Realm.intrinsics.undefined; } desc = desc.throwIfNotConcrete(this.$Realm); return desc.value ? desc.value : this.$Realm.intrinsics.undefined; } finally { this.$Realm.invariantLevel = savedInvariantLevel; } } $GetPartial(P, Receiver) { return (0, _index3.OrdinaryGetPartial)(this.$Realm, this, P, Receiver); } // ECMA262 9.1.9 $Set(P, V, Receiver) { // 1. Return ? OrdinarySet(O, P, V, Receiver). return _singletons.Properties.OrdinarySet(this.$Realm, this, P, V, Receiver); } $SetPartial(P, V, Receiver) { return _singletons.Properties.OrdinarySetPartial(this.$Realm, this, P, V, Receiver); } // ECMA262 9.1.10 $Delete(P) { if (this.unknownProperty !== undefined) { // TODO #946: generate a delete from the object _index2.AbstractValue.reportIntrospectionError(this, P); throw new _errors.FatalError(); } // 1. Return ? OrdinaryDelete(O, P). return _singletons.Properties.OrdinaryDelete(this.$Realm, this, P); } // ECMA262 9.1.11 $OwnPropertyKeys(getOwnPropertyKeysEvenIfPartial = false) { return (0, _index3.OrdinaryOwnPropertyKeys)(this.$Realm, this, getOwnPropertyKeysEvenIfPartial); } static refuseSerializationOnPropertyBinding(pb) { if (pb.object.refuseSerialization) return true; if (pb.internalSlot && typeof pb.key === "string" && pb.key[0] === "_") return true; return false; } static isIntrinsicDerivedObject(obj) { return obj instanceof ObjectValue && obj.intrinsicName !== undefined && obj.isScopedTemplate !== undefined; } } exports.default = ObjectValue; _defineProperty(ObjectValue, "trackedPropertyNames", ["_isPartial", "_isLeaked", "_isSimple", "_isFinal", "_simplicityIsTransitive", "_temporalAlias", "$ArrayIteratorNextIndex", "$DateValue", "$Extensible", "$IteratedList", "$IteratedObject", "$IteratedSet", "$IteratedString", "$Map", "$MapData", "$MapNextIndex", "$Prototype", "$SetData", "$SetNextIndex", "$StringIteratorNextIndex", "$WeakMapData", "$WeakSetData"]); _defineProperty(ObjectValue, "trackedPropertyBindingNames", new Map()); //# sourceMappingURL=ObjectValue.js.map