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('./IsPropertyKey');
  11. var SameValue = require('./SameValue');
  12. var Type = require('./Type');
  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. var oType = Type(O);
  19. if (oType !== 'Undefined' && oType !== 'Object') {
  20. throw new $TypeError('Assertion failed: O must be undefined or an Object');
  21. }
  22. if (typeof extensible !== 'boolean') {
  23. throw new $TypeError('Assertion failed: extensible must be a Boolean');
  24. }
  25. if (!isPropertyDescriptor(Desc)) {
  26. throw new $TypeError('Assertion failed: Desc must be a Property Descriptor');
  27. }
  28. if (typeof current !== 'undefined' && !isPropertyDescriptor(current)) {
  29. throw new $TypeError('Assertion failed: current must be a Property Descriptor, or undefined');
  30. }
  31. if (oType !== 'Undefined' && !IsPropertyKey(P)) {
  32. throw new $TypeError('Assertion failed: if O is not undefined, P must be a Property Key');
  33. }
  34. if (typeof current === 'undefined') {
  35. if (!extensible) {
  36. return false;
  37. }
  38. if (IsGenericDescriptor(Desc) || IsDataDescriptor(Desc)) {
  39. if (oType !== 'Undefined') {
  40. DefineOwnProperty(
  41. IsDataDescriptor,
  42. SameValue,
  43. FromPropertyDescriptor,
  44. O,
  45. P,
  46. {
  47. '[[Configurable]]': Desc['[[Configurable]]'],
  48. '[[Enumerable]]': Desc['[[Enumerable]]'],
  49. '[[Value]]': Desc['[[Value]]'],
  50. '[[Writable]]': Desc['[[Writable]]']
  51. }
  52. );
  53. }
  54. } else {
  55. if (!IsAccessorDescriptor(Desc)) {
  56. throw new $TypeError('Assertion failed: Desc is not an accessor descriptor');
  57. }
  58. if (oType !== 'Undefined') {
  59. return DefineOwnProperty(
  60. IsDataDescriptor,
  61. SameValue,
  62. FromPropertyDescriptor,
  63. O,
  64. P,
  65. Desc
  66. );
  67. }
  68. }
  69. return true;
  70. }
  71. if (IsGenericDescriptor(Desc) && !('[[Configurable]]' in Desc) && !('[[Enumerable]]' in Desc)) {
  72. return true;
  73. }
  74. if (isSamePropertyDescriptor({ SameValue: SameValue }, Desc, current)) {
  75. return true; // removed by ES2017, but should still be correct
  76. }
  77. // "if every field in Desc is absent, return true" can't really match the assertion that it's a Property Descriptor
  78. if (!current['[[Configurable]]']) {
  79. if (Desc['[[Configurable]]']) {
  80. return false;
  81. }
  82. if ('[[Enumerable]]' in Desc && !Desc['[[Enumerable]]'] === !!current['[[Enumerable]]']) {
  83. return false;
  84. }
  85. }
  86. if (IsGenericDescriptor(Desc)) {
  87. // no further validation is required.
  88. } else if (IsDataDescriptor(current) !== IsDataDescriptor(Desc)) {
  89. if (!current['[[Configurable]]']) {
  90. return false;
  91. }
  92. if (IsDataDescriptor(current)) {
  93. if (oType !== 'Undefined') {
  94. DefineOwnProperty(
  95. IsDataDescriptor,
  96. SameValue,
  97. FromPropertyDescriptor,
  98. O,
  99. P,
  100. {
  101. '[[Configurable]]': current['[[Configurable]]'],
  102. '[[Enumerable]]': current['[[Enumerable]]'],
  103. '[[Get]]': undefined
  104. }
  105. );
  106. }
  107. } else if (oType !== 'Undefined') {
  108. DefineOwnProperty(
  109. IsDataDescriptor,
  110. SameValue,
  111. FromPropertyDescriptor,
  112. O,
  113. P,
  114. {
  115. '[[Configurable]]': current['[[Configurable]]'],
  116. '[[Enumerable]]': current['[[Enumerable]]'],
  117. '[[Value]]': undefined
  118. }
  119. );
  120. }
  121. } else if (IsDataDescriptor(current) && IsDataDescriptor(Desc)) {
  122. if (!current['[[Configurable]]'] && !current['[[Writable]]']) {
  123. if ('[[Writable]]' in Desc && Desc['[[Writable]]']) {
  124. return false;
  125. }
  126. if ('[[Value]]' in Desc && !SameValue(Desc['[[Value]]'], current['[[Value]]'])) {
  127. return false;
  128. }
  129. return true;
  130. }
  131. } else if (IsAccessorDescriptor(current) && IsAccessorDescriptor(Desc)) {
  132. if (!current['[[Configurable]]']) {
  133. if ('[[Set]]' in Desc && !SameValue(Desc['[[Set]]'], current['[[Set]]'])) {
  134. return false;
  135. }
  136. if ('[[Get]]' in Desc && !SameValue(Desc['[[Get]]'], current['[[Get]]'])) {
  137. return false;
  138. }
  139. return true;
  140. }
  141. } else {
  142. throw new $TypeError('Assertion failed: current and Desc are not both data, both accessors, or one accessor and one data.');
  143. }
  144. if (oType !== 'Undefined') {
  145. return DefineOwnProperty(
  146. IsDataDescriptor,
  147. SameValue,
  148. FromPropertyDescriptor,
  149. O,
  150. P,
  151. Desc
  152. );
  153. }
  154. return true;
  155. };