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
JavaScript
"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