You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
177 lines
5.8 KiB
177 lines
5.8 KiB
'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, max-params |
|
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 |
|
};
|
|
|