UNPKG

prepack

Version:

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

1,233 lines (905 loc) 76.6 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.PropertiesImplementation = void 0; var _realm = require("../realm.js"); var _index = require("../values/index.js"); var _ObjectExpression = require("../evaluators/ObjectExpression.js"); var _environment = require("../environment.js"); var _errors = require("../errors.js"); var _invariant = _interopRequireDefault(require("../invariant.js")); var _index2 = require("./index.js"); var _types = require("@babel/types"); var _singletons = require("../singletons.js"); var _strict = _interopRequireDefault(require("../utils/strict.js")); var _generator = require("../utils/generator.js"); var _index3 = require("../domains/index.js"); var _descriptors = require("../descriptors.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. */ function StringKey(key) { if (key instanceof _index.StringValue) key = key.value; if (typeof key !== "string") { // The generator currently only supports string keys. throw new _errors.FatalError(); } return key; } function InternalDescriptorPropertyToValue(realm, value) { if (value === undefined) return realm.intrinsics.undefined; if (typeof value === "boolean") return new _index.BooleanValue(realm, value); (0, _invariant.default)(value instanceof _index.Value); return value; } function InternalGetPropertiesKey(P) { if (typeof P === "string") { return P; } else if (P instanceof _index.StringValue) { return P.value; } else if (P instanceof _index.SymbolValue) { return P; } // otherwise, undefined } function InternalGetPropertiesMap(O, P) { if (typeof P === "string" || P instanceof _index.StringValue) { return O.properties; } else if (P instanceof _index.SymbolValue) { return O.symbols; } else { (0, _invariant.default)(false); } } function InternalSetProperty(realm, O, P, desc) { let map = InternalGetPropertiesMap(O, P); let key = InternalGetPropertiesKey(P); let propertyBinding = map.get(key); if (propertyBinding === undefined) { propertyBinding = { descriptor: undefined, object: O, key: key }; map.set(key, propertyBinding); } realm.recordModifiedProperty(propertyBinding); propertyBinding.descriptor = desc; } function InternalUpdatedProperty(realm, O, P, oldDesc) { let generator = realm.generator; if (!generator) return; if (!O.isIntrinsic() && O.temporalAlias === undefined) return; if (P instanceof _index.SymbolValue) return; if (P instanceof _index.StringValue) P = P.value; (0, _invariant.default)(!O.mightBeLeakedObject()); // leaked objects are never updated (0, _invariant.default)(!O.mightBeFinalObject()); // final objects are never updated (0, _invariant.default)(typeof P === "string"); let propertyBinding = InternalGetPropertiesMap(O, P).get(P); (0, _invariant.default)(propertyBinding !== undefined); // The callers ensure this let desc = propertyBinding.descriptor; if (desc === undefined) { // The property is being deleted if (O === realm.$GlobalObject) { generator.emitGlobalDelete(P); } else { generator.emitPropertyDelete(O, P); } } else { desc = desc.throwIfNotConcrete(realm); if (oldDesc === undefined) { // The property is being created if (O === realm.$GlobalObject) { if ((0, _index2.IsDataDescriptor)(realm, desc)) { let descValue = desc.value || realm.intrinsics.undefined; if ((0, _types.isValidIdentifier)(P) && !desc.configurable && desc.enumerable && desc.writable) { generator.emitGlobalDeclaration(P, descValue); } else if (desc.configurable && desc.enumerable && desc.writable) { generator.emitGlobalAssignment(P, descValue); } else { generator.emitDefineProperty(O, P, desc); } } else { generator.emitDefineProperty(O, P, desc); } } else { if ((0, _index2.IsDataDescriptor)(realm, desc) && desc.configurable && desc.enumerable && desc.writable) { let descValue = desc.value || realm.intrinsics.undefined; generator.emitPropertyAssignment(O, P, descValue); } else { generator.emitDefineProperty(O, P, desc); } } } else { (0, _invariant.default)(oldDesc instanceof _descriptors.PropertyDescriptor); // The property is being modified if ((0, _descriptors.equalDescriptors)(desc, oldDesc)) { (0, _invariant.default)((0, _index2.IsDataDescriptor)(realm, desc)); let descValue = desc.value || realm.intrinsics.undefined; // only the value is being modified if (O === realm.$GlobalObject) { generator.emitGlobalAssignment(P, descValue); } else { generator.emitPropertyAssignment(O, P, descValue); } } else { generator.emitDefineProperty(O, P, desc, /*isDescChanged*/ true); } } } } function leakDescriptor(realm, desc) { if (desc instanceof _descriptors.AbstractJoinedDescriptor) { if (desc.descriptor1) { leakDescriptor(realm, desc.descriptor1); } if (desc.descriptor2) { leakDescriptor(realm, desc.descriptor2); } } (0, _invariant.default)(desc instanceof _descriptors.PropertyDescriptor); if (desc.value) { if (desc.value instanceof _index.Value) _singletons.Leak.value(realm, desc.value);else if (desc.value !== undefined) { for (let val of desc.value) _singletons.Leak.value(realm, val); } } if (desc.get) { _singletons.Leak.value(realm, desc.get); } if (desc.set) { _singletons.Leak.value(realm, desc.set); } } // Determines if an object with parent O may create its own property P. function parentPermitsChildPropertyCreation(realm, O, P) { if (O.isSimpleObject()) { // Simple object always allow property creation since there are no setters. // Object.prototype is considered simple even though __proto__ is a setter. // TODO: That is probably the incorrect assumption but that is implied everywhere. return true; } let ownDesc = O.$GetOwnProperty(P); if (!ownDesc || ownDesc.mightHaveBeenDeleted()) { // O might not object, so first ask its parent let parent = O.$GetPrototypeOf(); if (!(parent instanceof _index.NullValue)) { parent = parent.throwIfNotConcreteObject(); //TODO #1016: deal with abstract parents if (!parentPermitsChildPropertyCreation(realm, parent, P)) return false; } // Parent is OK, so if O does not object return true if (!ownDesc) return true; // O has no opinion of its ownDesc } (0, _invariant.default)(ownDesc !== undefined); // O might have a property P and so might object if ((0, _index2.IsDataDescriptor)(realm, ownDesc)) { if (ownDesc.writable) { // The grand parent does not object so it is OK that parent does not have P // If parent does have P, it is also OK because it is a writable data property return true; } } // If parent does not have property P, this is too pessimistic, but that is // the caller's problem. return false; } function ensureIsNotFinal(realm, O, P) { if (O.mightNotBeFinalObject()) { return; } // We can't continue because this object is already in its final state if (realm.instantRender.enabled) { realm.instantRenderBailout("Object mutations that require materialization are currently not supported by InstantRender", realm.currentLocation); } else { let error = new _errors.CompilerDiagnostic("Mutating a final object, or an object with unknown properties, after some of those " + "properties have already been used, is not supported.", realm.currentLocation, "PP0026", "FatalError"); realm.handleError(error); throw new _errors.FatalError(); } } function isWidenedValue(v) { if (!(v instanceof _index.AbstractValue)) return false; if (v.kind === "widened" || v.kind === "widened property") return true; for (let a of v.args) { if (isWidenedValue(a)) return true; } return false; } class PropertiesImplementation { // ECMA262 9.1.9.1 OrdinarySet(realm, O, P, V, Receiver) { ensureIsNotFinal(realm, O, P); if (!realm.ignoreLeakLogic && O.mightBeLeakedObject()) { // Leaking is transitive, hence writing a value to a leaked object leaks the value _singletons.Leak.value(realm, V); // The receiver might leak to a getter so if it's not already leaked, we need to leak it. _singletons.Leak.value(realm, Receiver); if (realm.generator !== undefined) { realm.generator.emitPropertyAssignment(Receiver, StringKey(P), V); } return true; } let weakDeletion = V.mightHaveBeenDeleted(); // 1. Assert: IsPropertyKey(P) is true. (0, _invariant.default)((0, _index2.IsPropertyKey)(realm, P), "expected property key"); // 2. Let ownDesc be ? O.[[GetOwnProperty]](P). let ownDesc = O.$GetOwnProperty(P); // 3. If ownDesc is undefined (or might be), then if (!ownDesc || ownDesc.mightHaveBeenDeleted()) { // a. Let parent be ? O.[[GetPrototypeOf]](). let parent = O.$GetPrototypeOf(); // b. If parent is not null, then if (!(parent instanceof _index.NullValue)) { parent = parent.throwIfNotConcreteObject(); //TODO #1016: deal with abstract parents if (!ownDesc) { // i. Return ? parent.[[Set]](P, V, Receiver). return parent.$Set(P, V, Receiver); } // But since we don't know if O has its own property P, the parent might // actually have a say. Give up, unless the parent would be OK with it. if (!parentPermitsChildPropertyCreation(realm, parent, P)) { // TODO: Join the effects depending on if the property was deleted or not. let error = new _errors.CompilerDiagnostic("assignment might or might not invoke a setter", realm.currentLocation, "PP0043", "RecoverableError"); if (realm.handleError(error) !== "Recover") { throw new _errors.FatalError(); } // If we recover, we assume that the parent would've been fine creating the property. } // Since the parent is OK with us creating a local property for O // we can carry on as if there were no parent. } // i. Let ownDesc be the PropertyDescriptor{[[Value]]: undefined, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true}. if (!ownDesc) ownDesc = new _descriptors.PropertyDescriptor({ value: realm.intrinsics.undefined, writable: true, enumerable: true, configurable: true }); } // joined descriptors need special treatment if (ownDesc instanceof _descriptors.AbstractJoinedDescriptor) { let joinCondition = ownDesc.joinCondition; let descriptor2 = ownDesc.descriptor2; ownDesc = ownDesc.descriptor1; let e1 = _singletons.Path.withCondition(joinCondition, () => { return ownDesc !== undefined ? realm.evaluateForEffects(() => new _index.BooleanValue(realm, OrdinarySetHelper()), undefined, "OrdinarySet/1") : (0, _realm.construct_empty_effects)(realm); }); let { result: result1, generator: generator1, modifiedBindings: modifiedBindings1, modifiedProperties: modifiedProperties1, createdObjects: createdObjects1 } = e1; ownDesc = descriptor2; let e2 = _singletons.Path.withInverseCondition(joinCondition, () => { return ownDesc !== undefined ? realm.evaluateForEffects(() => new _index.BooleanValue(realm, OrdinarySetHelper()), undefined, "OrdinarySet/2") : (0, _realm.construct_empty_effects)(realm); }); let { result: result2, generator: generator2, modifiedBindings: modifiedBindings2, modifiedProperties: modifiedProperties2, createdObjects: createdObjects2 } = e2; // Join the effects, creating an abstract view of what happened, regardless // of the actual value of ownDesc.joinCondition. let joinedEffects = _singletons.Join.joinEffects(joinCondition, new _realm.Effects(result1, generator1, modifiedBindings1, modifiedProperties1, createdObjects1), new _realm.Effects(result2, generator2, modifiedBindings2, modifiedProperties2, createdObjects2)); realm.applyEffects(joinedEffects); return _singletons.To.ToBooleanPartial(realm, realm.returnOrThrowCompletion(joinedEffects.result)); } return OrdinarySetHelper(); function OrdinarySetHelper() { (0, _invariant.default)(ownDesc !== undefined); // 4. If IsDataDescriptor(ownDesc) is true, then if ((0, _index2.IsDataDescriptor)(realm, ownDesc)) { // a. If ownDesc.[[Writable]] is false, return false. if (!ownDesc.writable && !weakDeletion) { // The write will fail if the property actually exists if (ownDesc.value && ownDesc.value.mightHaveBeenDeleted()) { // But maybe it does not and thus would succeed. // Since we don't know what will happen, give up for now. // TODO: Join the effects depending on if the property was deleted or not. let error = new _errors.CompilerDiagnostic("assignment might or might not invoke a setter", realm.currentLocation, "PP0043", "RecoverableError"); if (realm.handleError(error) !== "Recover") { throw new _errors.FatalError(); } // If we recover we assume that the property was there. } return false; } // b. If Type(Receiver) is not Object, return false. if (!Receiver.mightBeObject()) return false; (0, _invariant.default)(Receiver instanceof _index.ObjectValue || Receiver instanceof _index.AbstractObjectValue); // c. Let existingDescriptor be ? Receiver.[[GetOwnProperty]](P). let existingDescriptor = Receiver.$GetOwnProperty(P); if (existingDescriptor instanceof _descriptors.AbstractJoinedDescriptor) { if (existingDescriptor.descriptor1 === ownDesc) existingDescriptor = ownDesc;else if (existingDescriptor.descriptor2 === ownDesc) existingDescriptor = ownDesc; } let existingDescValue = !existingDescriptor ? realm.intrinsics.undefined : existingDescriptor.value === undefined ? realm.intrinsics.undefined : existingDescriptor.value; (0, _invariant.default)(existingDescValue instanceof _index.Value); // d. If existingDescriptor is not undefined, then if (existingDescriptor !== undefined) { // i. If IsAccessorDescriptor(existingDescriptor) is true, return false. if ((0, _index2.IsAccessorDescriptor)(realm, existingDescriptor)) { (0, _invariant.default)(!existingDescValue.mightHaveBeenDeleted(), "should not fail until weak deletes of accessors are suppported"); return false; } // ii. If existingDescriptor.[[Writable]] is false, return false. if (!existingDescriptor.writable && !(weakDeletion && existingDescriptor.configurable)) { // If we are not sure the receiver actually has a property P we can't just return false here. if (existingDescValue.mightHaveBeenDeleted()) { (0, _invariant.default)(existingDescValue instanceof _index.AbstractValue); _index.AbstractValue.reportIntrospectionError(existingDescValue); throw new _errors.FatalError(); } return false; } // iii. Let valueDesc be the PropertyDescriptor{[[Value]]: V}. let valueDesc = new _descriptors.PropertyDescriptor({ value: V }); // iv. Return ? Receiver.[[DefineOwnProperty]](P, valueDesc). if (weakDeletion || existingDescValue.mightHaveBeenDeleted()) { // At this point we are not sure that Receiver actually has a property P. // If, however, it has -> P. If, however, it has, we are sure that its a // data property, and that redefining the property with valueDesc will not // change the attributes of the property, so we can reuse the existing // descriptor. valueDesc = existingDescriptor; valueDesc.throwIfNotConcrete(realm).value = V; } return Receiver.$DefineOwnProperty(P, valueDesc); } else { // e. Else Receiver does not currently have a property P, // i. Return ? CreateDataProperty(Receiver, P, V). return _singletons.Create.CreateDataProperty(realm, Receiver, P, V); } } // 5. Assert: IsAccessorDescriptor(ownDesc) is true. (0, _invariant.default)((0, _index2.IsAccessorDescriptor)(realm, ownDesc), "expected accessor"); // 6. Let setter be ownDesc.[[Set]]. let setter = ownDesc.set; // 7. If setter is undefined, return false. if (!setter || setter instanceof _index.UndefinedValue) return false; // 8. Perform ? Call(setter, Receiver, « V »). (0, _index2.Call)(realm, setter.throwIfNotConcrete(), Receiver, [V]); // 9. Return true. return true; } } OrdinarySetPartial(realm, O, P, V, Receiver) { if (!(P instanceof _index.AbstractValue)) return O.$Set(P, V, Receiver); let pIsLoopVar = isWidenedValue(P); let pIsNumeric = _index.Value.isTypeCompatibleWith(P.getType(), _index.NumberValue); // A string coercion might have side-effects. // TODO #1682: We assume that simple objects mean that they don't have a // side-effectful valueOf and toString but that's not enforced. if (P.mightNotBeString() && P.mightNotBeNumber() && !P.isSimpleObject()) { if (realm.isInPureScope()) { // If we're in pure scope, we can leak the key and keep going. // Coercion can only have effects on anything reachable from the key. _singletons.Leak.value(realm, P); } else { let error = new _errors.CompilerDiagnostic("property key might not have a well behaved toString or be a symbol", realm.currentLocation, "PP0002", "RecoverableError"); if (realm.handleError(error) !== "Recover") { throw new _errors.FatalError(); } } } // We assume that simple objects have no getter/setter properties and // that all properties are writable. if (!O.isSimpleObject()) { if (realm.isInPureScope()) { // If we're in pure scope, we can leak the object and leave an // assignment in place. _singletons.Leak.value(realm, Receiver); // We also need to leak the value since it might leak to a setter. _singletons.Leak.value(realm, V); realm.evaluateWithPossibleThrowCompletion(() => { let generator = realm.generator; (0, _invariant.default)(generator); (0, _invariant.default)(P instanceof _index.AbstractValue); generator.emitPropertyAssignment(Receiver, P, V); return realm.intrinsics.undefined; }, _index3.TypesDomain.topVal, _index3.ValuesDomain.topVal); // The emitted assignment might throw at runtime but if it does, that // is handled by evaluateWithPossibleThrowCompletion. Anything that // happens after this, can assume we didn't throw and therefore, // we return true here. return true; } else { let error = new _errors.CompilerDiagnostic("unknown property access might need to invoke a setter", realm.currentLocation, "PP0030", "RecoverableError"); if (realm.handleError(error) !== "Recover") { throw new _errors.FatalError(); } } } // We should never consult the prototype chain for unknown properties. // If it was simple, it would've been an assignment to the receiver. // The only case the Receiver isn't this, if this was a ToObject // coercion from a PrimitiveValue. let abstractOverO = false; if (Receiver instanceof _index.AbstractObjectValue && !Receiver.values.isTop()) { let elements = Receiver.values.getElements(); (0, _invariant.default)(elements); if (elements.has(O)) { abstractOverO = true; } } (0, _invariant.default)(O === Receiver || (0, _index2.HasCompatibleType)(Receiver, _index.PrimitiveValue) || abstractOverO); P = _singletons.To.ToStringAbstract(realm, P); function createTemplate(propName) { return _index.AbstractValue.createFromBinaryOp(realm, "===", propName, new _index.StringValue(realm, ""), undefined, "template for property name condition"); } let prop; if (O.unknownProperty === undefined) { prop = { descriptor: undefined, object: O, key: P }; O.unknownProperty = prop; } else { prop = O.unknownProperty; } realm.recordModifiedProperty(prop); let desc = prop.descriptor; if (desc === undefined) { let newVal = V; if (!(V instanceof _index.UndefinedValue) && !isWidenedValue(P)) { // join V with sentinel, using a property name test as the condition let cond = createTemplate(P); let sentinel = _index.AbstractValue.createFromType(realm, _index.Value, "template for prototype member expression", [Receiver, P]); newVal = _index.AbstractValue.createFromConditionalOp(realm, cond, V, sentinel); } prop.descriptor = new _descriptors.PropertyDescriptor({ writable: true, enumerable: true, configurable: true, value: newVal }); } else { (0, _invariant.default)(desc instanceof _descriptors.PropertyDescriptor, "unknown properties are only created with Set and have equal descriptors"); // join V with current value of O.unknownProperty. I.e. weak update. let oldVal = desc.value; (0, _invariant.default)(oldVal); let newVal = oldVal; if (!(V instanceof _index.UndefinedValue)) { if (isWidenedValue(P)) { newVal = V; // It will be widened later on } else { let cond = createTemplate(P); newVal = _index.AbstractValue.createFromConditionalOp(realm, cond, V, oldVal); } } desc.value = newVal; } // Since we don't know the name of the property we are writing to, we also need // to perform weak updates of all of the known properties. // First clear out O.unknownProperty so that helper routines know its OK to update the properties let savedUnknownProperty = O.unknownProperty; O.unknownProperty = undefined; for (let [key, propertyBinding] of O.properties) { if (pIsLoopVar && pIsNumeric) { // Delete numeric properties and don't do weak updates on other properties. if (key !== +key + "") continue; O.properties.delete(key); continue; } let oldVal = realm.intrinsics.empty; if (propertyBinding.descriptor) { let d = propertyBinding.descriptor.throwIfNotConcrete(realm); if (d.value) { oldVal = d.value; } } let cond = _index.AbstractValue.createFromBinaryOp(realm, "===", P, new _index.StringValue(realm, key)); let newVal = _index.AbstractValue.createFromConditionalOp(realm, cond, V, oldVal); this.OrdinarySet(realm, O, key, newVal, Receiver); } O.unknownProperty = savedUnknownProperty; return true; } // ECMA262 6.2.4.4 FromPropertyDescriptor(realm, Desc) { // 1. If Desc is undefined, return undefined. if (!Desc) return realm.intrinsics.undefined; if (Desc instanceof _descriptors.AbstractJoinedDescriptor) { return _index.AbstractValue.createFromConditionalOp(realm, Desc.joinCondition, this.FromPropertyDescriptor(realm, Desc.descriptor1), this.FromPropertyDescriptor(realm, Desc.descriptor2)); } (0, _invariant.default)(Desc instanceof _descriptors.PropertyDescriptor); // 2. Let obj be ObjectCreate(%ObjectPrototype%). let obj = _singletons.Create.ObjectCreate(realm, realm.intrinsics.ObjectPrototype); // 3. Assert: obj is an extensible ordinary object with no own properties. (0, _invariant.default)(obj.getExtensible(), "expected an extensible object"); (0, _invariant.default)(!obj.properties.size, "expected an object with no own properties"); // 4. If Desc has a [[Value]] field, then let success = true; if (Desc.value !== undefined) { // a. Perform CreateDataProperty(obj, "value", Desc.[[Value]]). success = _singletons.Create.CreateDataProperty(realm, obj, "value", Desc.value) && success; } // 5. If Desc has a [[Writable]] field, then if (Desc.writable !== undefined) { // a. Perform CreateDataProperty(obj, "writable", Desc.[[Writable]]). success = _singletons.Create.CreateDataProperty(realm, obj, "writable", new _index.BooleanValue(realm, Desc.writable)) && success; } // 6. If Desc has a [[Get]] field, then if (Desc.get !== undefined) { // a. Perform CreateDataProperty(obj, "get", Desc.[[Get]]). success = _singletons.Create.CreateDataProperty(realm, obj, "get", Desc.get) && success; } // 7. If Desc has a [[Set]] field, then if (Desc.set !== undefined) { // a. Perform CreateDataProperty(obj, "set", Desc.[[Set]]). success = _singletons.Create.CreateDataProperty(realm, obj, "set", Desc.set) && success; } // 8. If Desc has an [[Enumerable]] field, then if (Desc.enumerable !== undefined) { // a. Perform CreateDataProperty(obj, "enumerable", Desc.[[Enumerable]]). success = _singletons.Create.CreateDataProperty(realm, obj, "enumerable", new _index.BooleanValue(realm, Desc.enumerable)) && success; } // 9. If Desc has a [[Configurable]] field, then if (Desc.configurable !== undefined) { // a. Perform CreateDataProperty(obj, "configurable", Desc.[[Configurable]]). success = _singletons.Create.CreateDataProperty(realm, obj, "configurable", new _index.BooleanValue(realm, Desc.configurable)) && success; } // 10. Assert: all of the above CreateDataProperty operations return true. (0, _invariant.default)(success, "fails to create data property"); // 11. Return obj. return obj; } // OrdinaryDelete(realm, O, P) { // 1. Assert: IsPropertyKey(P) is true. (0, _invariant.default)((0, _index2.IsPropertyKey)(realm, P), "expected a property key"); // 2. Let desc be ? O.[[GetOwnProperty]](P). let desc = O.$GetOwnProperty(P); // 3. If desc is undefined, return true. if (!desc) { ensureIsNotFinal(realm, O, P); if (!realm.ignoreLeakLogic && O.mightBeLeakedObject()) { if (realm.generator !== undefined) { realm.generator.emitPropertyDelete(O, StringKey(P)); } } return true; } desc = desc.throwIfNotConcrete(realm); // 4. If desc.[[Configurable]] is true, then if (desc.configurable) { ensureIsNotFinal(realm, O, P); if (O.mightBeLeakedObject()) { if (realm.generator !== undefined) { realm.generator.emitPropertyDelete(O, StringKey(P)); } return true; } // a. Remove the own property with name P from O. let key = InternalGetPropertiesKey(P); let map = InternalGetPropertiesMap(O, P); let propertyBinding = map.get(key); if (propertyBinding === undefined && O.isPartialObject() && O.isSimpleObject()) { let generator = realm.generator; if (generator) { (0, _invariant.default)(typeof key === "string" || key instanceof _index.SymbolValue); generator.emitPropertyDelete(O, StringKey(key)); return true; } } (0, _invariant.default)(propertyBinding !== undefined); realm.recordModifiedProperty(propertyBinding); propertyBinding.descriptor = undefined; InternalUpdatedProperty(realm, O, P, desc); // b. Return true. return true; } // 5. Return false. return false; } // ECMA262 7.3.8 DeletePropertyOrThrow(realm, O, P) { // 1. Assert: Type(O) is Object. (0, _invariant.default)(O instanceof _index.ObjectValue, "expected an object"); // 2. Assert: IsPropertyKey(P) is true. (0, _invariant.default)((0, _index2.IsPropertyKey)(realm, P), "expected a property key"); // 3. Let success be ? O.[[Delete]](P). let success = O.$Delete(P); // 4. If success is false, throw a TypeError exception. if (!success) { throw realm.createErrorThrowCompletion(realm.intrinsics.TypeError, "couldn't delete property"); } // 5. Return success. return success; } // ECMA262 6.2.4.6 CompletePropertyDescriptor(realm, _Desc) { // 1. Assert: Desc is a Property Descriptor. let Desc = _Desc.throwIfNotConcrete(realm); // 2. Let like be Record{[[Value]]: undefined, [[Writable]]: false, [[Get]]: undefined, [[Set]]: undefined, [[Enumerable]]: false, [[Configurable]]: false}. let like = { value: realm.intrinsics.undefined, get: realm.intrinsics.undefined, set: realm.intrinsics.undefined, writable: false, enumerable: false, configurable: false }; // 3. If either IsGenericDescriptor(Desc) or IsDataDescriptor(Desc) is true, then if ((0, _index2.IsGenericDescriptor)(realm, Desc) || (0, _index2.IsDataDescriptor)(realm, Desc)) { // a. If Desc does not have a [[Value]] field, set Desc.[[Value]] to like.[[Value]]. if (Desc.value === undefined) Desc.value = like.value; // b. If Desc does not have a [[Writable]] field, set Desc.[[Writable]] to like.[[Writable]]. if (Desc.writable === undefined) Desc.writable = like.writable; } else { // 4. Else, // a. If Desc does not have a [[Get]] field, set Desc.[[Get]] to like.[[Get]]. if (Desc.get === undefined) Desc.get = like.get; // b. If Desc does not have a [[Set]] field, set Desc.[[Set]] to like.[[Set]]. if (Desc.set === undefined) Desc.set = like.set; } // 5. If Desc does not have an [[Enumerable]] field, set Desc.[[Enumerable]] to like.[[Enumerable]]. if (Desc.enumerable === undefined) Desc.enumerable = like.enumerable; // 6. If Desc does not have a [[Configurable]] field, set Desc.[[Configurable]] to like.[[Configurable]]. if (Desc.configurable === undefined) Desc.configurable = like.configurable; // 7. Return Desc. return Desc; } // ECMA262 9.1.6.2 IsCompatiblePropertyDescriptor(realm, extensible, Desc, current) { // 1. Return ValidateAndApplyPropertyDescriptor(undefined, undefined, Extensible, Desc, Current). return this.ValidateAndApplyPropertyDescriptor(realm, undefined, undefined, extensible, Desc, current); } // ECMA262 9.1.6.3 ValidateAndApplyPropertyDescriptor(realm, O, P, extensible, _Desc, _current) { let Desc = _Desc; let current = _current; // 1. Assert: If O is not undefined, then IsPropertyKey(P) is true. if (O !== undefined) { (0, _invariant.default)(P !== undefined); (0, _invariant.default)((0, _index2.IsPropertyKey)(realm, P)); } if (current instanceof _descriptors.AbstractJoinedDescriptor) { let jc = current.joinCondition; if (_singletons.Path.implies(jc)) current = current.descriptor1;else if (!_index.AbstractValue.createFromUnaryOp(realm, "!", jc, true).mightNotBeTrue()) current = current.descriptor2; } // 2. If current is undefined, then if (!current) { // a. If extensible is false, return false. if (!extensible) return false; // b. Assert: extensible is true. (0, _invariant.default)(extensible === true, "expected extensible to be true"); if (O !== undefined && P !== undefined) { ensureIsNotFinal(realm, O, P); if (!realm.ignoreLeakLogic && O.mightBeLeakedObject()) { leakDescriptor(realm, Desc); if (realm.generator !== undefined) { realm.generator.emitDefineProperty(O, StringKey(P), Desc.throwIfNotConcrete(realm)); } return true; } } // c. If IsGenericDescriptor(Desc) is true or IsDataDescriptor(Desc) is true, then if ((0, _index2.IsGenericDescriptor)(realm, Desc) || (0, _index2.IsDataDescriptor)(realm, Desc)) { // i. If O is not undefined, create an own data property named P of object O whose [[Value]], // [[Writable]], [[Enumerable]] and [[Configurable]] attribute values are described by Desc. If the // value of an attribute field of Desc is absent, the attribute of the newly created property is set // to its default value. if (O !== undefined) { (0, _invariant.default)(P !== undefined); InternalSetProperty(realm, O, P, new _descriptors.PropertyDescriptor({ value: Desc.value !== undefined ? Desc.value : realm.intrinsics.undefined, writable: Desc.writable !== undefined ? Desc.writable : false, enumerable: Desc.enumerable !== undefined ? Desc.enumerable : false, configurable: Desc.configurable !== undefined ? Desc.configurable : false })); InternalUpdatedProperty(realm, O, P, undefined); } } else { // d. Else Desc must be an accessor Property Descriptor, // i. If O is not undefined, create an own accessor property named P of object O whose [[Get]], // [[Set]], [[Enumerable]] and [[Configurable]] attribute values are described by Desc. If the value // of an attribute field of Desc is absent, the attribute of the newly created property is set to its // default value. if (O !== undefined) { (0, _invariant.default)(P !== undefined); Desc = Desc.throwIfNotConcrete(realm); InternalSetProperty(realm, O, P, new _descriptors.PropertyDescriptor({ get: Desc.get !== undefined ? Desc.get : realm.intrinsics.undefined, set: Desc.set !== undefined ? Desc.set : realm.intrinsics.undefined, enumerable: Desc.enumerable !== undefined ? Desc.enumerable : false, configurable: Desc.configurable !== undefined ? Desc.configurable : false })); InternalUpdatedProperty(realm, O, P, undefined); } } // e. Return true. return true; } current = current.throwIfNotConcrete(realm); Desc = Desc.throwIfNotConcrete(realm); // 3. Return true, if every field in Desc is absent. let allAbsent = true; for (let field in Desc) { if (Desc[field] !== undefined) { allAbsent = false; break; } } if (allAbsent) return true; // 4. Return true, if every field in Desc also occurs in current and the value of every field in Desc is the // same value as the corresponding field in current when compared using the SameValue algorithm. let identical = true; for (let field in Desc) { if (Desc[field] === undefined) { continue; } if (current[field] === undefined) { identical = false; } else { let dval = InternalDescriptorPropertyToValue(realm, Desc[field]); let cval = InternalDescriptorPropertyToValue(realm, current[field]); if (dval instanceof _index.ConcreteValue && cval instanceof _index.ConcreteValue) identical = (0, _index2.SameValue)(realm, dval, cval);else { identical = dval === cval; // This might be false now but true at runtime. This does not // matter because the logic for non identical values will still // do the right thing in the cases below that does not blow up // when dealing with an abstract value. } } if (!identical) break; } // Only return here if the assigment is not temporal. if (identical && (O === realm.$GlobalObject || O !== undefined && !O.isIntrinsic())) { return true; } let mightHaveBeenDeleted = current.value instanceof _index.Value && current.value.mightHaveBeenDeleted(); // 5. If the [[Configurable]] field of current is false, then if (!current.configurable) { (0, _invariant.default)(!mightHaveBeenDeleted, "a non-configurable property can't be deleted"); // a. Return false, if the [[Configurable]] field of Desc is true. if (Desc.configurable) return false; // b. Return false, if the [[Enumerable]] field of Desc is present and the [[Enumerable]] fields of current and Desc are the Boolean negation of each other. if (Desc.enumerable !== undefined && Desc.enumerable !== current.enumerable) { return false; } } current = current.throwIfNotConcrete(realm); Desc = Desc.throwIfNotConcrete(realm); if (O !== undefined && P !== undefined) { ensureIsNotFinal(realm, O, P); if (!realm.ignoreLeakLogic && O.mightBeLeakedObject()) { leakDescriptor(realm, Desc); if (realm.generator !== undefined) { realm.generator.emitDefineProperty(O, StringKey(P), Desc); } return true; } } let oldDesc = current; current = (0, _descriptors.cloneDescriptor)(current); (0, _invariant.default)(current !== undefined); // 6. If IsGenericDescriptor(Desc) is true, no further validation is required. if ((0, _index2.IsGenericDescriptor)(realm, Desc)) {} else if ((0, _index2.IsDataDescriptor)(realm, current) !== (0, _index2.IsDataDescriptor)(realm, Desc)) { // 7. Else if IsDataDescriptor(current) and IsDataDescriptor(Desc) have different results, then // a. Return false, if the [[Configurable]] field of current is false. if (!current.configurable) return false; // b. If IsDataDescriptor(current) is true, then if ((0, _index2.IsDataDescriptor)(realm, current)) { // i. If O is not undefined, convert the property named P of object O from a data property to an accessor property. // Preserve the existing values of the converted property's [[Configurable]] and [[Enumerable]] attributes and set the rest of the property's attributes to their default values. if (O !== undefined) { (0, _invariant.default)(P !== undefined); current.writable = undefined; current.value = undefined; current.get = realm.intrinsics.undefined; current.set = realm.intrinsics.undefined; } } else { // c. Else, // i. If O is not undefined, convert the property named P of object O from an accessor property to a data property. Preserve the existing values of the converted property's [[Configurable]] and [[Enumerable]] attributes and set the rest of the property's attributes to their default values. if (O !== undefined) { (0, _invariant.default)(P !== undefined); current.get = undefined; current.set = undefined; current.writable = false; current.value = realm.intrinsics.undefined; } } } else if ((0, _index2.IsDataDescriptor)(realm, current) && (0, _index2.IsDataDescriptor)(realm, Desc)) { // 8. Else if IsDataDescriptor(current) and IsDataDescriptor(Desc) are both true, then // a. If the [[Configurable]] field of current is false, then if (!current.configurable) { // i. Return false, if the [[Writable]] field of current is false and the [[Writable]] field of Desc is true. if (!current.writable && Desc.writable) return false; // ii. If the [[Writable]] field of current is false, then if (!current.writable) { // 1. Return false, if the [[Value]] field of Desc is present and SameValue(Desc.[[Value]], current.[[Value]]) is false. let descValue = Desc.value || realm.intrinsics.undefined; (0, _invariant.default)(descValue instanceof _index.Value); let currentValue = current.value || realm.intrinsics.undefined; (0, _invariant.default)(currentValue instanceof _index.Value); if (Desc.value && !(0, _index2.SameValuePartial)(realm, descValue, currentValue)) { return false; } } } else {// b. Else the [[Configurable]] field of current is true, so any change is acceptable. } } else { // 9. Else IsAccessorDescriptor(current) and IsAccessorDescriptor(Desc) are both true, // a. If the [[Configurable]] field of current is false, then if (!current.configurable) { // i. Return false, if the [[Set]] field of Desc is present and SameValue(Desc.[[Set]], current.[[Set]]) is false. if (Desc.set && !(0, _index2.SameValuePartial)(realm, Desc.set, current.set || realm.intrinsics.undefined)) return false; // ii. Return false, if the [[Get]] field of Desc is present and SameValue(Desc.[[Get]], current.[[Get]]) is false. if (Desc.get && !(0, _index2.SameValuePartial)(realm, Desc.get, current.get || realm.intrinsics.undefined)) return false; } } if (mightHaveBeenDeleted) { // If the property might have been deleted, we need to ensure that either // the new descriptor overrides any existing values, or always results in // the default value. let unknownEnumerable = Desc.enumerable === undefined && !!current.enumerable; let unknownWritable = Desc.writable === undefined && !!current.writable; if (unknownEnumerable || unknownWritable) { let error = new _errors.CompilerDiagnostic("unknown descriptor attributes on deleted property", realm.currentLocation, "PP0038", "RecoverableError"); if (realm.handleError(error) !== "Recover") { throw new _errors.FatalError(); } } } // 10. If O is not undefined, then if (O !== undefined) { (0, _invariant.default)(P !== undefined); let key = InternalGetPropertiesKey(P); let map = InternalGetPropertiesMap(O, P); let propertyBinding = map.get(key); if (propertyBinding === undefined) { propertyBinding = { descriptor: undefined, object: O, key: key }; realm.recordModifiedProperty(propertyBinding); propertyBinding.descriptor = current; map.set(key, propertyBinding); } else if (propertyBinding.descriptor === undefined) { realm.recordModifiedProperty(propertyBinding); propertyBinding.descriptor = current; } else { realm.recordModifiedProperty(propertyBinding); propertyBinding.descriptor = current; } // a. For each field of Desc that is present, set the corresponding attribute of the property named P of // object O to the value of the field. for (let field in Desc) { if (Desc[field] !== undefined) { current[field] = Desc[field]; } } InternalUpdatedProperty(realm, O, P, oldDesc); } // 11. Return true. return true; } // ECMA262 9.1.6.1 OrdinaryDefineOwnProperty(realm, O, P, Desc) { (0, _invariant.default)(O instanceof _index.ObjectValue); // 1. Let current be ? O.[[GetOwnProperty]](P). let current = O.$GetOwnProperty(P); // 2. Let extensible be the value of the [[Extensible]] internal slot of O. let extensible = O.getExtensible(); // 3. Return ValidateAndApplyPropertyDescriptor(O, P, extensible, Desc, current). return this.ValidateAndApplyPropertyDescriptor(realm, O, P, extensible, Desc, current); } // ECMA262 19.1.2.3.1 ObjectDefineProperties(realm, O, Properties) { // 1. If Type(O) is not Object, throw a TypeError exception. if (O.mightNotBeObject()) { if (O.mightBeObject()) O.throwIfNotConcrete(); throw realm.createErrorThrowCompletion(realm.intrinsics.TypeError); } (0, _invariant.default)(O instanceof _index.ObjectValue || O instanceof _index.AbstractObjectValue); // 2. Let props be ? ToObject(Properties). let props = _singletons.To.ToObject(realm, Properties); // 3. Let keys be ? props.[[OwnPropertyKeys]](). let keys = props.$OwnPropertyKeys(); // 4. Let descriptors be a new empty List. let descriptors = []; // 5. Repeat for each element nextKey of keys in List order, for (let nextKey of keys) { // a. Let propDesc be ? props.[[GetOwnProperty]](nextKey). let propDesc = props.$GetOwnProperty(nextKey); // b. If propDesc is not undefined and propDesc.[[Enumerable]] is true, then if (propDesc && propDesc.throwIfNotConcrete(realm).enumerable) { this.ThrowIfMightHaveBeenDeleted(propDesc); // i. Let descObj be ? Get(props, nextKey). let descObj = (0, _index2.Get)(realm, props, nextKey); // ii. Let desc be ? ToPropertyDescriptor(descObj). let desc = _singletons.To.ToPropertyDescriptor(realm, descObj); // iii. Append the pair (a two element List) consisting of nextKey and desc to the end of descriptors. descriptors.push([nextKey, desc]); } } // 6. For each pair from descriptors in list order, for (let pair of descriptors) { // a. Let P be the first element of pair. let P = pair[0]; // b. Let desc be the second element of pair. let desc = pair[1]; // c. Perform ? DefinePropertyOrThrow(O, P, desc). this.DefinePropertyOrThrow(realm, O, P, desc); } // 7. Return O. return O; } // ECMA262 7.3.3 Set(realm, O, P, V, Throw) { // 1. Assert: Type(O) is Object. // 2. Assert: IsPropertyKey(P) is true. (0, _invariant.default)((0, _index2.IsPropertyKey)(realm, P), "expected property key"); // 3. Assert: Type(Throw) is Boolean. (0, _invariant.default)(typeof Throw === "boolean", "expected boolean"); // 4. Let success be ? O.[[Set]](P, V, O). let success = O.$Set(P, V, O); // 5. If success is false and Throw is true, throw a TypeError exception. if (success === false && Throw === true) { throw realm.createErrorThrowCompletion(realm.intrinsics.TypeError); } // 6. Return success. return success; } // ECMA262 7.3.7 DefinePropertyOrThrow(realm, O, P, desc) { // 1. Assert: Type(O) is Object. // 2. Assert: IsPropertyKey(P) is true. (0, _invariant.default)(typeof P === "string" || (0, _index2.IsPropertyKey)(realm, P), "expected property key"); // 3. Let success be ? O.[[DefineOwnProperty]](P, desc). let success = O.$DefineOwnProperty(P, desc); // 4. If success is false, throw a TypeError exception. if (success === false) { throw realm.createErrorThrowCompletion(realm.intrinsics.TypeError); } // 5. Return success. return success; } // ECMA262 6.2.3.2 PutValue(realm, V, W) { W = W.promoteEmptyToUndefined(); // The following two steps are not necessary as we propagate completions with exceptions. // 1. ReturnIfAbrupt(V). // 2. ReturnIfAbrupt(W). // 3. If Type(V) is not Reference, throw a ReferenceError exception. if (!(V instanceof _environment.Reference)) { throw realm.createErrorThrowCompletion(realm.intrinsics.ReferenceError, "can't put a value to a non-reference"); } // 4. Let base be GetBase(V). let base = _singletons.Environment.GetBase(realm, V); // 5. If IsUnresolvableReference(V) is true, then if (_singletons.Environment.IsUnresolvableReference(realm, V)) { // a. If IsStrictReference(V) is true, then if (_singletons.Environment.IsStrictReference(realm, V)) { // i. Throw a ReferenceError exception. throw realm.createErrorThrowCompletion(realm.intrinsics.ReferenceError); } // b. Let globalObj be GetGlobalObject(). let globalObj = (0, _index2.GetGlobalObject)(realm); // c. Return ? Set(globalObj, GetReferencedName(V), W, false). return this.Set(realm, globalObj, _singletons.Environment.GetReferencedName(realm, V), W, false); } // 6. Else if IsPropertyReference(V) is true, then if (_singletons.Environment.IsPropertyReference(realm, V)) { if (base instanceof _index.AbstractValue) { // Ensure that abstract values are coerced to objects. This might yield // an operation that might throw. base = _singletons.To.ToObject(realm, base); } // a. If HasPrimitiveBase(V) is true, then if (_singletons.Environment.HasPrimitiveBase(realm, V)) { // i. Assert: In realm case, base will never be null or undefined. (0, _invariant.default)(base instanceof _index.Value && !(0, _index2.HasSomeCompatibleType)(base, _index.UndefinedValue, _index.NullValue)); // ii. Set base to ToObject(base). base = _singletons.To.ToObject(realm, base); } (0, _invariant.default)(base instanceof _index.ObjectValue || base instanceof _index.AbstractObjectValue); // b. Let succeeded be ? base.[[Set]](GetReferencedName(V), W, GetThisValue(V)). let succeeded = base.$SetPartial(_singletons.Environment.GetReferencedNamePartial(realm, V), W, (0, _index2.GetThisValue)(realm, V)); // c. If succeeded is false and IsStrictReference(V) is true, throw a TypeError exception. if (succeeded === false && _singletons.Environment.IsStrictReference(realm, V)) { throw realm.createErrorThrowCompletion(realm.intrinsics.TypeError); } // d. Return. return; } // 7. Else base must be an Environment Record, if (base instanceof _environment.EnvironmentRecord) { // a. Return ? base.SetMutableBinding(GetReferencedName(V), W, IsStrictReference(V)) (see 8.1.1). let referencedName = _singletons.Environment.GetReferencedName(realm, V); (0, _invariant.default)(typeof referencedName === "string"); return base.SetMutableBinding(referencedName, W, _singletons.Environment.IsStrictReference(realm, V)); } (0, _invariant.default)(false); } // ECMA262 9.4.2.4 ArraySetLength(realm, A, _Desc) { let Desc = _Desc.throwIfNotConcrete(realm); // 1. If the [[Value]] field of Desc is absent, then let DescValue = Desc.value; if (!DescValue) { // a. Return OrdinaryDefineOwnProperty(A, "lengt