ArrayConstructor.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352
  1. /*
  2. * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
  3. * Copyright (c) 2020-2023, 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/ArrayConstructor.h>
  11. #include <LibJS/Runtime/Error.h>
  12. #include <LibJS/Runtime/GlobalObject.h>
  13. #include <LibJS/Runtime/IteratorOperations.h>
  14. #include <LibJS/Runtime/Shape.h>
  15. namespace JS {
  16. ArrayConstructor::ArrayConstructor(Realm& realm)
  17. : NativeFunction(realm.vm().names.Array.as_string(), *realm.intrinsics().function_prototype())
  18. {
  19. }
  20. ThrowCompletionOr<void> ArrayConstructor::initialize(Realm& realm)
  21. {
  22. auto& vm = this->vm();
  23. MUST_OR_THROW_OOM(NativeFunction::initialize(realm));
  24. // 23.1.2.4 Array.prototype, https://tc39.es/ecma262/#sec-array.prototype
  25. define_direct_property(vm.names.prototype, realm.intrinsics().array_prototype(), 0);
  26. u8 attr = Attribute::Writable | Attribute::Configurable;
  27. define_native_function(realm, vm.names.from, from, 1, attr);
  28. define_native_function(realm, vm.names.isArray, is_array, 1, attr);
  29. define_native_function(realm, vm.names.of, of, 0, attr);
  30. // 23.1.2.5 get Array [ @@species ], https://tc39.es/ecma262/#sec-get-array-@@species
  31. define_native_accessor(realm, *vm.well_known_symbol_species(), symbol_species_getter, {}, Attribute::Configurable);
  32. define_direct_property(vm.names.length, Value(1), Attribute::Configurable);
  33. return {};
  34. }
  35. // 23.1.1.1 Array ( ...values ), https://tc39.es/ecma262/#sec-array
  36. ThrowCompletionOr<Value> ArrayConstructor::call()
  37. {
  38. // 1. If NewTarget is undefined, let newTarget be the active function object; else let newTarget be NewTarget.
  39. return TRY(construct(*this));
  40. }
  41. // 23.1.1.1 Array ( ...values ), https://tc39.es/ecma262/#sec-array
  42. ThrowCompletionOr<NonnullGCPtr<Object>> ArrayConstructor::construct(FunctionObject& new_target)
  43. {
  44. auto& vm = this->vm();
  45. auto& realm = *vm.current_realm();
  46. // 2. Let proto be ? GetPrototypeFromConstructor(newTarget, "%Array.prototype%").
  47. auto* proto = TRY(get_prototype_from_constructor(vm, new_target, &Intrinsics::array_prototype));
  48. // 3. Let numberOfArgs be the number of elements in values.
  49. // 4. If numberOfArgs = 0, then
  50. if (vm.argument_count() == 0) {
  51. // a. Return ! ArrayCreate(0, proto).
  52. return MUST(Array::create(realm, 0, proto));
  53. }
  54. // 5. Else if numberOfArgs = 1, then
  55. if (vm.argument_count() == 1) {
  56. // a. Let len be values[0].
  57. auto length = vm.argument(0);
  58. // b. Let array be ! ArrayCreate(0, proto).
  59. auto array = MUST(Array::create(realm, 0, proto));
  60. size_t int_length;
  61. // c. If len is not a Number, then
  62. if (!length.is_number()) {
  63. // i. Perform ! CreateDataPropertyOrThrow(array, "0", len).
  64. MUST(array->create_data_property_or_throw(0, length));
  65. // ii. Let intLen be 1𝔽.
  66. int_length = 1;
  67. }
  68. // d. Else,
  69. else {
  70. // i. Let intLen be ! ToUint32(len).
  71. int_length = MUST(length.to_u32(vm));
  72. // ii. If SameValueZero(intLen, len) is false, throw a RangeError exception.
  73. if (int_length != length.as_double())
  74. return vm.throw_completion<RangeError>(ErrorType::InvalidLength, "array");
  75. }
  76. // e. Perform ! Set(array, "length", intLen, true).
  77. TRY(array->set(vm.names.length, Value(int_length), Object::ShouldThrowExceptions::Yes));
  78. // f. Return array.
  79. return array;
  80. }
  81. // 6. Else,
  82. // a. Assert: numberOfArgs ≥ 2.
  83. VERIFY(vm.argument_count() >= 2);
  84. // b. Let array be ? ArrayCreate(numberOfArgs, proto).
  85. auto array = TRY(Array::create(realm, vm.argument_count(), proto));
  86. // c. Let k be 0.
  87. // d. Repeat, while k < numberOfArgs,
  88. for (size_t k = 0; k < vm.argument_count(); ++k) {
  89. // i. Let Pk be ! ToString(𝔽(k)).
  90. auto property_key = PropertyKey { k };
  91. // ii. Let itemK be values[k].
  92. auto item_k = vm.argument(k);
  93. // iii. Perform ! CreateDataPropertyOrThrow(array, Pk, itemK).
  94. MUST(array->create_data_property_or_throw(property_key, item_k));
  95. // iv. Set k to k + 1.
  96. }
  97. // e. Assert: The mathematical value of array's "length" property is numberOfArgs.
  98. // f. Return array.
  99. return array;
  100. }
  101. // 23.1.2.1 Array.from ( items [ , mapfn [ , thisArg ] ] ), https://tc39.es/ecma262/#sec-array.from
  102. JS_DEFINE_NATIVE_FUNCTION(ArrayConstructor::from)
  103. {
  104. auto& realm = *vm.current_realm();
  105. auto items = vm.argument(0);
  106. auto mapfn_value = vm.argument(1);
  107. auto this_arg = vm.argument(2);
  108. // 1. Let C be the this value.
  109. auto constructor = vm.this_value();
  110. // 2. If mapfn is undefined, let mapping be false.
  111. GCPtr<FunctionObject> mapfn;
  112. // 3. Else,
  113. if (!mapfn_value.is_undefined()) {
  114. // a. If IsCallable(mapfn) is false, throw a TypeError exception.
  115. if (!mapfn_value.is_function())
  116. return vm.throw_completion<TypeError>(ErrorType::NotAFunction, TRY_OR_THROW_OOM(vm, mapfn_value.to_string_without_side_effects()));
  117. // b. Let mapping be true.
  118. mapfn = &mapfn_value.as_function();
  119. }
  120. // 4. Let usingIterator be ? GetMethod(items, @@iterator).
  121. auto using_iterator = TRY(items.get_method(vm, *vm.well_known_symbol_iterator()));
  122. // 5. If usingIterator is not undefined, then
  123. if (using_iterator) {
  124. GCPtr<Object> array;
  125. // a. If IsConstructor(C) is true, then
  126. if (constructor.is_constructor()) {
  127. // i. Let A be ? Construct(C).
  128. array = TRY(JS::construct(vm, constructor.as_function()));
  129. }
  130. // b. Else,
  131. else {
  132. // i. Let A be ! ArrayCreate(0).
  133. array = MUST(Array::create(realm, 0));
  134. }
  135. // c. Let iteratorRecord be ? GetIterator(items, sync, usingIterator).
  136. auto iterator = TRY(get_iterator(vm, items, IteratorHint::Sync, using_iterator));
  137. // d. Let k be 0.
  138. // e. Repeat,
  139. for (size_t k = 0;; ++k) {
  140. // i. If k ≥ 2^53 - 1, then
  141. if (k >= MAX_ARRAY_LIKE_INDEX) {
  142. // 1. Let error be ThrowCompletion(a newly created TypeError object).
  143. auto error = vm.throw_completion<TypeError>(ErrorType::ArrayMaxSize);
  144. // 2. Return ? IteratorClose(iteratorRecord, error).
  145. return *TRY(iterator_close(vm, iterator, move(error)));
  146. }
  147. // ii. Let Pk be ! ToString(𝔽(k)).
  148. auto property_key = PropertyKey { k };
  149. // iii. Let next be ? IteratorStep(iteratorRecord).
  150. auto* next = TRY(iterator_step(vm, iterator));
  151. // iv. If next is false, then
  152. if (!next) {
  153. // 1. Perform ? Set(A, "length", 𝔽(k), true).
  154. TRY(array->set(vm.names.length, Value(k), Object::ShouldThrowExceptions::Yes));
  155. // 2. Return A.
  156. return array;
  157. }
  158. // v. Let nextValue be ? IteratorValue(next).
  159. auto next_value = TRY(iterator_value(vm, *next));
  160. Value mapped_value;
  161. // vi. If mapping is true, then
  162. if (mapfn) {
  163. // 1. Let mappedValue be Completion(Call(mapfn, thisArg, « nextValue, 𝔽(k) »)).
  164. auto mapped_value_or_error = JS::call(vm, *mapfn, this_arg, next_value, Value(k));
  165. // 2. IfAbruptCloseIterator(mappedValue, iteratorRecord).
  166. if (mapped_value_or_error.is_error())
  167. return *TRY(iterator_close(vm, iterator, mapped_value_or_error.release_error()));
  168. mapped_value = mapped_value_or_error.release_value();
  169. }
  170. // vii. Else, let mappedValue be nextValue.
  171. else {
  172. mapped_value = next_value;
  173. }
  174. // viii. Let defineStatus be Completion(CreateDataPropertyOrThrow(A, Pk, mappedValue)).
  175. auto result_or_error = array->create_data_property_or_throw(property_key, mapped_value);
  176. // IfAbruptCloseIterator(defineStatus, iteratorRecord).
  177. if (result_or_error.is_error())
  178. return *TRY(iterator_close(vm, iterator, result_or_error.release_error()));
  179. // x. Set k to k + 1.
  180. }
  181. }
  182. // 6. NOTE: items is not an Iterable so assume it is an array-like object.
  183. // 7. Let arrayLike be ! ToObject(items).
  184. auto* array_like = MUST(items.to_object(vm));
  185. // 8. Let len be ? LengthOfArrayLike(arrayLike).
  186. auto length = TRY(length_of_array_like(vm, *array_like));
  187. GCPtr<Object> array;
  188. // 9. If IsConstructor(C) is true, then
  189. if (constructor.is_constructor()) {
  190. // a. Let A be ? Construct(C, « 𝔽(len) »).
  191. array = TRY(JS::construct(vm, constructor.as_function(), Value(length)));
  192. } else {
  193. // a. Let A be ? ArrayCreate(len).
  194. array = TRY(Array::create(realm, length));
  195. }
  196. // 11. Let k be 0.
  197. // 12. Repeat, while k < len,
  198. for (size_t k = 0; k < length; ++k) {
  199. // a. Let Pk be ! ToString(𝔽(k)).
  200. auto property_key = PropertyKey { k };
  201. // b. Let kValue be ? Get(arrayLike, Pk).
  202. auto k_value = TRY(array_like->get(property_key));
  203. Value mapped_value;
  204. // c. If mapping is true, then
  205. if (mapfn) {
  206. // i. Let mappedValue be ? Call(mapfn, thisArg, « kValue, 𝔽(k) »).
  207. mapped_value = TRY(JS::call(vm, *mapfn, this_arg, k_value, Value(k)));
  208. }
  209. // d. Else, let mappedValue be kValue.
  210. else {
  211. mapped_value = k_value;
  212. }
  213. // e. Perform ? CreateDataPropertyOrThrow(A, Pk, mappedValue).
  214. TRY(array->create_data_property_or_throw(property_key, mapped_value));
  215. // f. Set k to k + 1.
  216. }
  217. // 13. Perform ? Set(A, "length", 𝔽(len), true).
  218. TRY(array->set(vm.names.length, Value(length), Object::ShouldThrowExceptions::Yes));
  219. // 14. Return A.
  220. return array;
  221. }
  222. // 23.1.2.2 Array.isArray ( arg ), https://tc39.es/ecma262/#sec-array.isarray
  223. JS_DEFINE_NATIVE_FUNCTION(ArrayConstructor::is_array)
  224. {
  225. auto arg = vm.argument(0);
  226. // 1. Return ? IsArray(arg).
  227. return Value(TRY(arg.is_array(vm)));
  228. }
  229. // 23.1.2.3 Array.of ( ...items ), https://tc39.es/ecma262/#sec-array.of
  230. JS_DEFINE_NATIVE_FUNCTION(ArrayConstructor::of)
  231. {
  232. auto& realm = *vm.current_realm();
  233. // 1. Let len be the number of elements in items.
  234. auto len = vm.argument_count();
  235. // 2. Let lenNumber be 𝔽(len).
  236. auto len_number = Value(len);
  237. // 3. Let C be the this value.
  238. auto constructor = vm.this_value();
  239. GCPtr<Object> array;
  240. // 4. If IsConstructor(C) is true, then
  241. if (constructor.is_constructor()) {
  242. // a. Let A be ? Construct(C, « lenNumber »).
  243. array = TRY(JS::construct(vm, constructor.as_function(), Value(vm.argument_count())));
  244. } else {
  245. // a. Let A be ? ArrayCreate(len).
  246. array = TRY(Array::create(realm, len));
  247. }
  248. // 6. Let k be 0.
  249. // 7. Repeat, while k < len,
  250. for (size_t k = 0; k < len; ++k) {
  251. // a. Let kValue be items[k].
  252. auto k_value = vm.argument(k);
  253. // b. Let Pk be ! ToString(𝔽(k)).
  254. auto property_key = PropertyKey { k };
  255. // c. Perform ? CreateDataPropertyOrThrow(A, Pk, kValue).
  256. TRY(array->create_data_property_or_throw(property_key, k_value));
  257. // d. Set k to k + 1.
  258. }
  259. // 8. Perform ? Set(A, "length", lenNumber, true).
  260. TRY(array->set(vm.names.length, len_number, Object::ShouldThrowExceptions::Yes));
  261. // 9. Return A.
  262. return array;
  263. }
  264. // 23.1.2.5 get Array [ @@species ], https://tc39.es/ecma262/#sec-get-array-@@species
  265. JS_DEFINE_NATIVE_FUNCTION(ArrayConstructor::symbol_species_getter)
  266. {
  267. // 1. Return the this value.
  268. return vm.this_value();
  269. }
  270. }