ValidateAndApplyPropertyDescriptor.js 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  1. 'use strict';
  2. var $TypeError = require('es-errors/type');
  3. var DefineOwnProperty = require('../helpers/DefineOwnProperty');
  4. var isPropertyDescriptor = require('../helpers/records/property-descriptor');
  5. var isSamePropertyDescriptor = require('../helpers/isSamePropertyDescriptor');
  6. var FromPropertyDescriptor = require('./FromPropertyDescriptor');
  7. var IsAccessorDescriptor = require('./IsAccessorDescriptor');
  8. var IsDataDescriptor = require('./IsDataDescriptor');
  9. var IsGenericDescriptor = require('./IsGenericDescriptor');
  10. var isPropertyKey = require('../helpers/isPropertyKey');
  11. var SameValue = require('./SameValue');
  12. var isObject = require('../helpers/isObject');
  13. // https://262.ecma-international.org/6.0/#sec-validateandapplypropertydescriptor
  14. // https://262.ecma-international.org/8.0/#sec-validateandapplypropertydescriptor
  15. // eslint-disable-next-line max-lines-per-function, max-statements
  16. module.exports = function ValidateAndApplyPropertyDescriptor(O, P, extensible, Desc, current) {
  17. // this uses the ES2017+ logic, since it fixes a number of bugs in the ES2015 logic.
  18. if (typeof O !== 'undefined' && !isObject(O)) {
  19. throw new $TypeError('Assertion failed: O must be undefined or an Object');
  20. }
  21. if (typeof extensible !== 'boolean') {
  22. throw new $TypeError('Assertion failed: extensible must be a Boolean');
  23. }
  24. if (!isPropertyDescriptor(Desc)) {
  25. throw new $TypeError('Assertion failed: Desc must be a Property Descriptor');
  26. }
  27. if (typeof current !== 'undefined' && !isPropertyDescriptor(current)) {
  28. throw new $TypeError('Assertion failed: current must be a Property Descriptor, or undefined');
  29. }
  30. if (typeof O !== 'undefined' && !isPropertyKey(P)) {
  31. throw new $TypeError('Assertion failed: if O is not undefined, P must be a Property Key');
  32. }
  33. if (typeof current === 'undefined') {
  34. if (!extensible) {
  35. return false;
  36. }
  37. if (IsGenericDescriptor(Desc) || IsDataDescriptor(Desc)) {
  38. if (typeof O !== 'undefined') {
  39. DefineOwnProperty(
  40. IsDataDescriptor,
  41. SameValue,
  42. FromPropertyDescriptor,
  43. O,
  44. P,
  45. {
  46. '[[Configurable]]': Desc['[[Configurable]]'],
  47. '[[Enumerable]]': Desc['[[Enumerable]]'],
  48. '[[Value]]': Desc['[[Value]]'],
  49. '[[Writable]]': Desc['[[Writable]]']
  50. }
  51. );
  52. }
  53. } else {
  54. if (!IsAccessorDescriptor(Desc)) {
  55. throw new $TypeError('Assertion failed: Desc is not an accessor descriptor');
  56. }
  57. if (typeof O !== 'undefined') {
  58. return DefineOwnProperty(
  59. IsDataDescriptor,
  60. SameValue,
  61. FromPropertyDescriptor,
  62. O,
  63. P,
  64. Desc
  65. );
  66. }
  67. }
  68. return true;
  69. }
  70. if (IsGenericDescriptor(Desc) && !('[[Configurable]]' in Desc) && !('[[Enumerable]]' in Desc)) {
  71. return true;
  72. }
  73. if (isSamePropertyDescriptor({ SameValue: SameValue }, Desc, current)) {
  74. return true; // removed by ES2017, but should still be correct
  75. }
  76. // "if every field in Desc is absent, return true" can't really match the assertion that it's a Property Descriptor
  77. if (!current['[[Configurable]]']) {
  78. if (Desc['[[Configurable]]']) {
  79. return false;
  80. }
  81. if ('[[Enumerable]]' in Desc && !Desc['[[Enumerable]]'] === !!current['[[Enumerable]]']) {
  82. return false;
  83. }
  84. }
  85. if (IsGenericDescriptor(Desc)) {
  86. // no further validation is required.
  87. } else if (IsDataDescriptor(current) !== IsDataDescriptor(Desc)) {
  88. if (!current['[[Configurable]]']) {
  89. return false;
  90. }
  91. if (IsDataDescriptor(current)) {
  92. if (typeof O !== 'undefined') {
  93. DefineOwnProperty(
  94. IsDataDescriptor,
  95. SameValue,
  96. FromPropertyDescriptor,
  97. O,
  98. P,
  99. {
  100. '[[Configurable]]': current['[[Configurable]]'],
  101. '[[Enumerable]]': current['[[Enumerable]]'],
  102. '[[Get]]': undefined
  103. }
  104. );
  105. }
  106. } else if (typeof O !== 'undefined') {
  107. DefineOwnProperty(
  108. IsDataDescriptor,
  109. SameValue,
  110. FromPropertyDescriptor,
  111. O,
  112. P,
  113. {
  114. '[[Configurable]]': current['[[Configurable]]'],
  115. '[[Enumerable]]': current['[[Enumerable]]'],
  116. '[[Value]]': undefined
  117. }
  118. );
  119. }
  120. } else if (IsDataDescriptor(current) && IsDataDescriptor(Desc)) {
  121. if (!current['[[Configurable]]'] && !current['[[Writable]]']) {
  122. if ('[[Writable]]' in Desc && Desc['[[Writable]]']) {
  123. return false;
  124. }
  125. if ('[[Value]]' in Desc && !SameValue(Desc['[[Value]]'], current['[[Value]]'])) {
  126. return false;
  127. }
  128. return true;
  129. }
  130. } else if (IsAccessorDescriptor(current) && IsAccessorDescriptor(Desc)) {
  131. if (!current['[[Configurable]]']) {
  132. if ('[[Set]]' in Desc && !SameValue(Desc['[[Set]]'], current['[[Set]]'])) {
  133. return false;
  134. }
  135. if ('[[Get]]' in Desc && !SameValue(Desc['[[Get]]'], current['[[Get]]'])) {
  136. return false;
  137. }
  138. return true;
  139. }
  140. } else {
  141. throw new $TypeError('Assertion failed: current and Desc are not both data, both accessors, or one accessor and one data.');
  142. }
  143. if (typeof O !== 'undefined') {
  144. return DefineOwnProperty(
  145. IsDataDescriptor,
  146. SameValue,
  147. FromPropertyDescriptor,
  148. O,
  149. P,
  150. Desc
  151. );
  152. }
  153. return true;
  154. };