Array.cpp 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236
  1. /*
  2. * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
  3. * Copyright (c) 2020-2021, Linus Groh <linusg@serenityos.org>
  4. *
  5. * SPDX-License-Identifier: BSD-2-Clause
  6. */
  7. #include <AK/Function.h>
  8. #include <LibJS/Runtime/Array.h>
  9. #include <LibJS/Runtime/Error.h>
  10. #include <LibJS/Runtime/GlobalObject.h>
  11. namespace JS {
  12. // 10.4.2.2 ArrayCreate ( length [ , proto ] ), https://tc39.es/ecma262/#sec-arraycreate
  13. Array* Array::create(GlobalObject& global_object, size_t length, Object* prototype)
  14. {
  15. auto& vm = global_object.vm();
  16. if (length > NumericLimits<u32>::max()) {
  17. vm.throw_exception<RangeError>(global_object, ErrorType::InvalidLength, "array");
  18. return nullptr;
  19. }
  20. if (!prototype)
  21. prototype = global_object.array_prototype();
  22. auto* array = global_object.heap().allocate<Array>(global_object, *prototype);
  23. array->internal_define_own_property(vm.names.length, { .value = Value(length), .writable = true, .enumerable = false, .configurable = false });
  24. return array;
  25. }
  26. // 7.3.17 CreateArrayFromList ( elements ), https://tc39.es/ecma262/#sec-createarrayfromlist
  27. Array* Array::create_from(GlobalObject& global_object, Vector<Value> const& elements)
  28. {
  29. // 1. Assert: elements is a List whose elements are all ECMAScript language values.
  30. // 2. Let array be ! ArrayCreate(0).
  31. auto* array = Array::create(global_object, 0);
  32. // 3. Let n be 0.
  33. // 4. For each element e of elements, do
  34. for (u32 n = 0; n < elements.size(); ++n) {
  35. // a. Perform ! CreateDataPropertyOrThrow(array, ! ToString(𝔽(n)), e).
  36. array->create_data_property_or_throw(n, elements[n]);
  37. // b. Set n to n + 1.
  38. }
  39. // 5. Return array.
  40. return array;
  41. }
  42. Array::Array(Object& prototype)
  43. : Object(prototype)
  44. {
  45. }
  46. Array::~Array()
  47. {
  48. }
  49. // 10.4.2.4 ArraySetLength ( A, Desc ), https://tc39.es/ecma262/#sec-arraysetlength
  50. bool Array::set_length(PropertyDescriptor const& property_descriptor)
  51. {
  52. auto& global_object = this->global_object();
  53. auto& vm = this->vm();
  54. // 1. If Desc.[[Value]] is absent, then
  55. // a. Return OrdinaryDefineOwnProperty(A, "length", Desc).
  56. // 2. Let newLenDesc be a copy of Desc.
  57. // NOTE: Handled by step 16
  58. size_t new_length = indexed_properties().array_like_size();
  59. if (property_descriptor.value.has_value()) {
  60. // 3. Let newLen be ? ToUint32(Desc.[[Value]]).
  61. new_length = property_descriptor.value->to_u32(global_object);
  62. if (vm.exception())
  63. return {};
  64. // 4. Let numberLen be ? ToNumber(Desc.[[Value]]).
  65. auto number_length = property_descriptor.value->to_number(global_object);
  66. if (vm.exception())
  67. return {};
  68. // 5. If newLen is not the same value as numberLen, throw a RangeError exception.
  69. if (new_length != number_length.as_double()) {
  70. vm.throw_exception<RangeError>(global_object, ErrorType::InvalidLength, "array");
  71. return {};
  72. }
  73. }
  74. // 6. Set newLenDesc.[[Value]] to newLen.
  75. // 7. Let oldLenDesc be OrdinaryGetOwnProperty(A, "length").
  76. // 8. Assert: ! IsDataDescriptor(oldLenDesc) is true.
  77. // 9. Assert: oldLenDesc.[[Configurable]] is false.
  78. // 10. Let oldLen be oldLenDesc.[[Value]].
  79. // 11. If newLen ≥ oldLen, then
  80. // a. Return OrdinaryDefineOwnProperty(A, "length", newLenDesc).
  81. // 12. If oldLenDesc.[[Writable]] is false, return false.
  82. // NOTE: Handled by step 16
  83. // 13. If newLenDesc.[[Writable]] is absent or has the value true, let newWritable be true.
  84. // 14. Else,
  85. // a. NOTE: Setting the [[Writable]] attribute to false is deferred in case any elements cannot be deleted.
  86. // b. Let newWritable be false.
  87. auto new_writable = property_descriptor.writable.value_or(true);
  88. // c. Set newLenDesc.[[Writable]] to true.
  89. // 15. Let succeeded be ! OrdinaryDefineOwnProperty(A, "length", newLenDesc).
  90. // 16. If succeeded is false, return false.
  91. // NOTE: Because the length property does not actually exist calling OrdinaryDefineOwnProperty
  92. // will result in unintended behavior, so instead we only implement here the small subset of
  93. // checks performed inside of it that would have mattered to us:
  94. // 10.1.6.3 ValidateAndApplyPropertyDescriptor ( O, P, extensible, Desc, current ), https://tc39.es/ecma262/#sec-validateandapplypropertydescriptor
  95. // 4. If current.[[Configurable]] is false, then
  96. // a. If Desc.[[Configurable]] is present and its value is true, return false.
  97. if (property_descriptor.configurable.has_value() && *property_descriptor.configurable)
  98. return false;
  99. // b. If Desc.[[Enumerable]] is present and ! SameValue(Desc.[[Enumerable]], current.[[Enumerable]]) is false, return false.
  100. if (property_descriptor.enumerable.has_value() && *property_descriptor.enumerable)
  101. return false;
  102. // 6. Else if ! SameValue(! IsDataDescriptor(current), ! IsDataDescriptor(Desc)) is false, then
  103. if (property_descriptor.is_accessor_descriptor()) {
  104. // a. If current.[[Configurable]] is false, return false.
  105. return false;
  106. }
  107. // 7. Else if IsDataDescriptor(current) and IsDataDescriptor(Desc) are both true, then
  108. // a. If current.[[Configurable]] is false and current.[[Writable]] is false, then
  109. if (!m_length_writable) {
  110. // i. If Desc.[[Writable]] is present and Desc.[[Writable]] is true, return false.
  111. if (property_descriptor.writable.has_value() && *property_descriptor.writable)
  112. return false;
  113. // ii. If Desc.[[Value]] is present and SameValue(Desc.[[Value]], current.[[Value]]) is false, return false.
  114. if (new_length != indexed_properties().array_like_size())
  115. return false;
  116. }
  117. // 17. For each own property key P of A that is an array index, whose numeric value is greater than or equal to newLen, in descending numeric index order, do
  118. // a. Let deleteSucceeded be ! A.[[Delete]](P).
  119. // b. If deleteSucceeded is false, then
  120. // i. Set newLenDesc.[[Value]] to ! ToUint32(P) + 1𝔽.
  121. bool success = indexed_properties().set_array_like_size(new_length);
  122. // ii. If newWritable is false, set newLenDesc.[[Writable]] to false.
  123. // iii. Perform ! OrdinaryDefineOwnProperty(A, "length", newLenDesc).
  124. // NOTE: Handled by step 18
  125. // 18. If newWritable is false, then
  126. // a. Set succeeded to ! OrdinaryDefineOwnProperty(A, "length", PropertyDescriptor { [[Writable]]: false }).
  127. // b. Assert: succeeded is true.
  128. if (!new_writable)
  129. m_length_writable = false;
  130. // NOTE: Continuation of step #17
  131. // iv. Return false.
  132. if (!success)
  133. return false;
  134. // 19. Return true.
  135. return true;
  136. }
  137. // NON-STANDARD: Used to return the value of the ephemeral length property
  138. Optional<PropertyDescriptor> Array::internal_get_own_property(PropertyName const& property_name) const
  139. {
  140. auto& vm = this->vm();
  141. if (property_name.is_string() && property_name.as_string() == vm.names.length.as_string())
  142. return PropertyDescriptor { .value = Value(indexed_properties().array_like_size()), .writable = m_length_writable, .enumerable = false, .configurable = false };
  143. return Object::internal_get_own_property(property_name);
  144. }
  145. // 10.4.2.1 [[DefineOwnProperty]] ( P, Desc ), https://tc39.es/ecma262/#sec-array-exotic-objects-defineownproperty-p-desc
  146. bool Array::internal_define_own_property(PropertyName const& property_name, PropertyDescriptor const& property_descriptor)
  147. {
  148. auto& vm = this->vm();
  149. // 1. Assert: IsPropertyKey(P) is true.
  150. VERIFY(property_name.is_valid());
  151. // 2. If P is "length", then
  152. if (property_name.is_string() && property_name.as_string() == vm.names.length.as_string()) {
  153. // a. Return ? ArraySetLength(A, Desc).
  154. return set_length(property_descriptor);
  155. }
  156. // 3. Else if P is an array index, then
  157. if (property_name.is_number()) {
  158. // a. Let oldLenDesc be OrdinaryGetOwnProperty(A, "length").
  159. // b. Assert: ! IsDataDescriptor(oldLenDesc) is true.
  160. // c. Assert: oldLenDesc.[[Configurable]] is false.
  161. // d. Let oldLen be oldLenDesc.[[Value]].
  162. // e. Assert: oldLen is a non-negative integral Number.
  163. // f. Let index be ! ToUint32(P).
  164. // g. If index ≥ oldLen and oldLenDesc.[[Writable]] is false, return false.
  165. if (property_name.as_number() >= indexed_properties().array_like_size() && !m_length_writable)
  166. return false;
  167. // h. Let succeeded be ! OrdinaryDefineOwnProperty(A, P, Desc).
  168. auto succeeded = Object::internal_define_own_property(property_name, property_descriptor);
  169. // i. If succeeded is false, return false.
  170. if (!succeeded)
  171. return false;
  172. // j. If index ≥ oldLen, then
  173. // i. Set oldLenDesc.[[Value]] to index + 1𝔽.
  174. // ii. Set succeeded to OrdinaryDefineOwnProperty(A, "length", oldLenDesc).
  175. // iii. Assert: succeeded is true.
  176. // k. Return true.
  177. return true;
  178. }
  179. // 4. Return OrdinaryDefineOwnProperty(A, P, Desc).
  180. return Object::internal_define_own_property(property_name, property_descriptor);
  181. }
  182. // NON-STANDARD: Used to reject deletes to ephemeral (non-configurable) length property
  183. bool Array::internal_delete(PropertyName const& property_name)
  184. {
  185. auto& vm = this->vm();
  186. if (property_name.is_string() && property_name.as_string() == vm.names.length.as_string())
  187. return false;
  188. return Object::internal_delete(property_name);
  189. }
  190. // NON-STANDARD: Used to inject the ephemeral length property's key
  191. MarkedValueList Array::internal_own_property_keys() const
  192. {
  193. auto& vm = this->vm();
  194. auto keys = Object::internal_own_property_keys();
  195. if (vm.exception())
  196. return MarkedValueList { vm.heap() };
  197. // FIXME: This is pretty expensive, find a better way to do this
  198. keys.insert(indexed_properties().real_size(), js_string(vm, vm.names.length.as_string()));
  199. return keys;
  200. }
  201. }