'use strict'; var GetIntrinsic = require('get-intrinsic'); var $TypeError = GetIntrinsic('%TypeError%'); var DefineOwnProperty = require('../helpers/DefineOwnProperty'); var isFullyPopulatedPropertyDescriptor = require('../helpers/isFullyPopulatedPropertyDescriptor'); var isPropertyDescriptor = require('../helpers/isPropertyDescriptor'); var FromPropertyDescriptor = require('./FromPropertyDescriptor'); var IsAccessorDescriptor = require('./IsAccessorDescriptor'); var IsDataDescriptor = require('./IsDataDescriptor'); var IsGenericDescriptor = require('./IsGenericDescriptor'); var IsPropertyKey = require('./IsPropertyKey'); var SameValue = require('./SameValue'); var Type = require('./Type'); // https://262.ecma-international.org/13.0/#sec-validateandapplypropertydescriptor // see https://github.com/tc39/ecma262/pull/2468 for ES2022 changes // eslint-disable-next-line max-lines-per-function, max-statements module.exports = function ValidateAndApplyPropertyDescriptor(O, P, extensible, Desc, current) { var oType = Type(O); if (oType !== 'Undefined' && oType !== 'Object') { throw new $TypeError('Assertion failed: O must be undefined or an Object'); } if (!IsPropertyKey(P)) { throw new $TypeError('Assertion failed: P must be a Property Key'); } if (Type(extensible) !== 'Boolean') { throw new $TypeError('Assertion failed: extensible must be a Boolean'); } if (!isPropertyDescriptor({ Type: Type, IsDataDescriptor: IsDataDescriptor, IsAccessorDescriptor: IsAccessorDescriptor }, Desc)) { throw new $TypeError('Assertion failed: Desc must be a Property Descriptor'); } if (Type(current) !== 'Undefined' && !isPropertyDescriptor({ Type: Type, IsDataDescriptor: IsDataDescriptor, IsAccessorDescriptor: IsAccessorDescriptor }, current)) { throw new $TypeError('Assertion failed: current must be a Property Descriptor, or undefined'); } if (Type(current) === 'Undefined') { // step 2 if (!extensible) { return false; // step 2.a } if (oType === 'Undefined') { return true; // step 2.b } if (IsAccessorDescriptor(Desc)) { // step 2.c return DefineOwnProperty( IsDataDescriptor, SameValue, FromPropertyDescriptor, O, P, Desc ); } // step 2.d return DefineOwnProperty( IsDataDescriptor, SameValue, FromPropertyDescriptor, O, P, { '[[Configurable]]': !!Desc['[[Configurable]]'], '[[Enumerable]]': !!Desc['[[Enumerable]]'], '[[Value]]': Desc['[[Value]]'], '[[Writable]]': !!Desc['[[Writable]]'] } ); } // 3. Assert: current is a fully populated Property Descriptor. if (!isFullyPopulatedPropertyDescriptor({ IsAccessorDescriptor: IsAccessorDescriptor, IsDataDescriptor: IsDataDescriptor }, current)) { throw new $TypeError('`current`, when present, must be a fully populated and valid Property Descriptor'); } // 4. If every field in Desc is absent, return true. // this can't really match the assertion that it's a Property Descriptor in our JS implementation // 5. If current.[[Configurable]] is false, then if (!current['[[Configurable]]']) { if ('[[Configurable]]' in Desc && Desc['[[Configurable]]']) { // step 5.a return false; } if ('[[Enumerable]]' in Desc && !SameValue(Desc['[[Enumerable]]'], current['[[Enumerable]]'])) { // step 5.b return false; } if (!IsGenericDescriptor(Desc) && !SameValue(IsAccessorDescriptor(Desc), IsAccessorDescriptor(current))) { // step 5.c return false; } if (IsAccessorDescriptor(current)) { // step 5.d if ('[[Get]]' in Desc && !SameValue(Desc['[[Get]]'], current['[[Get]]'])) { return false; } if ('[[Set]]' in Desc && !SameValue(Desc['[[Set]]'], current['[[Set]]'])) { return false; } } else if (!current['[[Writable]]']) { // step 5.e if ('[[Writable]]' in Desc && Desc['[[Writable]]']) { return false; } if ('[[Value]]' in Desc && !SameValue(Desc['[[Value]]'], current['[[Value]]'])) { return false; } } } // 6. If O is not undefined, then if (oType !== 'Undefined') { var configurable; var enumerable; if (IsDataDescriptor(current) && IsAccessorDescriptor(Desc)) { // step 6.a configurable = ('[[Configurable]]' in Desc ? Desc : current)['[[Configurable]]']; enumerable = ('[[Enumerable]]' in Desc ? Desc : current)['[[Enumerable]]']; // Replace the property named P of object O with an accessor property having [[Configurable]] and [[Enumerable]] attributes as described by current and each other attribute set to its default value. return DefineOwnProperty( IsDataDescriptor, SameValue, FromPropertyDescriptor, O, P, { '[[Configurable]]': !!configurable, '[[Enumerable]]': !!enumerable, '[[Get]]': ('[[Get]]' in Desc ? Desc : current)['[[Get]]'], '[[Set]]': ('[[Set]]' in Desc ? Desc : current)['[[Set]]'] } ); } else if (IsAccessorDescriptor(current) && IsDataDescriptor(Desc)) { configurable = ('[[Configurable]]' in Desc ? Desc : current)['[[Configurable]]']; enumerable = ('[[Enumerable]]' in Desc ? Desc : current)['[[Enumerable]]']; // i. Replace the property named P of object O with a data property having [[Configurable]] and [[Enumerable]] attributes as described by current and each other attribute set to its default value. return DefineOwnProperty( IsDataDescriptor, SameValue, FromPropertyDescriptor, O, P, { '[[Configurable]]': !!configurable, '[[Enumerable]]': !!enumerable, '[[Value]]': ('[[Value]]' in Desc ? Desc : current)['[[Value]]'], '[[Writable]]': !!('[[Writable]]' in Desc ? Desc : current)['[[Writable]]'] } ); } // 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. return DefineOwnProperty( IsDataDescriptor, SameValue, FromPropertyDescriptor, O, P, Desc ); } return true; // step 7 };