Array.cpp 11 KB

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