ArrayPrototype.cpp 72 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992
  1. /*
  2. * Copyright (c) 2020, Andreas Kling <andreas@ladybird.org>
  3. * Copyright (c) 2020-2023, Linus Groh <linusg@serenityos.org>
  4. * Copyright (c) 2020, Marcin Gasperowicz <xnooga@gmail.com>
  5. * Copyright (c) 2021, David Tuin <davidot@serenityos.org>
  6. *
  7. * SPDX-License-Identifier: BSD-2-Clause
  8. */
  9. #include <AK/Function.h>
  10. #include <AK/HashTable.h>
  11. #include <AK/ScopeGuard.h>
  12. #include <AK/StringBuilder.h>
  13. #include <LibJS/Runtime/AbstractOperations.h>
  14. #include <LibJS/Runtime/Array.h>
  15. #include <LibJS/Runtime/ArrayConstructor.h>
  16. #include <LibJS/Runtime/ArrayIterator.h>
  17. #include <LibJS/Runtime/ArrayPrototype.h>
  18. #include <LibJS/Runtime/Error.h>
  19. #include <LibJS/Runtime/FunctionObject.h>
  20. #include <LibJS/Runtime/GlobalObject.h>
  21. #include <LibJS/Runtime/Map.h>
  22. #include <LibJS/Runtime/ObjectPrototype.h>
  23. #include <LibJS/Runtime/Realm.h>
  24. #include <LibJS/Runtime/Value.h>
  25. #include <LibJS/Runtime/ValueInlines.h>
  26. namespace JS {
  27. GC_DEFINE_ALLOCATOR(ArrayPrototype);
  28. static HashTable<GC::Ref<Object>> s_array_join_seen_objects;
  29. ArrayPrototype::ArrayPrototype(Realm& realm)
  30. : Array(realm.intrinsics().object_prototype())
  31. {
  32. }
  33. void ArrayPrototype::initialize(Realm& realm)
  34. {
  35. auto& vm = this->vm();
  36. Base::initialize(realm);
  37. u8 attr = Attribute::Writable | Attribute::Configurable;
  38. define_native_function(realm, vm.names.at, at, 1, attr);
  39. define_native_function(realm, vm.names.concat, concat, 1, attr);
  40. define_native_function(realm, vm.names.copyWithin, copy_within, 2, attr);
  41. define_native_function(realm, vm.names.entries, entries, 0, attr);
  42. define_native_function(realm, vm.names.every, every, 1, attr);
  43. define_native_function(realm, vm.names.fill, fill, 1, attr);
  44. define_native_function(realm, vm.names.filter, filter, 1, attr);
  45. define_native_function(realm, vm.names.find, find, 1, attr);
  46. define_native_function(realm, vm.names.findIndex, find_index, 1, attr);
  47. define_native_function(realm, vm.names.findLast, find_last, 1, attr);
  48. define_native_function(realm, vm.names.findLastIndex, find_last_index, 1, attr);
  49. define_native_function(realm, vm.names.flat, flat, 0, attr);
  50. define_native_function(realm, vm.names.flatMap, flat_map, 1, attr);
  51. define_native_function(realm, vm.names.forEach, for_each, 1, attr);
  52. define_native_function(realm, vm.names.includes, includes, 1, attr);
  53. define_native_function(realm, vm.names.indexOf, index_of, 1, attr);
  54. define_native_function(realm, vm.names.join, join, 1, attr);
  55. define_native_function(realm, vm.names.keys, keys, 0, attr);
  56. define_native_function(realm, vm.names.lastIndexOf, last_index_of, 1, attr);
  57. define_native_function(realm, vm.names.map, map, 1, attr);
  58. define_native_function(realm, vm.names.pop, pop, 0, attr);
  59. define_native_function(realm, vm.names.push, push, 1, attr);
  60. define_native_function(realm, vm.names.reduce, reduce, 1, attr);
  61. define_native_function(realm, vm.names.reduceRight, reduce_right, 1, attr);
  62. define_native_function(realm, vm.names.reverse, reverse, 0, attr);
  63. define_native_function(realm, vm.names.shift, shift, 0, attr);
  64. define_native_function(realm, vm.names.slice, slice, 2, attr);
  65. define_native_function(realm, vm.names.some, some, 1, attr);
  66. define_native_function(realm, vm.names.sort, sort, 1, attr);
  67. define_native_function(realm, vm.names.splice, splice, 2, attr);
  68. define_native_function(realm, vm.names.toLocaleString, to_locale_string, 0, attr);
  69. define_native_function(realm, vm.names.toReversed, to_reversed, 0, attr);
  70. define_native_function(realm, vm.names.toSorted, to_sorted, 1, attr);
  71. define_native_function(realm, vm.names.toSpliced, to_spliced, 2, attr);
  72. define_native_function(realm, vm.names.toString, to_string, 0, attr);
  73. define_native_function(realm, vm.names.unshift, unshift, 1, attr);
  74. define_native_function(realm, vm.names.values, values, 0, attr);
  75. define_native_function(realm, vm.names.with, with, 2, attr);
  76. // Use define_direct_property here instead of define_native_function so that
  77. // Object.is(Array.prototype[Symbol.iterator], Array.prototype.values)
  78. // evaluates to true
  79. // 23.1.3.40 Array.prototype [ @@iterator ] ( ), https://tc39.es/ecma262/#sec-array.prototype-@@iterator
  80. define_direct_property(vm.well_known_symbol_iterator(), get_without_side_effects(vm.names.values), attr);
  81. // 23.1.3.41 Array.prototype [ @@unscopables ], https://tc39.es/ecma262/#sec-array.prototype-@@unscopables
  82. auto unscopable_list = Object::create(realm, nullptr);
  83. MUST(unscopable_list->create_data_property_or_throw(vm.names.at, Value(true)));
  84. MUST(unscopable_list->create_data_property_or_throw(vm.names.copyWithin, Value(true)));
  85. MUST(unscopable_list->create_data_property_or_throw(vm.names.entries, Value(true)));
  86. MUST(unscopable_list->create_data_property_or_throw(vm.names.fill, Value(true)));
  87. MUST(unscopable_list->create_data_property_or_throw(vm.names.find, Value(true)));
  88. MUST(unscopable_list->create_data_property_or_throw(vm.names.findIndex, Value(true)));
  89. MUST(unscopable_list->create_data_property_or_throw(vm.names.findLast, Value(true)));
  90. MUST(unscopable_list->create_data_property_or_throw(vm.names.findLastIndex, Value(true)));
  91. MUST(unscopable_list->create_data_property_or_throw(vm.names.flat, Value(true)));
  92. MUST(unscopable_list->create_data_property_or_throw(vm.names.flatMap, Value(true)));
  93. MUST(unscopable_list->create_data_property_or_throw(vm.names.includes, Value(true)));
  94. MUST(unscopable_list->create_data_property_or_throw(vm.names.keys, Value(true)));
  95. MUST(unscopable_list->create_data_property_or_throw(vm.names.toReversed, Value(true)));
  96. MUST(unscopable_list->create_data_property_or_throw(vm.names.toSorted, Value(true)));
  97. MUST(unscopable_list->create_data_property_or_throw(vm.names.toSpliced, Value(true)));
  98. MUST(unscopable_list->create_data_property_or_throw(vm.names.values, Value(true)));
  99. define_direct_property(vm.well_known_symbol_unscopables(), unscopable_list, Attribute::Configurable);
  100. }
  101. // 10.4.2.3 ArraySpeciesCreate ( originalArray, length ), https://tc39.es/ecma262/#sec-arrayspeciescreate
  102. static ThrowCompletionOr<Object*> array_species_create(VM& vm, Object& original_array, size_t length)
  103. {
  104. auto& realm = *vm.current_realm();
  105. auto is_array = TRY(Value(&original_array).is_array(vm));
  106. if (!is_array)
  107. return TRY(Array::create(realm, length)).ptr();
  108. auto constructor = TRY(original_array.get(vm.names.constructor));
  109. if (constructor.is_constructor()) {
  110. auto& constructor_function = constructor.as_function();
  111. auto* this_realm = vm.current_realm();
  112. auto* constructor_realm = TRY(get_function_realm(vm, constructor_function));
  113. if (constructor_realm != this_realm) {
  114. if (&constructor_function == constructor_realm->intrinsics().array_constructor())
  115. constructor = js_undefined();
  116. }
  117. }
  118. if (constructor.is_object()) {
  119. constructor = TRY(constructor.as_object().get(vm.well_known_symbol_species()));
  120. if (constructor.is_null())
  121. constructor = js_undefined();
  122. }
  123. if (constructor.is_undefined())
  124. return TRY(Array::create(realm, length)).ptr();
  125. if (!constructor.is_constructor())
  126. return vm.throw_completion<TypeError>(ErrorType::NotAConstructor, constructor.to_string_without_side_effects());
  127. return TRY(construct(vm, constructor.as_function(), Value(length))).ptr();
  128. }
  129. // 23.1.3.1 Array.prototype.at ( index ), https://tc39.es/ecma262/#sec-array.prototype.at
  130. JS_DEFINE_NATIVE_FUNCTION(ArrayPrototype::at)
  131. {
  132. auto this_object = TRY(vm.this_value().to_object(vm));
  133. auto length = TRY(length_of_array_like(vm, this_object));
  134. auto relative_index = TRY(vm.argument(0).to_integer_or_infinity(vm));
  135. if (Value(relative_index).is_infinity())
  136. return js_undefined();
  137. Checked<size_t> index { 0 };
  138. if (relative_index >= 0) {
  139. index += relative_index;
  140. } else {
  141. index += length;
  142. index -= -relative_index;
  143. }
  144. if (index.has_overflow() || index.value() >= length)
  145. return js_undefined();
  146. return TRY(this_object->get(index.value()));
  147. }
  148. // 23.1.3.2 Array.prototype.concat ( ...items ), https://tc39.es/ecma262/#sec-array.prototype.concat
  149. JS_DEFINE_NATIVE_FUNCTION(ArrayPrototype::concat)
  150. {
  151. auto this_object = TRY(vm.this_value().to_object(vm));
  152. auto* new_array = TRY(array_species_create(vm, this_object, 0));
  153. size_t n = 0;
  154. // 23.1.3.2.1 IsConcatSpreadable ( O ), https://tc39.es/ecma262/#sec-isconcatspreadable
  155. auto is_concat_spreadable = [&vm](Value const& val) -> ThrowCompletionOr<bool> {
  156. if (!val.is_object())
  157. return false;
  158. auto& object = val.as_object();
  159. auto spreadable = TRY(object.get(vm.well_known_symbol_is_concat_spreadable()));
  160. if (!spreadable.is_undefined())
  161. return spreadable.to_boolean();
  162. return TRY(val.is_array(vm));
  163. };
  164. auto append_to_new_array = [&vm, &is_concat_spreadable, &new_array, &n](Value arg) -> ThrowCompletionOr<void> {
  165. auto spreadable = TRY(is_concat_spreadable(arg));
  166. if (spreadable) {
  167. VERIFY(arg.is_object());
  168. Object& obj = arg.as_object();
  169. size_t k = 0;
  170. auto length = TRY(length_of_array_like(vm, obj));
  171. if (n + length > MAX_ARRAY_LIKE_INDEX)
  172. return vm.throw_completion<TypeError>(ErrorType::ArrayMaxSize);
  173. while (k < length) {
  174. auto k_exists = TRY(obj.has_property(k));
  175. if (k_exists) {
  176. auto k_value = TRY(obj.get(k));
  177. TRY(new_array->create_data_property_or_throw(n, k_value));
  178. }
  179. ++n;
  180. ++k;
  181. }
  182. } else {
  183. if (n >= MAX_ARRAY_LIKE_INDEX)
  184. return vm.throw_completion<TypeError>(ErrorType::ArrayMaxSize);
  185. TRY(new_array->create_data_property_or_throw(n, arg));
  186. ++n;
  187. }
  188. return {};
  189. };
  190. TRY(append_to_new_array(this_object));
  191. for (size_t i = 0; i < vm.argument_count(); ++i)
  192. TRY(append_to_new_array(vm.argument(i)));
  193. TRY(new_array->set(vm.names.length, Value(n), Object::ShouldThrowExceptions::Yes));
  194. return Value(new_array);
  195. }
  196. // 23.1.3.4 Array.prototype.copyWithin ( target, start [ , end ] ), https://tc39.es/ecma262/#sec-array.prototype.copywithin
  197. JS_DEFINE_NATIVE_FUNCTION(ArrayPrototype::copy_within)
  198. {
  199. auto this_object = TRY(vm.this_value().to_object(vm));
  200. auto length = TRY(length_of_array_like(vm, this_object));
  201. auto relative_target = TRY(vm.argument(0).to_integer_or_infinity(vm));
  202. double to;
  203. if (relative_target < 0)
  204. to = max(length + relative_target, 0.0);
  205. else
  206. to = min(relative_target, (double)length);
  207. auto relative_start = TRY(vm.argument(1).to_integer_or_infinity(vm));
  208. double from;
  209. if (relative_start < 0)
  210. from = max(length + relative_start, 0.0);
  211. else
  212. from = min(relative_start, (double)length);
  213. auto relative_end = vm.argument(2).is_undefined() ? length : TRY(vm.argument(2).to_integer_or_infinity(vm));
  214. double final;
  215. if (relative_end < 0)
  216. final = max(length + relative_end, 0.0);
  217. else
  218. final = min(relative_end, (double)length);
  219. double count = min(final - from, length - to);
  220. i32 direction = 1;
  221. if (from < to && to < from + count) {
  222. direction = -1;
  223. from = from + count - 1;
  224. to = to + count - 1;
  225. }
  226. if (count < 0) {
  227. return this_object;
  228. }
  229. size_t from_i = from;
  230. size_t to_i = to;
  231. size_t count_i = count;
  232. while (count_i > 0) {
  233. auto from_present = TRY(this_object->has_property(from_i));
  234. if (from_present) {
  235. auto from_value = TRY(this_object->get(from_i));
  236. TRY(this_object->set(to_i, from_value, Object::ShouldThrowExceptions::Yes));
  237. } else {
  238. TRY(this_object->delete_property_or_throw(to_i));
  239. }
  240. from_i += direction;
  241. to_i += direction;
  242. --count_i;
  243. }
  244. return this_object;
  245. }
  246. // 23.1.3.5 Array.prototype.entries ( ), https://tc39.es/ecma262/#sec-array.prototype.entries
  247. JS_DEFINE_NATIVE_FUNCTION(ArrayPrototype::entries)
  248. {
  249. auto& realm = *vm.current_realm();
  250. auto this_object = TRY(vm.this_value().to_object(vm));
  251. return ArrayIterator::create(realm, this_object, Object::PropertyKind::KeyAndValue);
  252. }
  253. // 23.1.3.6 Array.prototype.every ( callbackfn [ , thisArg ] ), https://tc39.es/ecma262/#sec-array.prototype.every
  254. JS_DEFINE_NATIVE_FUNCTION(ArrayPrototype::every)
  255. {
  256. auto callback_function = vm.argument(0);
  257. auto this_arg = vm.argument(1);
  258. // 1. Let O be ? ToObject(this value).
  259. auto object = TRY(vm.this_value().to_object(vm));
  260. // 2. Let len be ? LengthOfArrayLike(O).
  261. auto length = TRY(length_of_array_like(vm, object));
  262. // 3. If IsCallable(callbackfn) is false, throw a TypeError exception.
  263. if (!callback_function.is_function())
  264. return vm.throw_completion<TypeError>(ErrorType::NotAFunction, callback_function.to_string_without_side_effects());
  265. // 4. Let k be 0.
  266. // 5. Repeat, while k < len,
  267. for (size_t k = 0; k < length; ++k) {
  268. // a. Let Pk be ! ToString(𝔽(k)).
  269. auto property_key = PropertyKey { k };
  270. // b. Let kPresent be ? HasProperty(O, Pk).
  271. auto k_present = TRY(object->has_property(property_key));
  272. // c. If kPresent is true, then
  273. if (k_present) {
  274. // i. Let kValue be ? Get(O, Pk).
  275. auto k_value = TRY(object->get(property_key));
  276. // ii. Let testResult be ToBoolean(? Call(callbackfn, thisArg, « kValue, 𝔽(k), O »)).
  277. auto test_result = TRY(call(vm, callback_function.as_function(), this_arg, k_value, Value(k), object)).to_boolean();
  278. // iii. If testResult is false, return false.
  279. if (!test_result)
  280. return Value(false);
  281. }
  282. // d. Set k to k + 1.
  283. }
  284. // 6. Return true.
  285. return Value(true);
  286. }
  287. // 23.1.3.7 Array.prototype.fill ( value [ , start [ , end ] ] ), https://tc39.es/ecma262/#sec-array.prototype.fill
  288. JS_DEFINE_NATIVE_FUNCTION(ArrayPrototype::fill)
  289. {
  290. auto this_object = TRY(vm.this_value().to_object(vm));
  291. auto length = TRY(length_of_array_like(vm, this_object));
  292. double relative_start = 0;
  293. double relative_end = length;
  294. if (vm.argument_count() >= 2) {
  295. relative_start = TRY(vm.argument(1).to_integer_or_infinity(vm));
  296. if (Value(relative_start).is_negative_infinity())
  297. relative_start = 0;
  298. }
  299. // If end is undefined, let relativeEnd be len; else let relativeEnd be ? ToIntegerOrInfinity(end).
  300. if (vm.argument_count() >= 3 && !vm.argument(2).is_undefined()) {
  301. relative_end = TRY(vm.argument(2).to_integer_or_infinity(vm));
  302. if (Value(relative_end).is_negative_infinity())
  303. relative_end = 0;
  304. }
  305. u64 from, to;
  306. if (relative_start < 0)
  307. from = max(length + relative_start, 0L);
  308. else
  309. from = min(relative_start, length);
  310. if (relative_end < 0)
  311. to = max(length + relative_end, 0L);
  312. else
  313. to = min(relative_end, length);
  314. for (u64 i = from; i < to; i++)
  315. TRY(this_object->set(i, vm.argument(0), Object::ShouldThrowExceptions::Yes));
  316. return this_object;
  317. }
  318. // 23.1.3.8 Array.prototype.filter ( callbackfn [ , thisArg ] ), https://tc39.es/ecma262/#sec-array.prototype.filter
  319. JS_DEFINE_NATIVE_FUNCTION(ArrayPrototype::filter)
  320. {
  321. auto callback_function = vm.argument(0);
  322. auto this_arg = vm.argument(1);
  323. // 1. Let O be ? ToObject(this value).
  324. auto object = TRY(vm.this_value().to_object(vm));
  325. // 2. Let len be ? LengthOfArrayLike(O).
  326. auto length = TRY(length_of_array_like(vm, object));
  327. // 3. If IsCallable(callbackfn) is false, throw a TypeError exception.
  328. if (!callback_function.is_function())
  329. return vm.throw_completion<TypeError>(ErrorType::NotAFunction, callback_function.to_string_without_side_effects());
  330. // 4. Let A be ? ArraySpeciesCreate(O, 0).
  331. auto* array = TRY(array_species_create(vm, object, 0));
  332. // 5. Let k be 0.
  333. size_t k = 0;
  334. // 6. Let to be 0.
  335. size_t to = 0;
  336. // 7. Repeat, while k < len,
  337. for (; k < length; ++k) {
  338. // a. Let Pk be ! ToString(𝔽(k)).
  339. auto property_key = PropertyKey { k };
  340. // b. Let kPresent be ? HasProperty(O, Pk).
  341. auto k_present = TRY(object->has_property(property_key));
  342. // c. If kPresent is true, then
  343. if (k_present) {
  344. // i. Let kValue be ? Get(O, Pk).
  345. auto k_value = TRY(object->get(k));
  346. // ii. Let selected be ToBoolean(? Call(callbackfn, thisArg, « kValue, 𝔽(k), O »)).
  347. auto selected = TRY(call(vm, callback_function.as_function(), this_arg, k_value, Value(k), object)).to_boolean();
  348. // iii. If selected is true, then
  349. if (selected) {
  350. // 1. Perform ? CreateDataPropertyOrThrow(A, ! ToString(𝔽(to)), kValue).
  351. TRY(array->create_data_property_or_throw(to, k_value));
  352. // 2. Set to to to + 1.
  353. ++to;
  354. }
  355. }
  356. // d. Set k to k + 1.
  357. }
  358. // 8. Return A.
  359. return array;
  360. }
  361. // 23.1.3.9 Array.prototype.find ( predicate [ , thisArg ] ), https://tc39.es/ecma262/#sec-array.prototype.find
  362. JS_DEFINE_NATIVE_FUNCTION(ArrayPrototype::find)
  363. {
  364. auto predicate = vm.argument(0);
  365. auto this_arg = vm.argument(1);
  366. // 1. Let O be ? ToObject(this value).
  367. auto object = TRY(vm.this_value().to_object(vm));
  368. // 2. Let len be ? LengthOfArrayLike(O).
  369. auto length = TRY(length_of_array_like(vm, object));
  370. // 3. If IsCallable(predicate) is false, throw a TypeError exception.
  371. if (!predicate.is_function())
  372. return vm.throw_completion<TypeError>(ErrorType::NotAFunction, predicate.to_string_without_side_effects());
  373. // 4. Let k be 0.
  374. // 5. Repeat, while k < len,
  375. for (size_t k = 0; k < length; ++k) {
  376. // a. Let Pk be ! ToString(𝔽(k)).
  377. auto property_key = PropertyKey { k };
  378. // b. Let kValue be ? Get(O, Pk).
  379. auto k_value = TRY(object->get(property_key));
  380. // c. Let testResult be ToBoolean(? Call(predicate, thisArg, « kValue, 𝔽(k), O »)).
  381. auto test_result = TRY(call(vm, predicate.as_function(), this_arg, k_value, Value(k), object)).to_boolean();
  382. // d. If testResult is true, return kValue.
  383. if (test_result)
  384. return k_value;
  385. // e. Set k to k + 1.
  386. }
  387. // 6. Return undefined.
  388. return js_undefined();
  389. }
  390. // 23.1.3.10 Array.prototype.findIndex ( predicate [ , thisArg ] ), https://tc39.es/ecma262/#sec-array.prototype.findindex
  391. JS_DEFINE_NATIVE_FUNCTION(ArrayPrototype::find_index)
  392. {
  393. auto predicate = vm.argument(0);
  394. auto this_arg = vm.argument(1);
  395. // 1. Let O be ? ToObject(this value).
  396. auto object = TRY(vm.this_value().to_object(vm));
  397. // 2. Let len be ? LengthOfArrayLike(O).
  398. auto length = TRY(length_of_array_like(vm, object));
  399. // 3. If IsCallable(predicate) is false, throw a TypeError exception.
  400. if (!predicate.is_function())
  401. return vm.throw_completion<TypeError>(ErrorType::NotAFunction, predicate.to_string_without_side_effects());
  402. // 4. Let k be 0.
  403. // 5. Repeat, while k < len,
  404. for (size_t k = 0; k < length; ++k) {
  405. // a. Let Pk be ! ToString(𝔽(k)).
  406. auto property_key = PropertyKey { k };
  407. // b. Let kValue be ? Get(O, Pk).
  408. auto k_value = TRY(object->get(property_key));
  409. // c. Let testResult be ToBoolean(? Call(predicate, thisArg, « kValue, 𝔽(k), O »)).
  410. auto test_result = TRY(call(vm, predicate.as_function(), this_arg, k_value, Value(k), object)).to_boolean();
  411. // d. If testResult is true, return 𝔽(k).
  412. if (test_result)
  413. return Value(k);
  414. // e. Set k to k + 1.
  415. }
  416. // 6. Return -1𝔽.
  417. return Value(-1);
  418. }
  419. // 23.1.3.11 Array.prototype.findLast ( predicate [ , thisArg ] ), https://tc39.es/ecma262/#sec-array.prototype.findlast
  420. JS_DEFINE_NATIVE_FUNCTION(ArrayPrototype::find_last)
  421. {
  422. auto predicate = vm.argument(0);
  423. auto this_arg = vm.argument(1);
  424. // 1. Let O be ? ToObject(this value).
  425. auto object = TRY(vm.this_value().to_object(vm));
  426. // 2. Let len be ? LengthOfArrayLike(O).
  427. auto length = TRY(length_of_array_like(vm, object));
  428. // 3. If IsCallable(predicate) is false, throw a TypeError exception.
  429. if (!predicate.is_function())
  430. return vm.throw_completion<TypeError>(ErrorType::NotAFunction, predicate.to_string_without_side_effects());
  431. // 4. Let k be len - 1.
  432. // 5. Repeat, while k ≥ 0,
  433. for (i64 k = static_cast<i64>(length) - 1; k >= 0; --k) {
  434. // a. Let Pk be ! ToString(𝔽(k)).
  435. auto property_key = PropertyKey { k };
  436. // b. Let kValue be ? Get(O, Pk).
  437. auto k_value = TRY(object->get(property_key));
  438. // c. Let testResult be ToBoolean(? Call(predicate, thisArg, « kValue, 𝔽(k), O »)).
  439. auto test_result = TRY(call(vm, predicate.as_function(), this_arg, k_value, Value((double)k), object)).to_boolean();
  440. // d. If testResult is true, return kValue.
  441. if (test_result)
  442. return k_value;
  443. // e. Set k to k - 1.
  444. }
  445. // 6. Return undefined.
  446. return js_undefined();
  447. }
  448. // 23.1.3.12 Array.prototype.findLastIndex ( predicate [ , thisArg ] ), https://tc39.es/ecma262/#sec-array.prototype.findlastindex
  449. JS_DEFINE_NATIVE_FUNCTION(ArrayPrototype::find_last_index)
  450. {
  451. auto predicate = vm.argument(0);
  452. auto this_arg = vm.argument(1);
  453. // 1. Let O be ? ToObject(this value).
  454. auto object = TRY(vm.this_value().to_object(vm));
  455. // 2. Let len be ? LengthOfArrayLike(O).
  456. auto length = TRY(length_of_array_like(vm, object));
  457. // 3. If IsCallable(predicate) is false, throw a TypeError exception.
  458. if (!predicate.is_function())
  459. return vm.throw_completion<TypeError>(ErrorType::NotAFunction, predicate.to_string_without_side_effects());
  460. // 4. Let k be len - 1.
  461. // 5. Repeat, while k ≥ 0,
  462. for (i64 k = static_cast<i64>(length) - 1; k >= 0; --k) {
  463. // a. Let Pk be ! ToString(𝔽(k)).
  464. auto property_key = PropertyKey { k };
  465. // b. Let kValue be ? Get(O, Pk).
  466. auto k_value = TRY(object->get(property_key));
  467. // c. Let testResult be ToBoolean(? Call(predicate, thisArg, « kValue, 𝔽(k), O »)).
  468. auto test_result = TRY(call(vm, predicate.as_function(), this_arg, k_value, Value((double)k), object)).to_boolean();
  469. // d. If testResult is true, return 𝔽(k).
  470. if (test_result)
  471. return Value((double)k);
  472. // e. Set k to k - 1.
  473. }
  474. // 6. Return -1𝔽.
  475. return Value(-1);
  476. }
  477. // 23.1.3.13.1 FlattenIntoArray ( target, source, sourceLen, start, depth [ , mapperFunction [ , thisArg ] ] ), https://tc39.es/ecma262/#sec-flattenintoarray
  478. static ThrowCompletionOr<size_t> flatten_into_array(VM& vm, Object& new_array, Object& array, size_t array_length, size_t target_index, double depth, FunctionObject* mapper_func = {}, Value this_arg = {})
  479. {
  480. VERIFY(!mapper_func || (!this_arg.is_empty() && depth == 1));
  481. for (size_t j = 0; j < array_length; ++j) {
  482. auto value_exists = TRY(array.has_property(j));
  483. if (!value_exists)
  484. continue;
  485. auto value = TRY(array.get(j));
  486. if (mapper_func)
  487. value = TRY(call(vm, *mapper_func, this_arg, value, Value(j), &array));
  488. if (depth > 0 && TRY(value.is_array(vm))) {
  489. if (vm.did_reach_stack_space_limit())
  490. return vm.throw_completion<InternalError>(ErrorType::CallStackSizeExceeded);
  491. auto length = TRY(length_of_array_like(vm, value.as_object()));
  492. target_index = TRY(flatten_into_array(vm, new_array, value.as_object(), length, target_index, depth - 1));
  493. continue;
  494. }
  495. if (target_index >= MAX_ARRAY_LIKE_INDEX)
  496. return vm.throw_completion<TypeError>(ErrorType::InvalidIndex);
  497. TRY(new_array.create_data_property_or_throw(target_index, value));
  498. ++target_index;
  499. }
  500. return target_index;
  501. }
  502. // 23.1.3.13 Array.prototype.flat ( [ depth ] ), https://tc39.es/ecma262/#sec-array.prototype.flat
  503. JS_DEFINE_NATIVE_FUNCTION(ArrayPrototype::flat)
  504. {
  505. auto this_object = TRY(vm.this_value().to_object(vm));
  506. auto length = TRY(length_of_array_like(vm, this_object));
  507. double depth = 1;
  508. if (!vm.argument(0).is_undefined()) {
  509. auto depth_num = TRY(vm.argument(0).to_integer_or_infinity(vm));
  510. depth = max(depth_num, 0.0);
  511. }
  512. auto* new_array = TRY(array_species_create(vm, this_object, 0));
  513. TRY(flatten_into_array(vm, *new_array, this_object, length, 0, depth));
  514. return new_array;
  515. }
  516. // 23.1.3.14 Array.prototype.flatMap ( mapperFunction [ , thisArg ] ), https://tc39.es/ecma262/#sec-array.prototype.flatmap
  517. JS_DEFINE_NATIVE_FUNCTION(ArrayPrototype::flat_map)
  518. {
  519. auto mapper_function = vm.argument(0);
  520. auto this_arg = vm.argument(1);
  521. // 1. Let O be ? ToObject(this value).
  522. auto object = TRY(vm.this_value().to_object(vm));
  523. // 2. Let sourceLen be ? LengthOfArrayLike(O).
  524. auto source_length = TRY(length_of_array_like(vm, object));
  525. // 3. If IsCallable(mapperFunction) is false, throw a TypeError exception.
  526. if (!mapper_function.is_function())
  527. return vm.throw_completion<TypeError>(ErrorType::NotAFunction, mapper_function.to_string_without_side_effects());
  528. // 4. Let A be ? ArraySpeciesCreate(O, 0).
  529. auto* array = TRY(array_species_create(vm, object, 0));
  530. // 5. Perform ? FlattenIntoArray(A, O, sourceLen, 0, 1, mapperFunction, thisArg).
  531. TRY(flatten_into_array(vm, *array, object, source_length, 0, 1, &mapper_function.as_function(), this_arg));
  532. // 6. Return A.
  533. return array;
  534. }
  535. // 23.1.3.15 Array.prototype.forEach ( callbackfn [ , thisArg ] ), https://tc39.es/ecma262/#sec-array.prototype.foreach
  536. JS_DEFINE_NATIVE_FUNCTION(ArrayPrototype::for_each)
  537. {
  538. auto callback_function = vm.argument(0);
  539. auto this_arg = vm.argument(1);
  540. // 1. Let O be ? ToObject(this value).
  541. auto object = TRY(vm.this_value().to_object(vm));
  542. // 2. Let len be ? LengthOfArrayLike(O).
  543. auto length = TRY(length_of_array_like(vm, object));
  544. // 3. If IsCallable(callbackfn) is false, throw a TypeError exception.
  545. if (!callback_function.is_function())
  546. return vm.throw_completion<TypeError>(ErrorType::NotAFunction, callback_function.to_string_without_side_effects());
  547. // 4. Let k be 0.
  548. // 5. Repeat, while k < len,
  549. for (size_t k = 0; k < length; ++k) {
  550. // a. Let Pk be ! ToString(𝔽(k)).
  551. auto property_key = PropertyKey { k };
  552. // b. Let kPresent be ? HasProperty(O, Pk).
  553. auto k_present = TRY(object->has_property(property_key));
  554. // c. If kPresent is true, then
  555. if (k_present) {
  556. // i. Let kValue be ? Get(O, Pk).
  557. auto k_value = TRY(object->get(property_key));
  558. // ii. Perform ? Call(callbackfn, thisArg, « kValue, 𝔽(k), O »).
  559. TRY(call(vm, callback_function.as_function(), this_arg, k_value, Value(k), object));
  560. }
  561. // d. Set k to k + 1.
  562. }
  563. // 6. Return undefined.
  564. return js_undefined();
  565. }
  566. // 23.1.3.16 Array.prototype.includes ( searchElement [ , fromIndex ] ), https://tc39.es/ecma262/#sec-array.prototype.includes
  567. JS_DEFINE_NATIVE_FUNCTION(ArrayPrototype::includes)
  568. {
  569. auto this_object = TRY(vm.this_value().to_object(vm));
  570. auto length = TRY(length_of_array_like(vm, this_object));
  571. if (length == 0)
  572. return Value(false);
  573. u64 from_index = 0;
  574. if (vm.argument_count() >= 2) {
  575. auto from_argument = TRY(vm.argument(1).to_integer_or_infinity(vm));
  576. if (Value(from_argument).is_positive_infinity() || from_argument >= length)
  577. return Value(false);
  578. if (Value(from_argument).is_negative_infinity())
  579. from_argument = 0;
  580. if (from_argument < 0)
  581. from_index = max(length + from_argument, 0);
  582. else
  583. from_index = from_argument;
  584. }
  585. auto value_to_find = vm.argument(0);
  586. for (u64 i = from_index; i < length; ++i) {
  587. auto element = TRY(this_object->get(i));
  588. if (same_value_zero(element, value_to_find))
  589. return Value(true);
  590. }
  591. return Value(false);
  592. }
  593. // 23.1.3.17 Array.prototype.indexOf ( searchElement [ , fromIndex ] ), https://tc39.es/ecma262/#sec-array.prototype.indexof
  594. JS_DEFINE_NATIVE_FUNCTION(ArrayPrototype::index_of)
  595. {
  596. auto search_element = vm.argument(0);
  597. auto from_index = vm.argument(1);
  598. // 1. Let O be ? ToObject(this value).
  599. auto object = TRY(vm.this_value().to_object(vm));
  600. // 2. Let len be ? LengthOfArrayLike(O).
  601. auto length = TRY(length_of_array_like(vm, object));
  602. // 3. If len is 0, return -1𝔽.
  603. if (length == 0)
  604. return Value(-1);
  605. // 4. Let n be ? ToIntegerOrInfinity(fromIndex).
  606. auto n = TRY(from_index.to_integer_or_infinity(vm));
  607. // 5. Assert: If fromIndex is undefined, then n is 0.
  608. if (from_index.is_undefined())
  609. VERIFY(n == 0);
  610. // 6. If n is +∞, return -1𝔽.
  611. if (Value(n).is_positive_infinity())
  612. return Value(-1);
  613. // 7. Else if n is -∞, set n to 0.
  614. if (Value(n).is_negative_infinity())
  615. n = 0;
  616. size_t k;
  617. // 8. If n ≥ 0, then
  618. if (n >= 0) {
  619. // a. Let k be n.
  620. k = (size_t)n;
  621. }
  622. // 9. Else,
  623. else {
  624. // a. Let k be len + n.
  625. // b. If k < 0, set k to 0.
  626. k = max(length + n, 0);
  627. }
  628. // 10. Repeat, while k < len,
  629. for (; k < length; ++k) {
  630. auto property_key = PropertyKey { k };
  631. // a. Let kPresent be ? HasProperty(O, ! ToString(𝔽(k))).
  632. auto k_present = TRY(object->has_property(property_key));
  633. // b. If kPresent is true, then
  634. if (k_present) {
  635. // i. Let elementK be ? Get(O, ! ToString(𝔽(k))).
  636. auto element_k = TRY(object->get(property_key));
  637. // ii. Let same be IsStrictlyEqual(searchElement, elementK).
  638. auto same = is_strictly_equal(search_element, element_k);
  639. // iii. If same is true, return 𝔽(k).
  640. if (same)
  641. return Value(k);
  642. }
  643. // c. Set k to k + 1.
  644. }
  645. // 11. Return -1𝔽.
  646. return Value(-1);
  647. }
  648. // 23.1.3.18 Array.prototype.join ( separator ), https://tc39.es/ecma262/#sec-array.prototype.join
  649. JS_DEFINE_NATIVE_FUNCTION(ArrayPrototype::join)
  650. {
  651. auto this_object = TRY(vm.this_value().to_object(vm));
  652. // This is not part of the spec, but all major engines do some kind of circular reference checks.
  653. // FWIW: engine262, a "100% spec compliant" ECMA-262 impl, aborts with "too much recursion".
  654. // Same applies to Array.prototype.toLocaleString().
  655. if (s_array_join_seen_objects.contains(this_object))
  656. return PrimitiveString::create(vm, String {});
  657. s_array_join_seen_objects.set(this_object);
  658. ArmedScopeGuard unsee_object_guard = [&] {
  659. s_array_join_seen_objects.remove(this_object);
  660. };
  661. auto length = TRY(length_of_array_like(vm, this_object));
  662. ByteString separator = ",";
  663. if (!vm.argument(0).is_undefined())
  664. separator = TRY(vm.argument(0).to_byte_string(vm));
  665. StringBuilder builder;
  666. for (size_t i = 0; i < length; ++i) {
  667. if (i > 0)
  668. builder.append(separator);
  669. auto value = TRY(this_object->get(i));
  670. if (value.is_nullish())
  671. continue;
  672. auto string = TRY(value.to_byte_string(vm));
  673. builder.append(string);
  674. }
  675. return PrimitiveString::create(vm, builder.to_byte_string());
  676. }
  677. // 23.1.3.19 Array.prototype.keys ( ), https://tc39.es/ecma262/#sec-array.prototype.keys
  678. JS_DEFINE_NATIVE_FUNCTION(ArrayPrototype::keys)
  679. {
  680. auto& realm = *vm.current_realm();
  681. auto this_object = TRY(vm.this_value().to_object(vm));
  682. return ArrayIterator::create(realm, this_object, Object::PropertyKind::Key);
  683. }
  684. // 23.1.3.20 Array.prototype.lastIndexOf ( searchElement [ , fromIndex ] ), https://tc39.es/ecma262/#sec-array.prototype.lastindexof
  685. JS_DEFINE_NATIVE_FUNCTION(ArrayPrototype::last_index_of)
  686. {
  687. auto search_element = vm.argument(0);
  688. auto from_index = vm.argument(1);
  689. // 1. Let O be ? ToObject(this value).
  690. auto object = TRY(vm.this_value().to_object(vm));
  691. // 2. Let len be ? LengthOfArrayLike(O).
  692. auto length = TRY(length_of_array_like(vm, object));
  693. // 3. If len is 0, return -1𝔽.
  694. if (length == 0)
  695. return Value(-1);
  696. double n;
  697. // 4. If fromIndex is present, let n be ? ToIntegerOrInfinity(fromIndex); else let n be len - 1.
  698. if (vm.argument_count() >= 2)
  699. n = TRY(from_index.to_integer_or_infinity(vm));
  700. else
  701. n = (double)length - 1;
  702. // 5. If n is -∞, return -1𝔽.
  703. if (Value(n).is_negative_infinity())
  704. return Value(-1);
  705. ssize_t k;
  706. // 6. If n ≥ 0, then
  707. if (n >= 0) {
  708. // a. Let k be min(n, len - 1).
  709. k = min(n, (double)length - 1);
  710. }
  711. // 7. Else,
  712. else {
  713. // a. Let k be len + n.
  714. k = (double)length + n;
  715. }
  716. // 8. Repeat, while k ≥ 0,
  717. for (; k >= 0; --k) {
  718. auto property_key = PropertyKey { k };
  719. // a. Let kPresent be ? HasProperty(O, ! ToString(𝔽(k))).
  720. auto k_present = TRY(object->has_property(property_key));
  721. // b. If kPresent is true, then
  722. if (k_present) {
  723. // i. Let elementK be ? Get(O, ! ToString(𝔽(k))).
  724. auto element_k = TRY(object->get(property_key));
  725. // ii. Let same be IsStrictlyEqual(searchElement, elementK).
  726. auto same = is_strictly_equal(search_element, element_k);
  727. // iii. If same is true, return 𝔽(k).
  728. if (same)
  729. return Value((size_t)k);
  730. }
  731. // c. Set k to k - 1.
  732. }
  733. // 9. Return -1𝔽.
  734. return Value(-1);
  735. }
  736. // 23.1.3.21 Array.prototype.map ( callbackfn [ , thisArg ] ), https://tc39.es/ecma262/#sec-array.prototype.map
  737. JS_DEFINE_NATIVE_FUNCTION(ArrayPrototype::map)
  738. {
  739. auto callback_function = vm.argument(0);
  740. auto this_arg = vm.argument(1);
  741. // 1. Let O be ? ToObject(this value).
  742. auto object = TRY(vm.this_value().to_object(vm));
  743. // 2. Let len be ? LengthOfArrayLike(O).
  744. auto length = TRY(length_of_array_like(vm, object));
  745. // 3. If IsCallable(callbackfn) is false, throw a TypeError exception.
  746. if (!callback_function.is_function())
  747. return vm.throw_completion<TypeError>(ErrorType::NotAFunction, callback_function.to_string_without_side_effects());
  748. // 4. Let A be ? ArraySpeciesCreate(O, len).
  749. auto* array = TRY(array_species_create(vm, object, length));
  750. // 5. Let k be 0.
  751. // 6. Repeat, while k < len,
  752. for (size_t k = 0; k < length; ++k) {
  753. // a. Let Pk be ! ToString(𝔽(k)).
  754. auto property_key = PropertyKey { k };
  755. // b. Let kPresent be ? HasProperty(O, Pk).
  756. auto k_present = TRY(object->has_property(property_key));
  757. // c. If kPresent is true, then
  758. if (k_present) {
  759. // i. Let kValue be ? Get(O, Pk).
  760. auto k_value = TRY(object->get(property_key));
  761. // ii. Let mappedValue be ? Call(callbackfn, thisArg, « kValue, 𝔽(k), O »).
  762. auto mapped_value = TRY(call(vm, callback_function.as_function(), this_arg, k_value, Value(k), object));
  763. // iii. Perform ? CreateDataPropertyOrThrow(A, Pk, mappedValue).
  764. TRY(array->create_data_property_or_throw(property_key, mapped_value));
  765. }
  766. // d. Set k to k + 1.
  767. }
  768. // 7. Return A.
  769. return array;
  770. }
  771. // 23.1.3.22 Array.prototype.pop ( ), https://tc39.es/ecma262/#sec-array.prototype.pop
  772. JS_DEFINE_NATIVE_FUNCTION(ArrayPrototype::pop)
  773. {
  774. auto this_object = TRY(vm.this_value().to_object(vm));
  775. auto length = TRY(length_of_array_like(vm, this_object));
  776. if (length == 0) {
  777. TRY(this_object->set(vm.names.length, Value(0), Object::ShouldThrowExceptions::Yes));
  778. return js_undefined();
  779. }
  780. auto index = length - 1;
  781. auto element = TRY(this_object->get(index));
  782. TRY(this_object->delete_property_or_throw(index));
  783. TRY(this_object->set(vm.names.length, Value(index), Object::ShouldThrowExceptions::Yes));
  784. return element;
  785. }
  786. // 23.1.3.23 Array.prototype.push ( ...items ), https://tc39.es/ecma262/#sec-array.prototype.push
  787. JS_DEFINE_NATIVE_FUNCTION(ArrayPrototype::push)
  788. {
  789. auto this_object = TRY(vm.this_value().to_object(vm));
  790. auto length = TRY(length_of_array_like(vm, this_object));
  791. auto argument_count = vm.argument_count();
  792. auto new_length = length + argument_count;
  793. if (new_length > MAX_ARRAY_LIKE_INDEX)
  794. return vm.throw_completion<TypeError>(ErrorType::ArrayMaxSize);
  795. for (size_t i = 0; i < argument_count; ++i)
  796. TRY(this_object->set(length + i, vm.argument(i), Object::ShouldThrowExceptions::Yes));
  797. auto new_length_value = Value(new_length);
  798. TRY(this_object->set(vm.names.length, new_length_value, Object::ShouldThrowExceptions::Yes));
  799. return new_length_value;
  800. }
  801. // 23.1.3.24 Array.prototype.reduce ( callbackfn [ , initialValue ] ), https://tc39.es/ecma262/#sec-array.prototype.reduce
  802. JS_DEFINE_NATIVE_FUNCTION(ArrayPrototype::reduce)
  803. {
  804. auto callback_function = vm.argument(0);
  805. auto initial_value = vm.argument(1);
  806. // 1. Let O be ? ToObject(this value).
  807. auto object = TRY(vm.this_value().to_object(vm));
  808. // 2. Let len be ? LengthOfArrayLike(O).
  809. auto length = TRY(length_of_array_like(vm, object));
  810. // 3. If IsCallable(callbackfn) is false, throw a TypeError exception.
  811. if (!callback_function.is_function())
  812. return vm.throw_completion<TypeError>(ErrorType::NotAFunction, callback_function.to_string_without_side_effects());
  813. // 4. If len = 0 and initialValue is not present, throw a TypeError exception.
  814. if (length == 0 && vm.argument_count() <= 1)
  815. return vm.throw_completion<TypeError>(ErrorType::ReduceNoInitial);
  816. // 5. Let k be 0.
  817. size_t k = 0;
  818. // 6. Let accumulator be undefined.
  819. auto accumulator = js_undefined();
  820. // 7. If initialValue is present, then
  821. if (vm.argument_count() > 1) {
  822. // a. Set accumulator to initialValue.
  823. accumulator = initial_value;
  824. }
  825. // 8. Else,
  826. else {
  827. // a. Let kPresent be false.
  828. bool k_present = false;
  829. // b. Repeat, while kPresent is false and k < len,
  830. for (; !k_present && k < length; ++k) {
  831. // i. Let Pk be ! ToString(𝔽(k)).
  832. auto property_key = PropertyKey { k };
  833. // ii. Set kPresent to ? HasProperty(O, Pk).
  834. k_present = TRY(object->has_property(property_key));
  835. // iii. If kPresent is true, then
  836. if (k_present) {
  837. // 1. Set accumulator to ? Get(O, Pk).
  838. accumulator = TRY(object->get(property_key));
  839. }
  840. // iv. Set k to k + 1.
  841. }
  842. // c. If kPresent is false, throw a TypeError exception.
  843. if (!k_present)
  844. return vm.throw_completion<TypeError>(ErrorType::ReduceNoInitial);
  845. }
  846. // 9. Repeat, while k < len,
  847. for (; k < length; ++k) {
  848. // a. Let Pk be ! ToString(𝔽(k)).
  849. auto property_key = PropertyKey { k };
  850. // b. Let kPresent be ? HasProperty(O, Pk).
  851. auto k_present = TRY(object->has_property(property_key));
  852. // c. If kPresent is true, then
  853. if (k_present) {
  854. // i. Let kValue be ? Get(O, Pk).
  855. auto k_value = TRY(object->get(property_key));
  856. // ii. Set accumulator to ? Call(callbackfn, undefined, « accumulator, kValue, 𝔽(k), O »).
  857. accumulator = TRY(call(vm, callback_function.as_function(), js_undefined(), accumulator, k_value, Value(k), object));
  858. }
  859. // d. Set k to k + 1.
  860. }
  861. // 10. Return accumulator.
  862. return accumulator;
  863. }
  864. // 23.1.3.25 Array.prototype.reduceRight ( callbackfn [ , initialValue ] ), https://tc39.es/ecma262/#sec-array.prototype.reduceright
  865. JS_DEFINE_NATIVE_FUNCTION(ArrayPrototype::reduce_right)
  866. {
  867. auto callback_function = vm.argument(0);
  868. auto initial_value = vm.argument(1);
  869. // 1. Let O be ? ToObject(this value).
  870. auto object = TRY(vm.this_value().to_object(vm));
  871. // 2. Let len be ? LengthOfArrayLike(O).
  872. auto length = TRY(length_of_array_like(vm, object));
  873. // 3. If IsCallable(callbackfn) is false, throw a TypeError exception.
  874. if (!callback_function.is_function())
  875. return vm.throw_completion<TypeError>(ErrorType::NotAFunction, callback_function.to_string_without_side_effects());
  876. // 4. If len = 0 and initialValue is not present, throw a TypeError exception.
  877. if (length == 0 && vm.argument_count() <= 1)
  878. return vm.throw_completion<TypeError>(ErrorType::ReduceNoInitial);
  879. // 5. Let k be len - 1.
  880. ssize_t k = length - 1;
  881. // 6. Let accumulator be undefined.
  882. auto accumulator = js_undefined();
  883. // 7. If initialValue is present, then
  884. if (vm.argument_count() > 1) {
  885. // a. Set accumulator to initialValue.
  886. accumulator = initial_value;
  887. }
  888. // 8. Else,
  889. else {
  890. // a. Let kPresent be false.
  891. bool k_present = false;
  892. // b. Repeat, while kPresent is false and k ≥ 0,
  893. for (; !k_present && k >= 0; --k) {
  894. // i. Let Pk be ! ToString(𝔽(k)).
  895. auto property_key = PropertyKey { k };
  896. // ii. Set kPresent to ? HasProperty(O, Pk).
  897. k_present = TRY(object->has_property(property_key));
  898. // iii. If kPresent is true, then
  899. if (k_present) {
  900. // 1. Set accumulator to ? Get(O, Pk).
  901. accumulator = TRY(object->get(property_key));
  902. }
  903. // iv. Set k to k - 1.
  904. }
  905. // c. If kPresent is false, throw a TypeError exception.
  906. if (!k_present)
  907. return vm.throw_completion<TypeError>(ErrorType::ReduceNoInitial);
  908. }
  909. // 9. Repeat, while k ≥ 0,
  910. for (; k >= 0; --k) {
  911. // a. Let Pk be ! ToString(𝔽(k)).
  912. auto property_key = PropertyKey { k };
  913. // b. Let kPresent be ? HasProperty(O, Pk).
  914. auto k_present = TRY(object->has_property(property_key));
  915. // c. If kPresent is true, then
  916. if (k_present) {
  917. // i. Let kValue be ? Get(O, Pk).
  918. auto k_value = TRY(object->get(property_key));
  919. // ii. Set accumulator to ? Call(callbackfn, undefined, « accumulator, kValue, 𝔽(k), O »).
  920. accumulator = TRY(call(vm, callback_function.as_function(), js_undefined(), accumulator, k_value, Value((size_t)k), object));
  921. }
  922. // d. Set k to k - 1.
  923. }
  924. // 10. Return accumulator.
  925. return accumulator;
  926. }
  927. // 23.1.3.26 Array.prototype.reverse ( ), https://tc39.es/ecma262/#sec-array.prototype.reverse
  928. JS_DEFINE_NATIVE_FUNCTION(ArrayPrototype::reverse)
  929. {
  930. auto this_object = TRY(vm.this_value().to_object(vm));
  931. auto length = TRY(length_of_array_like(vm, this_object));
  932. auto middle = length / 2;
  933. for (size_t lower = 0; lower < middle; ++lower) {
  934. auto upper = length - lower - 1;
  935. auto lower_exists = TRY(this_object->has_property(lower));
  936. Value lower_value;
  937. if (lower_exists)
  938. lower_value = TRY(this_object->get(lower));
  939. auto upper_exists = TRY(this_object->has_property(upper));
  940. Value upper_value;
  941. if (upper_exists)
  942. upper_value = TRY(this_object->get(upper));
  943. if (lower_exists && upper_exists) {
  944. TRY(this_object->set(lower, upper_value, Object::ShouldThrowExceptions::Yes));
  945. TRY(this_object->set(upper, lower_value, Object::ShouldThrowExceptions::Yes));
  946. } else if (!lower_exists && upper_exists) {
  947. TRY(this_object->set(lower, upper_value, Object::ShouldThrowExceptions::Yes));
  948. TRY(this_object->delete_property_or_throw(upper));
  949. } else if (lower_exists && !upper_exists) {
  950. TRY(this_object->delete_property_or_throw(lower));
  951. TRY(this_object->set(upper, lower_value, Object::ShouldThrowExceptions::Yes));
  952. }
  953. }
  954. return this_object;
  955. }
  956. // 23.1.3.27 Array.prototype.shift ( ), https://tc39.es/ecma262/#sec-array.prototype.shift
  957. JS_DEFINE_NATIVE_FUNCTION(ArrayPrototype::shift)
  958. {
  959. auto this_object = TRY(vm.this_value().to_object(vm));
  960. auto length = TRY(length_of_array_like(vm, this_object));
  961. if (length == 0) {
  962. TRY(this_object->set(vm.names.length, Value(0), Object::ShouldThrowExceptions::Yes));
  963. return js_undefined();
  964. }
  965. auto first = TRY(this_object->get(0));
  966. for (size_t k = 1; k < length; ++k) {
  967. size_t from = k;
  968. size_t to = k - 1;
  969. bool from_present = TRY(this_object->has_property(from));
  970. if (from_present) {
  971. auto from_value = TRY(this_object->get(from));
  972. TRY(this_object->set(to, from_value, Object::ShouldThrowExceptions::Yes));
  973. } else {
  974. TRY(this_object->delete_property_or_throw(to));
  975. }
  976. }
  977. TRY(this_object->delete_property_or_throw(length - 1));
  978. TRY(this_object->set(vm.names.length, Value(length - 1), Object::ShouldThrowExceptions::Yes));
  979. return first;
  980. }
  981. // 23.1.3.28 Array.prototype.slice ( start, end ), https://tc39.es/ecma262/#sec-array.prototype.slice
  982. JS_DEFINE_NATIVE_FUNCTION(ArrayPrototype::slice)
  983. {
  984. auto this_object = TRY(vm.this_value().to_object(vm));
  985. auto initial_length = TRY(length_of_array_like(vm, this_object));
  986. auto relative_start = TRY(vm.argument(0).to_integer_or_infinity(vm));
  987. double actual_start;
  988. if (Value(relative_start).is_negative_infinity())
  989. actual_start = 0.0;
  990. else if (relative_start < 0.0)
  991. actual_start = max((double)initial_length + relative_start, 0.0);
  992. else
  993. actual_start = min(relative_start, (double)initial_length);
  994. double relative_end;
  995. if (vm.argument(1).is_undefined() || vm.argument(1).is_empty())
  996. relative_end = (double)initial_length;
  997. else
  998. relative_end = TRY(vm.argument(1).to_integer_or_infinity(vm));
  999. double final;
  1000. if (Value(relative_end).is_negative_infinity())
  1001. final = 0.0;
  1002. else if (relative_end < 0.0)
  1003. final = max((double)initial_length + relative_end, 0.0);
  1004. else
  1005. final = min(relative_end, (double)initial_length);
  1006. auto count = max(final - actual_start, 0.0);
  1007. auto* new_array = TRY(array_species_create(vm, this_object, count));
  1008. size_t index = 0;
  1009. size_t k = actual_start;
  1010. while (k < final) {
  1011. bool present = TRY(this_object->has_property(k));
  1012. if (present) {
  1013. auto value = TRY(this_object->get(k));
  1014. TRY(new_array->create_data_property_or_throw(index, value));
  1015. }
  1016. ++k;
  1017. ++index;
  1018. }
  1019. TRY(new_array->set(vm.names.length, Value(index), Object::ShouldThrowExceptions::Yes));
  1020. return new_array;
  1021. }
  1022. // 23.1.3.29 Array.prototype.some ( callbackfn [ , thisArg ] ), https://tc39.es/ecma262/#sec-array.prototype.some
  1023. JS_DEFINE_NATIVE_FUNCTION(ArrayPrototype::some)
  1024. {
  1025. auto callback_function = vm.argument(0);
  1026. auto this_arg = vm.argument(1);
  1027. // 1. Let O be ? ToObject(this value).
  1028. auto object = TRY(vm.this_value().to_object(vm));
  1029. // 2. Let len be ? LengthOfArrayLike(O).
  1030. auto length = TRY(length_of_array_like(vm, object));
  1031. // 3. If IsCallable(callbackfn) is false, throw a TypeError exception.
  1032. if (!callback_function.is_function())
  1033. return vm.throw_completion<TypeError>(ErrorType::NotAFunction, callback_function.to_string_without_side_effects());
  1034. // 4. Let k be 0.
  1035. // 5. Repeat, while k < len,
  1036. for (size_t k = 0; k < length; ++k) {
  1037. // a. Let Pk be ! ToString(𝔽(k)).
  1038. auto property_key = PropertyKey { k };
  1039. // b. Let kPresent be ? HasProperty(O, Pk).
  1040. auto k_present = TRY(object->has_property(property_key));
  1041. // c. If kPresent is true, then
  1042. if (k_present) {
  1043. // i. Let kValue be ? Get(O, Pk).
  1044. auto k_value = TRY(object->get(property_key));
  1045. // ii. Let testResult be ToBoolean(? Call(callbackfn, thisArg, « kValue, 𝔽(k), O »)).
  1046. auto test_result = TRY(call(vm, callback_function.as_function(), this_arg, k_value, Value(k), object)).to_boolean();
  1047. // iii. If testResult is true, return true.
  1048. if (test_result)
  1049. return Value(true);
  1050. }
  1051. // d. Set k to k + 1.
  1052. }
  1053. // 6. Return false.
  1054. return Value(false);
  1055. }
  1056. ThrowCompletionOr<void> array_merge_sort(VM& vm, Function<ThrowCompletionOr<double>(Value, Value)> const& compare_func, GC::RootVector<Value>& arr_to_sort)
  1057. {
  1058. // FIXME: it would probably be better to switch to insertion sort for small arrays for
  1059. // better performance
  1060. if (arr_to_sort.size() <= 1)
  1061. return {};
  1062. GC::RootVector<Value> left(vm.heap());
  1063. GC::RootVector<Value> right(vm.heap());
  1064. left.ensure_capacity(arr_to_sort.size() / 2);
  1065. right.ensure_capacity(arr_to_sort.size() / 2 + (arr_to_sort.size() & 1));
  1066. for (size_t i = 0; i < arr_to_sort.size(); ++i) {
  1067. if (i < arr_to_sort.size() / 2) {
  1068. left.append(arr_to_sort[i]);
  1069. } else {
  1070. right.append(arr_to_sort[i]);
  1071. }
  1072. }
  1073. TRY(array_merge_sort(vm, compare_func, left));
  1074. TRY(array_merge_sort(vm, compare_func, right));
  1075. arr_to_sort.clear();
  1076. size_t left_index = 0, right_index = 0;
  1077. while (left_index < left.size() && right_index < right.size()) {
  1078. auto x = left[left_index];
  1079. auto y = right[right_index];
  1080. double comparison_result = TRY(compare_func(x, y));
  1081. if (comparison_result <= 0) {
  1082. arr_to_sort.append(x);
  1083. left_index++;
  1084. } else {
  1085. arr_to_sort.append(y);
  1086. right_index++;
  1087. }
  1088. }
  1089. while (left_index < left.size()) {
  1090. arr_to_sort.append(left[left_index]);
  1091. left_index++;
  1092. }
  1093. while (right_index < right.size()) {
  1094. arr_to_sort.append(right[right_index]);
  1095. right_index++;
  1096. }
  1097. return {};
  1098. }
  1099. // 23.1.3.30 Array.prototype.sort ( comparefn ), https://tc39.es/ecma262/#sec-array.prototype.sort
  1100. JS_DEFINE_NATIVE_FUNCTION(ArrayPrototype::sort)
  1101. {
  1102. // 1. If comparefn is not undefined and IsCallable(comparefn) is false, throw a TypeError exception.
  1103. auto comparefn = vm.argument(0);
  1104. if (!comparefn.is_undefined() && !comparefn.is_function())
  1105. return vm.throw_completion<TypeError>(ErrorType::NotAFunction, comparefn.to_string_without_side_effects());
  1106. // 2. Let obj be ? ToObject(this value).
  1107. auto object = TRY(vm.this_value().to_object(vm));
  1108. // 3. Let len be ? LengthOfArrayLike(obj).
  1109. auto length = TRY(length_of_array_like(vm, object));
  1110. // 4. Let SortCompare be a new Abstract Closure with parameters (x, y) that captures comparefn and performs the following steps when called:
  1111. Function<ThrowCompletionOr<double>(Value, Value)> sort_compare = [&](auto x, auto y) -> ThrowCompletionOr<double> {
  1112. // a. Return ? CompareArrayElements(x, y, comparefn).
  1113. return TRY(compare_array_elements(vm, x, y, comparefn.is_undefined() ? nullptr : &comparefn.as_function()));
  1114. };
  1115. // 5. Let sortedList be ? SortIndexedProperties(obj, len, SortCompare, skip-holes).
  1116. auto sorted_list = TRY(sort_indexed_properties(vm, object, length, sort_compare, Holes::SkipHoles));
  1117. // 6. Let itemCount be the number of elements in sortedList.
  1118. auto item_count = sorted_list.size();
  1119. // 7. Let j be 0.
  1120. size_t j = 0;
  1121. // 8. Repeat, while j < itemCount,
  1122. for (; j < item_count; ++j) {
  1123. // a. Perform ? Set(obj, ! ToString(𝔽(j)), sortedList[j], true).
  1124. TRY(object->set(j, sorted_list[j], Object::ShouldThrowExceptions::Yes));
  1125. // b. Set j to j + 1.
  1126. }
  1127. // 9. NOTE: The call to SortIndexedProperties in step 5 uses skip-holes. The remaining indices are deleted to preserve the number of holes that were detected and excluded from the sort.
  1128. // 10. Repeat, while j < len,
  1129. for (; j < length; ++j) {
  1130. // a. Perform ? DeletePropertyOrThrow(obj, ! ToString(𝔽(j))).
  1131. TRY(object->delete_property_or_throw(j));
  1132. // b. Set j to j + 1.
  1133. }
  1134. // 11. Return obj.
  1135. return object;
  1136. }
  1137. // 23.1.3.31 Array.prototype.splice ( start, deleteCount, ...items ), https://tc39.es/ecma262/#sec-array.prototype.splice
  1138. JS_DEFINE_NATIVE_FUNCTION(ArrayPrototype::splice)
  1139. {
  1140. // 1. Let O be ? ToObject(this value).
  1141. auto this_object = TRY(vm.this_value().to_object(vm));
  1142. // 2. Let len be ? LengthOfArrayLike(O).
  1143. auto initial_length = TRY(length_of_array_like(vm, this_object));
  1144. // 3. Let relativeStart be ? ToIntegerOrInfinity(start).
  1145. auto relative_start = TRY(vm.argument(0).to_integer_or_infinity(vm));
  1146. u64 actual_start;
  1147. // 4. If relativeStart = -∞, let actualStart be 0.
  1148. if (Value(relative_start).is_negative_infinity())
  1149. actual_start = 0;
  1150. // 5. Else if relativeStart < 0, let actualStart be max(len + relativeStart, 0).
  1151. else if (relative_start < 0)
  1152. actual_start = max((ssize_t)initial_length + relative_start, (ssize_t)0);
  1153. // 6. Else, let actualStart be min(relativeStart, len).
  1154. else
  1155. actual_start = min(relative_start, initial_length);
  1156. // 7. Let itemCount be the number of elements in items.
  1157. u64 item_count = vm.argument_count() >= 2 ? vm.argument_count() - 2 : 0;
  1158. u64 actual_delete_count;
  1159. // 8. If start is not present, then
  1160. if (vm.argument_count() == 0) {
  1161. // a. Let actualDeleteCount be 0.
  1162. actual_delete_count = 0;
  1163. }
  1164. // 9. Else if deleteCount is not present, then
  1165. else if (vm.argument_count() == 1) {
  1166. // a. Let actualDeleteCount be len - actualStart.
  1167. actual_delete_count = initial_length - actual_start;
  1168. }
  1169. // 10. Else,
  1170. else {
  1171. // a. Let dc be ? ToIntegerOrInfinity(deleteCount).
  1172. auto delete_count = TRY(vm.argument(1).to_integer_or_infinity(vm));
  1173. // b. Let actualDeleteCount be the result of clamping dc between 0 and len - actualStart.
  1174. actual_delete_count = clamp(delete_count, 0, initial_length - actual_start);
  1175. }
  1176. // 11. If len + itemCount - actualDeleteCount > 2^53 - 1, throw a TypeError exception.
  1177. if (initial_length + item_count - actual_delete_count > MAX_ARRAY_LIKE_INDEX)
  1178. return vm.throw_completion<TypeError>(ErrorType::ArrayMaxSize);
  1179. // 12. Let A be ? ArraySpeciesCreate(O, actualDeleteCount).
  1180. auto* removed_elements = TRY(array_species_create(vm, this_object, actual_delete_count));
  1181. // 13. Let k be 0.
  1182. // 14. Repeat, while k < actualDeleteCount,
  1183. for (u64 k = 0; k < actual_delete_count; ++k) {
  1184. // a. Let from be ! ToString(𝔽(actualStart + k)).
  1185. auto from = PropertyKey { actual_start + k };
  1186. // b. If ? HasProperty(O, from) is true, then
  1187. if (TRY(this_object->has_property(from))) {
  1188. // i. Let fromValue be ? Get(O, from).
  1189. auto from_value = TRY(this_object->get(from));
  1190. // ii. Perform ? CreateDataPropertyOrThrow(A, ! ToString(𝔽(k)), fromValue).
  1191. TRY(removed_elements->create_data_property_or_throw(k, from_value));
  1192. }
  1193. // c. Set k to k + 1.
  1194. }
  1195. // 15. Perform ? Set(A, "length", 𝔽(actualDeleteCount), true).
  1196. TRY(removed_elements->set(vm.names.length, Value(actual_delete_count), Object::ShouldThrowExceptions::Yes));
  1197. // 16. If itemCount < actualDeleteCount, then
  1198. if (item_count < actual_delete_count) {
  1199. // a. Set k to actualStart.
  1200. // b. Repeat, while k < (len - actualDeleteCount),
  1201. for (u64 k = actual_start; k < initial_length - actual_delete_count; ++k) {
  1202. // i. Let from be ! ToString(𝔽(k + actualDeleteCount)).
  1203. auto from = PropertyKey { k + actual_delete_count };
  1204. // ii. Let to be ! ToString(𝔽(k + itemCount)).
  1205. auto to = PropertyKey { k + item_count };
  1206. // iii. If ? HasProperty(O, from) is true, then
  1207. if (TRY(this_object->has_property(from))) {
  1208. // 1. Let fromValue be ? Get(O, from).
  1209. auto from_value = TRY(this_object->get(from));
  1210. // 2. Perform ? Set(O, to, fromValue, true).
  1211. TRY(this_object->set(to, from_value, Object::ShouldThrowExceptions::Yes));
  1212. }
  1213. // iv. Else,
  1214. else {
  1215. // 1. Perform ? DeletePropertyOrThrow(O, to).
  1216. TRY(this_object->delete_property_or_throw(to));
  1217. }
  1218. // v. Set k to k + 1.
  1219. }
  1220. // c. Set k to len.
  1221. // d. Repeat, while k > (len - actualDeleteCount + itemCount),
  1222. for (u64 k = initial_length; k > initial_length - actual_delete_count + item_count; --k) {
  1223. // i. Perform ? DeletePropertyOrThrow(O, ! ToString(𝔽(k - 1))).
  1224. TRY(this_object->delete_property_or_throw(k - 1));
  1225. // ii. Set k to k - 1.
  1226. }
  1227. }
  1228. // 17. Else if itemCount > actualDeleteCount, then
  1229. else if (item_count > actual_delete_count) {
  1230. // a. Set k to (len - actualDeleteCount).
  1231. // b. Repeat, while k > actualStart,
  1232. for (u64 k = initial_length - actual_delete_count; k > actual_start; --k) {
  1233. // i. Let from be ! ToString(𝔽(k + actualDeleteCount - 1)).
  1234. auto from = PropertyKey { k + actual_delete_count - 1 };
  1235. // ii. Let to be ! ToString(𝔽(k + itemCount - 1)).
  1236. auto to = PropertyKey { k + item_count - 1 };
  1237. // iii. If ? HasProperty(O, from) is true, then
  1238. if (TRY(this_object->has_property(from))) {
  1239. // 1. Let fromValue be ? Get(O, from).
  1240. auto from_value = TRY(this_object->get(from));
  1241. // 2. Perform ? Set(O, to, fromValue, true).
  1242. TRY(this_object->set(to, from_value, Object::ShouldThrowExceptions::Yes));
  1243. }
  1244. // iv. Else,
  1245. else {
  1246. // 1. Perform ? DeletePropertyOrThrow(O, to).
  1247. TRY(this_object->delete_property_or_throw(to));
  1248. }
  1249. // v. Set k to k - 1.
  1250. }
  1251. }
  1252. // 18. Set k to actualStart.
  1253. auto k = actual_start;
  1254. // 19. For each element E of items, do
  1255. for (size_t element_index = 2; element_index < vm.argument_count(); ++element_index) {
  1256. auto element = vm.argument(element_index);
  1257. // a. Perform ? Set(O, ! ToString(𝔽(k)), E, true).
  1258. TRY(this_object->set(k, element, Object::ShouldThrowExceptions::Yes));
  1259. // b. Set k to k + 1.
  1260. ++k;
  1261. }
  1262. // 20. Perform ? Set(O, "length", 𝔽(len - actualDeleteCount + itemCount), true).
  1263. TRY(this_object->set(vm.names.length, Value(initial_length - actual_delete_count + item_count), Object::ShouldThrowExceptions::Yes));
  1264. // 21. Return A.
  1265. return removed_elements;
  1266. }
  1267. // 23.1.3.32 Array.prototype.toLocaleString ( [ reserved1 [ , reserved2 ] ] ), https://tc39.es/ecma262/#sec-array.prototype.tolocalestring
  1268. // 19.5.1 Array.prototype.toLocaleString ( [ locales [ , options ] ] ), https://tc39.es/ecma402/#sup-array.prototype.tolocalestring
  1269. JS_DEFINE_NATIVE_FUNCTION(ArrayPrototype::to_locale_string)
  1270. {
  1271. auto locales = vm.argument(0);
  1272. auto options = vm.argument(1);
  1273. // 1. Let array be ? ToObject(this value).
  1274. auto this_object = TRY(vm.this_value().to_object(vm));
  1275. if (s_array_join_seen_objects.contains(this_object))
  1276. return PrimitiveString::create(vm, String {});
  1277. s_array_join_seen_objects.set(this_object);
  1278. ArmedScopeGuard unsee_object_guard = [&] {
  1279. s_array_join_seen_objects.remove(this_object);
  1280. };
  1281. // 2. Let len be ? ToLength(? Get(array, "length")).
  1282. auto length = TRY(length_of_array_like(vm, this_object));
  1283. // 3. Let separator be the implementation-defined list-separator String value appropriate for the host environment's current locale (such as ", ").
  1284. constexpr auto separator = ","sv;
  1285. // 4. Let R be the empty String.
  1286. StringBuilder builder;
  1287. // 5. Let k be 0.
  1288. // 6. Repeat, while k < len,
  1289. for (size_t i = 0; i < length; ++i) {
  1290. // a. If k > 0, then
  1291. if (i > 0) {
  1292. // i. Set R to the string-concatenation of R and separator.
  1293. builder.append(separator);
  1294. }
  1295. // b. Let nextElement be ? Get(array, ! ToString(k)).
  1296. auto value = TRY(this_object->get(i));
  1297. // c. If nextElement is not undefined or null, then
  1298. if (!value.is_nullish()) {
  1299. // i. Let S be ? ToString(? Invoke(nextElement, "toLocaleString", « locales, options »)).
  1300. auto locale_string_result = TRY(value.invoke(vm, vm.names.toLocaleString, locales, options));
  1301. // ii. Set R to the string-concatenation of R and S.
  1302. auto string = TRY(locale_string_result.to_byte_string(vm));
  1303. builder.append(string);
  1304. }
  1305. // d. Increase k by 1.
  1306. }
  1307. // 7. Return R.
  1308. return PrimitiveString::create(vm, builder.to_byte_string());
  1309. }
  1310. // 23.1.3.33 Array.prototype.toReversed ( ), https://tc39.es/ecma262/#sec-array.prototype.toreversed
  1311. JS_DEFINE_NATIVE_FUNCTION(ArrayPrototype::to_reversed)
  1312. {
  1313. auto& realm = *vm.current_realm();
  1314. // 1. Let O be ? ToObject(this value).
  1315. auto object = TRY(vm.this_value().to_object(vm));
  1316. // 2. Let len be ? LengthOfArrayLike(O).
  1317. auto length = TRY(length_of_array_like(vm, object));
  1318. // 3. Let A be ? ArrayCreate(𝔽(len)).
  1319. auto array = TRY(Array::create(realm, length));
  1320. // 4. Let k be 0.
  1321. // 5. Repeat, while k < len,
  1322. for (size_t k = 0; k < length; ++k) {
  1323. // a. Let from be ! ToString(𝔽(len - k - 1)).
  1324. auto from = PropertyKey { length - k - 1 };
  1325. // b. Let Pk be ! ToString(𝔽(k)).
  1326. auto property_key = PropertyKey { k };
  1327. // c. Let fromValue be ? Get(O, from).
  1328. auto from_value = TRY(object->get(from));
  1329. // d. Perform ! CreateDataPropertyOrThrow(A, Pk, fromValue).
  1330. MUST(array->create_data_property_or_throw(property_key, from_value));
  1331. // e. Set k to k + 1.
  1332. }
  1333. // 6. Return A.
  1334. return array;
  1335. }
  1336. // 23.1.3.34 Array.prototype.toSorted ( comparefn ), https://tc39.es/ecma262/#sec-array.prototype.tosorted
  1337. JS_DEFINE_NATIVE_FUNCTION(ArrayPrototype::to_sorted)
  1338. {
  1339. auto& realm = *vm.current_realm();
  1340. auto comparefn = vm.argument(0);
  1341. // 1. If comparefn is not undefined and IsCallable(comparefn) is false, throw a TypeError exception.
  1342. if (!comparefn.is_undefined() && !comparefn.is_function())
  1343. return vm.throw_completion<TypeError>(ErrorType::NotAFunction, comparefn);
  1344. // 2. Let O be ? ToObject(this value).
  1345. auto object = TRY(vm.this_value().to_object(vm));
  1346. // 3. Let len be ? LengthOfArrayLike(O).
  1347. auto length = TRY(length_of_array_like(vm, object));
  1348. // 4. Let A be ? ArrayCreate(𝔽(len)).
  1349. auto array = TRY(Array::create(realm, length));
  1350. // 5. Let SortCompare be a new Abstract Closure with parameters (x, y) that captures comparefn and performs the following steps when called:
  1351. Function<ThrowCompletionOr<double>(Value, Value)> sort_compare = [&](auto x, auto y) -> ThrowCompletionOr<double> {
  1352. // a. Return ? CompareArrayElements(x, y, comparefn).
  1353. return TRY(compare_array_elements(vm, x, y, comparefn.is_undefined() ? nullptr : &comparefn.as_function()));
  1354. };
  1355. // 6. Let sortedList be ? SortIndexedProperties(obj, len, SortCompare, read-through-holes).
  1356. auto sorted_list = TRY(sort_indexed_properties(vm, object, length, sort_compare, Holes::ReadThroughHoles));
  1357. // 7. Let j be 0.
  1358. // 8. Repeat, while j < len,
  1359. for (size_t j = 0; j < length; ++j) {
  1360. // a. Perform ! CreateDataPropertyOrThrow(A, ! ToString(𝔽(j)), sortedList[j]).
  1361. MUST(array->create_data_property_or_throw(j, sorted_list[j]));
  1362. // b. Set j to j + 1.
  1363. }
  1364. // 9. Return A.
  1365. return array;
  1366. }
  1367. // 23.1.3.35 Array.prototype.toSpliced ( start, skipCount, ...items ), https://tc39.es/ecma262/#sec-array.prototype.tospliced
  1368. JS_DEFINE_NATIVE_FUNCTION(ArrayPrototype::to_spliced)
  1369. {
  1370. auto& realm = *vm.current_realm();
  1371. auto start = vm.argument(0);
  1372. auto skip_count = vm.argument(1);
  1373. // 1. Let O be ? ToObject(this value).
  1374. auto object = TRY(vm.this_value().to_object(vm));
  1375. // 2. Let len be ? LengthOfArrayLike(O).
  1376. auto length = TRY(length_of_array_like(vm, object));
  1377. // 3. Let relativeStart be ? ToIntegerOrInfinity(start).
  1378. auto relative_start = TRY(start.to_integer_or_infinity(vm));
  1379. size_t actual_start;
  1380. // 4. If relativeStart is -∞, let actualStart be 0.
  1381. if (Value(relative_start).is_negative_infinity())
  1382. actual_start = 0;
  1383. // 5. Else if relativeStart < 0, let actualStart be max(len + relativeStart, 0).
  1384. else if (relative_start < 0)
  1385. actual_start = static_cast<size_t>(max(static_cast<double>(length) + relative_start, 0));
  1386. // 6. Else, let actualStart be min(relativeStart, len).
  1387. else
  1388. actual_start = static_cast<size_t>(min(relative_start, static_cast<double>(length)));
  1389. // Sanity check
  1390. VERIFY(actual_start <= length);
  1391. // 7. Let insertCount be the number of elements in items.
  1392. auto insert_count = vm.argument_count() >= 2 ? vm.argument_count() - 2 : 0;
  1393. size_t actual_skip_count;
  1394. // 8. If start is not present, then
  1395. if (vm.argument_count() == 0) {
  1396. // a. Let actualSkipCount be 0.
  1397. actual_skip_count = 0;
  1398. }
  1399. // 9. Else if deleteCount is not present, then
  1400. else if (vm.argument_count() == 1) {
  1401. // a. Let actualSkipCount be len - actualStart.
  1402. actual_skip_count = length - actual_start;
  1403. }
  1404. // 10. Else,
  1405. else {
  1406. // a. Let sc be ? ToIntegerOrInfinity(skipCount).
  1407. auto sc = TRY(skip_count.to_integer_or_infinity(vm));
  1408. // b. Let actualSkipCount be the result of clamping sc between 0 and len - actualStart.
  1409. actual_skip_count = static_cast<size_t>(clamp(sc, 0, static_cast<double>(length - actual_start)));
  1410. }
  1411. // Sanity check
  1412. VERIFY(actual_skip_count <= (length - actual_start));
  1413. // 11. Let newLen be len + insertCount - actualSkipCount.
  1414. auto new_length_double = static_cast<double>(length) + static_cast<double>(insert_count) - static_cast<double>(actual_skip_count);
  1415. // 12. If newLen > 2^53 - 1, throw a TypeError exception.
  1416. if (new_length_double > MAX_ARRAY_LIKE_INDEX)
  1417. return vm.throw_completion<TypeError>(ErrorType::ArrayMaxSize);
  1418. auto new_length = static_cast<u64>(new_length_double);
  1419. // 13. Let A be ? ArrayCreate(𝔽(newLen)).
  1420. auto array = TRY(Array::create(realm, new_length));
  1421. // 14. Let i be 0.
  1422. size_t i = 0;
  1423. // 15. Let r be actualStart + actualSkipCount.
  1424. auto r = actual_start + actual_skip_count;
  1425. // 16. Repeat, while i < actualStart,
  1426. while (i < actual_start) {
  1427. // a. Let Pi be ! ToString(𝔽(i)).
  1428. auto property_key = PropertyKey { i };
  1429. // b. Let iValue be ? Get(O, Pi).
  1430. auto i_value = TRY(object->get(property_key));
  1431. // c. Perform ! CreateDataPropertyOrThrow(A, Pi, iValue).
  1432. MUST(array->create_data_property_or_throw(property_key, i_value));
  1433. // d. Set i to i + 1.
  1434. ++i;
  1435. }
  1436. // 17. For each element E of items, do
  1437. for (size_t element_index = 2; element_index < vm.argument_count(); ++element_index) {
  1438. auto element = vm.argument(element_index);
  1439. // a. Let Pi be ! ToString(𝔽(i)).
  1440. auto property_key = PropertyKey { i };
  1441. // b. Perform ! CreateDataPropertyOrThrow(A, Pi, E).
  1442. MUST(array->create_data_property_or_throw(property_key, element));
  1443. // c. Set i to i + 1.
  1444. ++i;
  1445. }
  1446. // 18. Repeat, while i < newLen,
  1447. while (i < new_length) {
  1448. // a. Let Pi be ! ToString(𝔽(i)).
  1449. auto property_key = PropertyKey { i };
  1450. // b. Let from be ! ToString(𝔽(r)).
  1451. auto from = PropertyKey { r };
  1452. // c. Let fromValue be ? Get(O, from).
  1453. auto from_value = TRY(object->get(from));
  1454. // d. Perform ! CreateDataPropertyOrThrow(A, Pi, fromValue).
  1455. MUST(array->create_data_property_or_throw(property_key, from_value));
  1456. // e. Set i to i + 1.
  1457. ++i;
  1458. // f. Set r to r + 1.
  1459. ++r;
  1460. }
  1461. // 19. Return A.
  1462. return array;
  1463. }
  1464. // 23.1.3.36 Array.prototype.toString ( ), https://tc39.es/ecma262/#sec-array.prototype.tostring
  1465. JS_DEFINE_NATIVE_FUNCTION(ArrayPrototype::to_string)
  1466. {
  1467. auto& realm = *vm.current_realm();
  1468. // 1. Let array be ? ToObject(this value).
  1469. auto array = TRY(vm.this_value().to_object(vm));
  1470. // 2. Let func be ? Get(array, "join").
  1471. auto func = TRY(array->get(vm.names.join));
  1472. // 3. If IsCallable(func) is false, set func to the intrinsic function %Object.prototype.toString%.
  1473. if (!func.is_function())
  1474. func = realm.intrinsics().object_prototype_to_string_function();
  1475. // 4. Return ? Call(func, array).
  1476. return TRY(call(vm, func.as_function(), array));
  1477. }
  1478. // 23.1.3.37 Array.prototype.unshift ( ...items ), https://tc39.es/ecma262/#sec-array.prototype.unshift
  1479. JS_DEFINE_NATIVE_FUNCTION(ArrayPrototype::unshift)
  1480. {
  1481. auto this_object = TRY(vm.this_value().to_object(vm));
  1482. auto length = TRY(length_of_array_like(vm, this_object));
  1483. auto arg_count = vm.argument_count();
  1484. size_t new_length = length + arg_count;
  1485. if (arg_count > 0) {
  1486. if (new_length > MAX_ARRAY_LIKE_INDEX)
  1487. return vm.throw_completion<TypeError>(ErrorType::ArrayMaxSize);
  1488. for (size_t k = length; k > 0; --k) {
  1489. auto from = k - 1;
  1490. auto to = k + arg_count - 1;
  1491. bool from_present = TRY(this_object->has_property(from));
  1492. if (from_present) {
  1493. auto from_value = TRY(this_object->get(from));
  1494. TRY(this_object->set(to, from_value, Object::ShouldThrowExceptions::Yes));
  1495. } else {
  1496. TRY(this_object->delete_property_or_throw(to));
  1497. }
  1498. }
  1499. for (size_t j = 0; j < arg_count; j++)
  1500. TRY(this_object->set(j, vm.argument(j), Object::ShouldThrowExceptions::Yes));
  1501. }
  1502. TRY(this_object->set(vm.names.length, Value(new_length), Object::ShouldThrowExceptions::Yes));
  1503. return Value(new_length);
  1504. }
  1505. // 23.1.3.38 Array.prototype.values ( ), https://tc39.es/ecma262/#sec-array.prototype.values
  1506. JS_DEFINE_NATIVE_FUNCTION(ArrayPrototype::values)
  1507. {
  1508. auto& realm = *vm.current_realm();
  1509. auto this_object = TRY(vm.this_value().to_object(vm));
  1510. return ArrayIterator::create(realm, this_object, Object::PropertyKind::Value);
  1511. }
  1512. // 23.1.3.39 Array.prototype.with ( index, value ), https://tc39.es/ecma262/#sec-array.prototype.with
  1513. JS_DEFINE_NATIVE_FUNCTION(ArrayPrototype::with)
  1514. {
  1515. auto& realm = *vm.current_realm();
  1516. auto index = vm.argument(0);
  1517. auto value = vm.argument(1);
  1518. // 1. Let O be ? ToObject(this value).
  1519. auto object = TRY(vm.this_value().to_object(vm));
  1520. // 2. Let len be ? LengthOfArrayLike(O).
  1521. auto length = TRY(length_of_array_like(vm, object));
  1522. // 3. Let relativeIndex be ? ToIntegerOrInfinity(index).
  1523. auto relative_index = TRY(index.to_integer_or_infinity(vm));
  1524. double actual_index;
  1525. // 4. If relativeIndex ≥ 0, let actualIndex be relativeIndex.
  1526. if (relative_index >= 0)
  1527. actual_index = relative_index;
  1528. // 5. Else, let actualIndex be len + relativeIndex.
  1529. else
  1530. actual_index = static_cast<double>(length) + relative_index;
  1531. // 6. If actualIndex ≥ len or actualIndex < 0, throw a RangeError exception.
  1532. if (actual_index >= static_cast<double>(length) || actual_index < 0)
  1533. return vm.throw_completion<RangeError>(ErrorType::IndexOutOfRange, actual_index, length);
  1534. // 7. Let A be ? ArrayCreate(𝔽(len)).
  1535. auto array = TRY(Array::create(realm, length));
  1536. // 8. Let k be 0.
  1537. // 9. Repeat, while k < len,
  1538. for (size_t k = 0; k < length; ++k) {
  1539. // a. Let Pk be ! ToString(𝔽(k)).
  1540. auto property_key = PropertyKey { k };
  1541. Value from_value;
  1542. // b. If k is actualIndex, let fromValue be value.
  1543. if (k == static_cast<size_t>(actual_index))
  1544. from_value = value;
  1545. // c. Else, let fromValue be ? Get(O, Pk).
  1546. else
  1547. from_value = TRY(object->get(property_key));
  1548. // d. Perform ! CreateDataPropertyOrThrow(A, Pk, fromValue).
  1549. MUST(array->create_data_property_or_throw(property_key, from_value));
  1550. // e. Set k to k + 1.
  1551. }
  1552. // 10. Return A.
  1553. return array;
  1554. }
  1555. }