PropertyDescriptor.cpp 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227
  1. /*
  2. * Copyright (c) 2021-2022, Linus Groh <linusg@serenityos.org>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include <LibJS/Runtime/Error.h>
  7. #include <LibJS/Runtime/FunctionObject.h>
  8. #include <LibJS/Runtime/GlobalObject.h>
  9. #include <LibJS/Runtime/Object.h>
  10. #include <LibJS/Runtime/PropertyDescriptor.h>
  11. #include <LibJS/Runtime/Value.h>
  12. namespace JS {
  13. // 6.2.5.1 IsAccessorDescriptor ( Desc ), https://tc39.es/ecma262/#sec-isaccessordescriptor
  14. bool PropertyDescriptor::is_accessor_descriptor() const
  15. {
  16. // 1. If Desc is undefined, return false.
  17. // 2. If Desc has a [[Get]] field, return true.
  18. if (get.has_value())
  19. return true;
  20. // 3. If Desc has a [[Set]] field, return true.
  21. if (set.has_value())
  22. return true;
  23. // 4. Return false.
  24. return false;
  25. }
  26. // 6.2.5.2 IsDataDescriptor ( Desc ), https://tc39.es/ecma262/#sec-isdatadescriptor
  27. bool PropertyDescriptor::is_data_descriptor() const
  28. {
  29. // 1. If Desc is undefined, return false.
  30. // 2. If Desc has a [[Value]] field, return true.
  31. if (value.has_value())
  32. return true;
  33. // 3. If Desc has a [[Writable]] field, return true.
  34. if (writable.has_value())
  35. return true;
  36. // 4. Return false.
  37. return false;
  38. }
  39. // 6.2.5.3 IsGenericDescriptor ( Desc ), https://tc39.es/ecma262/#sec-isgenericdescriptor
  40. bool PropertyDescriptor::is_generic_descriptor() const
  41. {
  42. // 1. If Desc is undefined, return false.
  43. // 2. If IsAccessorDescriptor(Desc) is true, return false.
  44. if (is_accessor_descriptor())
  45. return false;
  46. // 3. If IsDataDescriptor(Desc) is true, return false.
  47. if (is_data_descriptor())
  48. return false;
  49. // 4. Return true.
  50. return true;
  51. }
  52. // 6.2.5.4 FromPropertyDescriptor ( Desc ), https://tc39.es/ecma262/#sec-frompropertydescriptor
  53. Value from_property_descriptor(GlobalObject& global_object, Optional<PropertyDescriptor> const& property_descriptor)
  54. {
  55. if (!property_descriptor.has_value())
  56. return js_undefined();
  57. auto& vm = global_object.vm();
  58. auto* object = Object::create(global_object, global_object.object_prototype());
  59. if (property_descriptor->value.has_value())
  60. MUST(object->create_data_property_or_throw(vm.names.value, *property_descriptor->value));
  61. if (property_descriptor->writable.has_value())
  62. MUST(object->create_data_property_or_throw(vm.names.writable, Value(*property_descriptor->writable)));
  63. if (property_descriptor->get.has_value())
  64. MUST(object->create_data_property_or_throw(vm.names.get, *property_descriptor->get ? Value(*property_descriptor->get) : js_undefined()));
  65. if (property_descriptor->set.has_value())
  66. MUST(object->create_data_property_or_throw(vm.names.set, *property_descriptor->set ? Value(*property_descriptor->set) : js_undefined()));
  67. if (property_descriptor->enumerable.has_value())
  68. MUST(object->create_data_property_or_throw(vm.names.enumerable, Value(*property_descriptor->enumerable)));
  69. if (property_descriptor->configurable.has_value())
  70. MUST(object->create_data_property_or_throw(vm.names.configurable, Value(*property_descriptor->configurable)));
  71. return object;
  72. }
  73. // 6.2.5.5 ToPropertyDescriptor ( Obj ), https://tc39.es/ecma262/#sec-topropertydescriptor
  74. ThrowCompletionOr<PropertyDescriptor> to_property_descriptor(GlobalObject& global_object, Value argument)
  75. {
  76. auto& vm = global_object.vm();
  77. // 1. If Type(Obj) is not Object, throw a TypeError exception.
  78. if (!argument.is_object())
  79. return vm.throw_completion<TypeError>(global_object, ErrorType::NotAnObject, argument.to_string_without_side_effects());
  80. auto& object = argument.as_object();
  81. // 2. Let desc be a new Property Descriptor that initially has no fields.
  82. PropertyDescriptor descriptor;
  83. // 3. Let hasEnumerable be ? HasProperty(Obj, "enumerable").
  84. auto has_enumerable = TRY(object.has_property(vm.names.enumerable));
  85. // 4. If hasEnumerable is true, then
  86. if (has_enumerable) {
  87. // a. Let enumerable be ToBoolean(? Get(Obj, "enumerable")).
  88. auto enumerable = TRY(object.get(vm.names.enumerable)).to_boolean();
  89. // b. Set desc.[[Enumerable]] to enumerable.
  90. descriptor.enumerable = enumerable;
  91. }
  92. // 5. Let hasConfigurable be ? HasProperty(Obj, "configurable").
  93. auto has_configurable = TRY(object.has_property(vm.names.configurable));
  94. // 6. If hasConfigurable is true, then
  95. if (has_configurable) {
  96. // a. Let configurable be ToBoolean(? Get(Obj, "configurable")).
  97. auto configurable = TRY(object.get(vm.names.configurable)).to_boolean();
  98. // b. Set desc.[[Configurable]] to configurable.
  99. descriptor.configurable = configurable;
  100. }
  101. // 7. Let hasValue be ? HasProperty(Obj, "value").
  102. auto has_value = TRY(object.has_property(vm.names.value));
  103. // 8. If hasValue is true, then
  104. if (has_value) {
  105. // a. Let value be ? Get(Obj, "value").
  106. auto value = TRY(object.get(vm.names.value));
  107. // b. Set desc.[[Value]] to value.
  108. descriptor.value = value;
  109. }
  110. // 9. Let hasWritable be ? HasProperty(Obj, "writable").
  111. auto has_writable = TRY(object.has_property(vm.names.writable));
  112. // 10. If hasWritable is true, then
  113. if (has_writable) {
  114. // a. Let writable be ToBoolean(? Get(Obj, "writable")).
  115. auto writable = TRY(object.get(vm.names.writable)).to_boolean();
  116. // b. Set desc.[[Writable]] to writable.
  117. descriptor.writable = writable;
  118. }
  119. // 11. Let hasGet be ? HasProperty(Obj, "get").
  120. auto has_get = TRY(object.has_property(vm.names.get));
  121. // 12. If hasGet is true, then
  122. if (has_get) {
  123. // a. Let getter be ? Get(Obj, "get").
  124. auto getter = TRY(object.get(vm.names.get));
  125. // b. If IsCallable(getter) is false and getter is not undefined, throw a TypeError exception.
  126. if (!getter.is_function() && !getter.is_undefined())
  127. return vm.throw_completion<TypeError>(global_object, ErrorType::AccessorBadField, "get");
  128. // c. Set desc.[[Get]] to getter.
  129. descriptor.get = getter.is_function() ? &getter.as_function() : nullptr;
  130. }
  131. // 13. Let hasSet be ? HasProperty(Obj, "set").
  132. auto has_set = TRY(object.has_property(vm.names.set));
  133. // 14. If hasSet is true, then
  134. if (has_set) {
  135. // a. Let setter be ? Get(Obj, "set").
  136. auto setter = TRY(object.get(vm.names.set));
  137. // b. If IsCallable(setter) is false and setter is not undefined, throw a TypeError exception.
  138. if (!setter.is_function() && !setter.is_undefined())
  139. return vm.throw_completion<TypeError>(global_object, ErrorType::AccessorBadField, "set");
  140. // c. Set desc.[[Set]] to setter.
  141. descriptor.set = setter.is_function() ? &setter.as_function() : nullptr;
  142. }
  143. // 15. If desc has a [[Get]] field or desc has a [[Set]] field, then
  144. if (descriptor.get.has_value() || descriptor.set.has_value()) {
  145. // a. If desc has a [[Value]] field or desc has a [[Writable]] field, throw a TypeError exception.
  146. if (descriptor.value.has_value() || descriptor.writable.has_value())
  147. return vm.throw_completion<TypeError>(global_object, ErrorType::AccessorValueOrWritable);
  148. }
  149. // 16. Return desc.
  150. return descriptor;
  151. }
  152. // 6.2.5.6 CompletePropertyDescriptor ( Desc ), https://tc39.es/ecma262/#sec-completepropertydescriptor
  153. void PropertyDescriptor::complete()
  154. {
  155. if (is_generic_descriptor() || is_data_descriptor()) {
  156. if (!value.has_value())
  157. value = Value {};
  158. if (!writable.has_value())
  159. writable = false;
  160. } else {
  161. if (!get.has_value())
  162. get = nullptr;
  163. if (!set.has_value())
  164. set = nullptr;
  165. }
  166. if (!enumerable.has_value())
  167. enumerable = false;
  168. if (!configurable.has_value())
  169. configurable = false;
  170. }
  171. // Non-standard, just a convenient way to get from three Optional<bool> to PropertyAttributes.
  172. PropertyAttributes PropertyDescriptor::attributes() const
  173. {
  174. u8 attributes = 0;
  175. if (writable.value_or(false))
  176. attributes |= Attribute::Writable;
  177. if (enumerable.value_or(false))
  178. attributes |= Attribute::Enumerable;
  179. if (configurable.value_or(false))
  180. attributes |= Attribute::Configurable;
  181. return { attributes };
  182. }
  183. }