'use strict'; var $TypeError = require('es-errors/type'); var DefineOwnProperty = require('../helpers/DefineOwnProperty'); var isPropertyDescriptor = require('../helpers/records/property-descriptor'); var isSamePropertyDescriptor = require('../helpers/isSamePropertyDescriptor'); 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/6.0/#sec-validateandapplypropertydescriptor // https://262.ecma-international.org/8.0/#sec-validateandapplypropertydescriptor // eslint-disable-next-line max-lines-per-function, max-statements module.exports = function ValidateAndApplyPropertyDescriptor(O, P, extensible, Desc, current) { // this uses the ES2017+ logic, since it fixes a number of bugs in the ES2015 logic. var oType = Type(O); if (oType !== 'Undefined' && oType !== 'Object') { throw new $TypeError('Assertion failed: O must be undefined or an Object'); } if (typeof extensible !== 'boolean') { throw new $TypeError('Assertion failed: extensible must be a Boolean'); } if (!isPropertyDescriptor(Desc)) { throw new $TypeError('Assertion failed: Desc must be a Property Descriptor'); } if (typeof current !== 'undefined' && !isPropertyDescriptor(current)) { throw new $TypeError('Assertion failed: current must be a Property Descriptor, or undefined'); } if (oType !== 'Undefined' && !IsPropertyKey(P)) { throw new $TypeError('Assertion failed: if O is not undefined, P must be a Property Key'); } if (typeof current === 'undefined') { if (!extensible) { return false; } if (IsGenericDescriptor(Desc) || IsDataDescriptor(Desc)) { if (oType !== 'Undefined') { DefineOwnProperty( IsDataDescriptor, SameValue, FromPropertyDescriptor, O, P, { '[[Configurable]]': Desc['[[Configurable]]'], '[[Enumerable]]': Desc['[[Enumerable]]'], '[[Value]]': Desc['[[Value]]'], '[[Writable]]': Desc['[[Writable]]'] } ); } } else { if (!IsAccessorDescriptor(Desc)) { throw new $TypeError('Assertion failed: Desc is not an accessor descriptor'); } if (oType !== 'Undefined') { return DefineOwnProperty( IsDataDescriptor, SameValue, FromPropertyDescriptor, O, P, Desc ); } } return true; } if (IsGenericDescriptor(Desc) && !('[[Configurable]]' in Desc) && !('[[Enumerable]]' in Desc)) { return true; } if (isSamePropertyDescriptor({ SameValue: SameValue }, Desc, current)) { return true; // removed by ES2017, but should still be correct } // "if every field in Desc is absent, return true" can't really match the assertion that it's a Property Descriptor if (!current['[[Configurable]]']) { if (Desc['[[Configurable]]']) { return false; } if ('[[Enumerable]]' in Desc && !Desc['[[Enumerable]]'] === !!current['[[Enumerable]]']) { return false; } } if (IsGenericDescriptor(Desc)) { // no further validation is required. } else if (IsDataDescriptor(current) !== IsDataDescriptor(Desc)) { if (!current['[[Configurable]]']) { return false; } if (IsDataDescriptor(current)) { if (oType !== 'Undefined') { DefineOwnProperty( IsDataDescriptor, SameValue, FromPropertyDescriptor, O, P, { '[[Configurable]]': current['[[Configurable]]'], '[[Enumerable]]': current['[[Enumerable]]'], '[[Get]]': undefined } ); } } else if (oType !== 'Undefined') { DefineOwnProperty( IsDataDescriptor, SameValue, FromPropertyDescriptor, O, P, { '[[Configurable]]': current['[[Configurable]]'], '[[Enumerable]]': current['[[Enumerable]]'], '[[Value]]': undefined } ); } } else if (IsDataDescriptor(current) && IsDataDescriptor(Desc)) { if (!current['[[Configurable]]'] && !current['[[Writable]]']) { if ('[[Writable]]' in Desc && Desc['[[Writable]]']) { return false; } if ('[[Value]]' in Desc && !SameValue(Desc['[[Value]]'], current['[[Value]]'])) { return false; } return true; } } else if (IsAccessorDescriptor(current) && IsAccessorDescriptor(Desc)) { if (!current['[[Configurable]]']) { if ('[[Set]]' in Desc && !SameValue(Desc['[[Set]]'], current['[[Set]]'])) { return false; } if ('[[Get]]' in Desc && !SameValue(Desc['[[Get]]'], current['[[Get]]'])) { return false; } return true; } } else { throw new $TypeError('Assertion failed: current and Desc are not both data, both accessors, or one accessor and one data.'); } if (oType !== 'Undefined') { return DefineOwnProperty( IsDataDescriptor, SameValue, FromPropertyDescriptor, O, P, Desc ); } return true; };