ArrayPrototype.cpp 33 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043
  1. /*
  2. * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
  3. * Copyright (c) 2020, Linus Groh <mail@linusgroh.de>
  4. * Copyright (c) 2020, Marcin Gasperowicz <xnooga@gmail.com>
  5. * All rights reserved.
  6. *
  7. * Redistribution and use in source and binary forms, with or without
  8. * modification, are permitted provided that the following conditions are met:
  9. *
  10. * 1. Redistributions of source code must retain the above copyright notice, this
  11. * list of conditions and the following disclaimer.
  12. *
  13. * 2. Redistributions in binary form must reproduce the above copyright notice,
  14. * this list of conditions and the following disclaimer in the documentation
  15. * and/or other materials provided with the distribution.
  16. *
  17. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  18. * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  19. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  20. * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
  21. * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  22. * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  23. * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  24. * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  25. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  26. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  27. */
  28. #include <AK/Function.h>
  29. #include <AK/HashTable.h>
  30. #include <AK/ScopeGuard.h>
  31. #include <AK/StringBuilder.h>
  32. #include <LibJS/Runtime/Array.h>
  33. #include <LibJS/Runtime/ArrayIterator.h>
  34. #include <LibJS/Runtime/ArrayPrototype.h>
  35. #include <LibJS/Runtime/Error.h>
  36. #include <LibJS/Runtime/Function.h>
  37. #include <LibJS/Runtime/GlobalObject.h>
  38. #include <LibJS/Runtime/ObjectPrototype.h>
  39. #include <LibJS/Runtime/Value.h>
  40. namespace JS {
  41. static HashTable<Object*> s_array_join_seen_objects;
  42. ArrayPrototype::ArrayPrototype(GlobalObject& global_object)
  43. : Object(*global_object.object_prototype())
  44. {
  45. }
  46. void ArrayPrototype::initialize(GlobalObject& global_object)
  47. {
  48. auto& vm = this->vm();
  49. Object::initialize(global_object);
  50. u8 attr = Attribute::Writable | Attribute::Configurable;
  51. define_native_function(vm.names.filter, filter, 1, attr);
  52. define_native_function(vm.names.forEach, for_each, 1, attr);
  53. define_native_function(vm.names.map, map, 1, attr);
  54. define_native_function(vm.names.pop, pop, 0, attr);
  55. define_native_function(vm.names.push, push, 1, attr);
  56. define_native_function(vm.names.shift, shift, 0, attr);
  57. define_native_function(vm.names.toString, to_string, 0, attr);
  58. define_native_function(vm.names.toLocaleString, to_locale_string, 0, attr);
  59. define_native_function(vm.names.unshift, unshift, 1, attr);
  60. define_native_function(vm.names.join, join, 1, attr);
  61. define_native_function(vm.names.concat, concat, 1, attr);
  62. define_native_function(vm.names.slice, slice, 2, attr);
  63. define_native_function(vm.names.indexOf, index_of, 1, attr);
  64. define_native_function(vm.names.reduce, reduce, 1, attr);
  65. define_native_function(vm.names.reduceRight, reduce_right, 1, attr);
  66. define_native_function(vm.names.reverse, reverse, 0, attr);
  67. define_native_function(vm.names.sort, sort, 1, attr);
  68. define_native_function(vm.names.lastIndexOf, last_index_of, 1, attr);
  69. define_native_function(vm.names.includes, includes, 1, attr);
  70. define_native_function(vm.names.find, find, 1, attr);
  71. define_native_function(vm.names.findIndex, find_index, 1, attr);
  72. define_native_function(vm.names.some, some, 1, attr);
  73. define_native_function(vm.names.every, every, 1, attr);
  74. define_native_function(vm.names.splice, splice, 2, attr);
  75. define_native_function(vm.names.fill, fill, 1, attr);
  76. define_native_function(vm.names.values, values, 0, attr);
  77. define_property(vm.names.length, Value(0), Attribute::Configurable);
  78. // Use define_property here instead of define_native_function so that
  79. // Object.is(Array.prototype[Symbol.iterator], Array.prototype.values)
  80. // evaluates to true
  81. define_property(vm.well_known_symbol_iterator(), get(vm.names.values), attr);
  82. }
  83. ArrayPrototype::~ArrayPrototype()
  84. {
  85. }
  86. static Function* callback_from_args(GlobalObject& global_object, const String& name)
  87. {
  88. auto& vm = global_object.vm();
  89. if (vm.argument_count() < 1) {
  90. vm.throw_exception<TypeError>(global_object, ErrorType::ArrayPrototypeOneArg, name);
  91. return nullptr;
  92. }
  93. auto callback = vm.argument(0);
  94. if (!callback.is_function()) {
  95. vm.throw_exception<TypeError>(global_object, ErrorType::NotAFunction, callback.to_string_without_side_effects());
  96. return nullptr;
  97. }
  98. return &callback.as_function();
  99. }
  100. static size_t get_length(VM& vm, Object& object)
  101. {
  102. auto length_property = object.get(vm.names.length);
  103. if (vm.exception())
  104. return 0;
  105. return length_property.to_size_t(object.global_object());
  106. }
  107. static void for_each_item(VM& vm, GlobalObject& global_object, const String& name, AK::Function<IterationDecision(size_t index, Value value, Value callback_result)> callback, bool skip_empty = true)
  108. {
  109. auto* this_object = vm.this_value(global_object).to_object(global_object);
  110. if (!this_object)
  111. return;
  112. auto initial_length = get_length(vm, *this_object);
  113. if (vm.exception())
  114. return;
  115. auto* callback_function = callback_from_args(global_object, name);
  116. if (!callback_function)
  117. return;
  118. auto this_value = vm.argument(1);
  119. for (size_t i = 0; i < initial_length; ++i) {
  120. auto value = this_object->get(i);
  121. if (vm.exception())
  122. return;
  123. if (value.is_empty()) {
  124. if (skip_empty)
  125. continue;
  126. value = js_undefined();
  127. }
  128. auto callback_result = vm.call(*callback_function, this_value, value, Value((i32)i), this_object);
  129. if (vm.exception())
  130. return;
  131. if (callback(i, value, callback_result) == IterationDecision::Break)
  132. break;
  133. }
  134. }
  135. JS_DEFINE_NATIVE_FUNCTION(ArrayPrototype::filter)
  136. {
  137. auto* new_array = Array::create(global_object);
  138. for_each_item(vm, global_object, "filter", [&](auto, auto value, auto callback_result) {
  139. if (callback_result.to_boolean())
  140. new_array->indexed_properties().append(value);
  141. return IterationDecision::Continue;
  142. });
  143. return Value(new_array);
  144. }
  145. JS_DEFINE_NATIVE_FUNCTION(ArrayPrototype::for_each)
  146. {
  147. for_each_item(vm, global_object, "forEach", [](auto, auto, auto) {
  148. return IterationDecision::Continue;
  149. });
  150. return js_undefined();
  151. }
  152. JS_DEFINE_NATIVE_FUNCTION(ArrayPrototype::map)
  153. {
  154. auto* this_object = vm.this_value(global_object).to_object(global_object);
  155. if (!this_object)
  156. return {};
  157. auto initial_length = get_length(vm, *this_object);
  158. if (vm.exception())
  159. return {};
  160. auto* new_array = Array::create(global_object);
  161. new_array->indexed_properties().set_array_like_size(initial_length);
  162. for_each_item(vm, global_object, "map", [&](auto index, auto, auto callback_result) {
  163. if (vm.exception())
  164. return IterationDecision::Break;
  165. new_array->define_property(index, callback_result);
  166. return IterationDecision::Continue;
  167. });
  168. return Value(new_array);
  169. }
  170. JS_DEFINE_NATIVE_FUNCTION(ArrayPrototype::push)
  171. {
  172. auto* this_object = vm.this_value(global_object).to_object(global_object);
  173. if (!this_object)
  174. return {};
  175. if (this_object->is_array()) {
  176. auto* array = static_cast<Array*>(this_object);
  177. for (size_t i = 0; i < vm.argument_count(); ++i)
  178. array->indexed_properties().append(vm.argument(i));
  179. return Value(static_cast<i32>(array->indexed_properties().array_like_size()));
  180. }
  181. auto length = get_length(vm, *this_object);
  182. if (vm.exception())
  183. return {};
  184. auto argument_count = vm.argument_count();
  185. auto new_length = length + argument_count;
  186. if (new_length > MAX_ARRAY_LIKE_INDEX) {
  187. vm.throw_exception<TypeError>(global_object, ErrorType::ArrayMaxSize);
  188. return {};
  189. }
  190. for (size_t i = 0; i < argument_count; ++i) {
  191. this_object->put(length + i, vm.argument(i));
  192. if (vm.exception())
  193. return {};
  194. }
  195. auto new_length_value = Value((i32)new_length);
  196. this_object->put(vm.names.length, new_length_value);
  197. if (vm.exception())
  198. return {};
  199. return new_length_value;
  200. }
  201. JS_DEFINE_NATIVE_FUNCTION(ArrayPrototype::unshift)
  202. {
  203. auto* array = Array::typed_this(vm, global_object);
  204. if (!array)
  205. return {};
  206. for (size_t i = 0; i < vm.argument_count(); ++i)
  207. array->indexed_properties().insert(i, vm.argument(i));
  208. return Value(static_cast<i32>(array->indexed_properties().array_like_size()));
  209. }
  210. JS_DEFINE_NATIVE_FUNCTION(ArrayPrototype::pop)
  211. {
  212. auto* this_object = vm.this_value(global_object).to_object(global_object);
  213. if (!this_object)
  214. return {};
  215. if (this_object->is_array()) {
  216. auto* array = static_cast<Array*>(this_object);
  217. if (array->indexed_properties().is_empty())
  218. return js_undefined();
  219. return array->indexed_properties().take_last(array).value.value_or(js_undefined());
  220. }
  221. auto length = get_length(vm, *this_object);
  222. if (length == 0) {
  223. this_object->put(vm.names.length, Value(0));
  224. return js_undefined();
  225. }
  226. auto index = length - 1;
  227. auto element = this_object->get(index).value_or(js_undefined());
  228. if (vm.exception())
  229. return {};
  230. this_object->delete_property(index);
  231. if (vm.exception())
  232. return {};
  233. this_object->put(vm.names.length, Value((i32)index));
  234. if (vm.exception())
  235. return {};
  236. return element;
  237. }
  238. JS_DEFINE_NATIVE_FUNCTION(ArrayPrototype::shift)
  239. {
  240. auto* array = Array::typed_this(vm, global_object);
  241. if (!array)
  242. return {};
  243. if (array->indexed_properties().is_empty())
  244. return js_undefined();
  245. auto result = array->indexed_properties().take_first(array);
  246. if (vm.exception())
  247. return {};
  248. return result.value.value_or(js_undefined());
  249. }
  250. JS_DEFINE_NATIVE_FUNCTION(ArrayPrototype::to_string)
  251. {
  252. auto* this_object = vm.this_value(global_object).to_object(global_object);
  253. if (!this_object)
  254. return {};
  255. auto join_function = this_object->get(vm.names.join);
  256. if (vm.exception())
  257. return {};
  258. if (!join_function.is_function())
  259. return ObjectPrototype::to_string(vm, global_object);
  260. return vm.call(join_function.as_function(), this_object);
  261. }
  262. JS_DEFINE_NATIVE_FUNCTION(ArrayPrototype::to_locale_string)
  263. {
  264. auto* this_object = vm.this_value(global_object).to_object(global_object);
  265. if (!this_object)
  266. return {};
  267. if (s_array_join_seen_objects.contains(this_object))
  268. return js_string(vm, "");
  269. s_array_join_seen_objects.set(this_object);
  270. ArmedScopeGuard unsee_object_guard = [&] {
  271. s_array_join_seen_objects.remove(this_object);
  272. };
  273. auto length = get_length(vm, *this_object);
  274. if (vm.exception())
  275. return {};
  276. String separator = ","; // NOTE: This is implementation-specific.
  277. StringBuilder builder;
  278. for (size_t i = 0; i < length; ++i) {
  279. if (i > 0)
  280. builder.append(separator);
  281. auto value = this_object->get(i).value_or(js_undefined());
  282. if (vm.exception())
  283. return {};
  284. if (value.is_nullish())
  285. continue;
  286. auto* value_object = value.to_object(global_object);
  287. if (!value_object)
  288. return {};
  289. auto locale_string_result = value_object->invoke("toLocaleString");
  290. if (vm.exception())
  291. return {};
  292. auto string = locale_string_result.to_string(global_object);
  293. if (vm.exception())
  294. return {};
  295. builder.append(string);
  296. }
  297. return js_string(vm, builder.to_string());
  298. }
  299. JS_DEFINE_NATIVE_FUNCTION(ArrayPrototype::join)
  300. {
  301. auto* this_object = vm.this_value(global_object).to_object(global_object);
  302. if (!this_object)
  303. return {};
  304. // This is not part of the spec, but all major engines do some kind of circular reference checks.
  305. // FWIW: engine262, a "100% spec compliant" ECMA-262 impl, aborts with "too much recursion".
  306. // Same applies to Array.prototype.toLocaleString().
  307. if (s_array_join_seen_objects.contains(this_object))
  308. return js_string(vm, "");
  309. s_array_join_seen_objects.set(this_object);
  310. ArmedScopeGuard unsee_object_guard = [&] {
  311. s_array_join_seen_objects.remove(this_object);
  312. };
  313. auto length = get_length(vm, *this_object);
  314. if (vm.exception())
  315. return {};
  316. String separator = ",";
  317. if (!vm.argument(0).is_undefined()) {
  318. separator = vm.argument(0).to_string(global_object);
  319. if (vm.exception())
  320. return {};
  321. }
  322. StringBuilder builder;
  323. for (size_t i = 0; i < length; ++i) {
  324. if (i > 0)
  325. builder.append(separator);
  326. auto value = this_object->get(i).value_or(js_undefined());
  327. if (vm.exception())
  328. return {};
  329. if (value.is_nullish())
  330. continue;
  331. auto string = value.to_string(global_object);
  332. if (vm.exception())
  333. return {};
  334. builder.append(string);
  335. }
  336. return js_string(vm, builder.to_string());
  337. }
  338. JS_DEFINE_NATIVE_FUNCTION(ArrayPrototype::concat)
  339. {
  340. auto* array = Array::typed_this(vm, global_object);
  341. if (!array)
  342. return {};
  343. auto* new_array = Array::create(global_object);
  344. new_array->indexed_properties().append_all(array, array->indexed_properties());
  345. if (vm.exception())
  346. return {};
  347. for (size_t i = 0; i < vm.argument_count(); ++i) {
  348. auto argument = vm.argument(i);
  349. if (argument.is_array()) {
  350. auto& argument_object = argument.as_object();
  351. new_array->indexed_properties().append_all(&argument_object, argument_object.indexed_properties());
  352. if (vm.exception())
  353. return {};
  354. } else {
  355. new_array->indexed_properties().append(argument);
  356. }
  357. }
  358. return Value(new_array);
  359. }
  360. JS_DEFINE_NATIVE_FUNCTION(ArrayPrototype::slice)
  361. {
  362. auto* array = Array::typed_this(vm, global_object);
  363. if (!array)
  364. return {};
  365. auto* new_array = Array::create(global_object);
  366. if (vm.argument_count() == 0) {
  367. new_array->indexed_properties().append_all(array, array->indexed_properties());
  368. if (vm.exception())
  369. return {};
  370. return new_array;
  371. }
  372. ssize_t array_size = static_cast<ssize_t>(array->indexed_properties().array_like_size());
  373. auto start_slice = vm.argument(0).to_i32(global_object);
  374. if (vm.exception())
  375. return {};
  376. auto end_slice = array_size;
  377. if (start_slice > array_size)
  378. return new_array;
  379. if (start_slice < 0)
  380. start_slice = end_slice + start_slice;
  381. if (vm.argument_count() >= 2) {
  382. end_slice = vm.argument(1).to_i32(global_object);
  383. if (vm.exception())
  384. return {};
  385. if (end_slice < 0)
  386. end_slice = array_size + end_slice;
  387. else if (end_slice > array_size)
  388. end_slice = array_size;
  389. }
  390. for (ssize_t i = start_slice; i < end_slice; ++i) {
  391. new_array->indexed_properties().append(array->get(i));
  392. if (vm.exception())
  393. return {};
  394. }
  395. return new_array;
  396. }
  397. JS_DEFINE_NATIVE_FUNCTION(ArrayPrototype::index_of)
  398. {
  399. auto* this_object = vm.this_value(global_object).to_object(global_object);
  400. if (!this_object)
  401. return {};
  402. i32 length = get_length(vm, *this_object);
  403. if (vm.exception())
  404. return {};
  405. if (length == 0)
  406. return Value(-1);
  407. i32 from_index = 0;
  408. if (vm.argument_count() >= 2) {
  409. from_index = vm.argument(1).to_i32(global_object);
  410. if (vm.exception())
  411. return {};
  412. if (from_index >= length)
  413. return Value(-1);
  414. if (from_index < 0)
  415. from_index = max(length + from_index, 0);
  416. }
  417. auto search_element = vm.argument(0);
  418. for (i32 i = from_index; i < length; ++i) {
  419. auto element = this_object->get(i);
  420. if (vm.exception())
  421. return {};
  422. if (strict_eq(element, search_element))
  423. return Value(i);
  424. }
  425. return Value(-1);
  426. }
  427. JS_DEFINE_NATIVE_FUNCTION(ArrayPrototype::reduce)
  428. {
  429. auto* this_object = vm.this_value(global_object).to_object(global_object);
  430. if (!this_object)
  431. return {};
  432. auto initial_length = get_length(vm, *this_object);
  433. if (vm.exception())
  434. return {};
  435. auto* callback_function = callback_from_args(global_object, "reduce");
  436. if (!callback_function)
  437. return {};
  438. size_t start = 0;
  439. auto accumulator = js_undefined();
  440. if (vm.argument_count() > 1) {
  441. accumulator = vm.argument(1);
  442. } else {
  443. bool start_found = false;
  444. while (!start_found && start < initial_length) {
  445. auto value = this_object->get(start);
  446. if (vm.exception())
  447. return {};
  448. start_found = !value.is_empty();
  449. if (start_found)
  450. accumulator = value;
  451. start += 1;
  452. }
  453. if (!start_found) {
  454. vm.throw_exception<TypeError>(global_object, ErrorType::ReduceNoInitial);
  455. return {};
  456. }
  457. }
  458. auto this_value = js_undefined();
  459. for (size_t i = start; i < initial_length; ++i) {
  460. auto value = this_object->get(i);
  461. if (vm.exception())
  462. return {};
  463. if (value.is_empty())
  464. continue;
  465. accumulator = vm.call(*callback_function, this_value, accumulator, value, Value((i32)i), this_object);
  466. if (vm.exception())
  467. return {};
  468. }
  469. return accumulator;
  470. }
  471. JS_DEFINE_NATIVE_FUNCTION(ArrayPrototype::reduce_right)
  472. {
  473. auto* this_object = vm.this_value(global_object).to_object(global_object);
  474. if (!this_object)
  475. return {};
  476. auto initial_length = get_length(vm, *this_object);
  477. if (vm.exception())
  478. return {};
  479. auto* callback_function = callback_from_args(global_object, "reduceRight");
  480. if (!callback_function)
  481. return {};
  482. int start = initial_length - 1;
  483. auto accumulator = js_undefined();
  484. if (vm.argument_count() > 1) {
  485. accumulator = vm.argument(1);
  486. } else {
  487. bool start_found = false;
  488. while (!start_found && start >= 0) {
  489. auto value = this_object->get(start);
  490. if (vm.exception())
  491. return {};
  492. start_found = !value.is_empty();
  493. if (start_found)
  494. accumulator = value;
  495. start -= 1;
  496. }
  497. if (!start_found) {
  498. vm.throw_exception<TypeError>(global_object, ErrorType::ReduceNoInitial);
  499. return {};
  500. }
  501. }
  502. auto this_value = js_undefined();
  503. for (int i = start; i >= 0; --i) {
  504. auto value = this_object->get(i);
  505. if (vm.exception())
  506. return {};
  507. if (value.is_empty())
  508. continue;
  509. accumulator = vm.call(*callback_function, this_value, accumulator, value, Value((i32)i), this_object);
  510. if (vm.exception())
  511. return {};
  512. }
  513. return accumulator;
  514. }
  515. JS_DEFINE_NATIVE_FUNCTION(ArrayPrototype::reverse)
  516. {
  517. auto* array = Array::typed_this(vm, global_object);
  518. if (!array)
  519. return {};
  520. if (array->indexed_properties().is_empty())
  521. return array;
  522. MarkedValueList array_reverse(vm.heap());
  523. auto size = array->indexed_properties().array_like_size();
  524. array_reverse.ensure_capacity(size);
  525. for (ssize_t i = size - 1; i >= 0; --i) {
  526. array_reverse.append(array->get(i));
  527. if (vm.exception())
  528. return {};
  529. }
  530. array->set_indexed_property_elements(move(array_reverse));
  531. return array;
  532. }
  533. static void array_merge_sort(VM& vm, GlobalObject& global_object, Function* compare_func, MarkedValueList& arr_to_sort)
  534. {
  535. // FIXME: it would probably be better to switch to insertion sort for small arrays for
  536. // better performance
  537. if (arr_to_sort.size() <= 1)
  538. return;
  539. MarkedValueList left(vm.heap());
  540. MarkedValueList right(vm.heap());
  541. left.ensure_capacity(arr_to_sort.size() / 2);
  542. right.ensure_capacity(arr_to_sort.size() / 2 + (arr_to_sort.size() & 1));
  543. for (size_t i = 0; i < arr_to_sort.size(); ++i) {
  544. if (i < arr_to_sort.size() / 2) {
  545. left.append(arr_to_sort[i]);
  546. } else {
  547. right.append(arr_to_sort[i]);
  548. }
  549. }
  550. array_merge_sort(vm, global_object, compare_func, left);
  551. if (vm.exception())
  552. return;
  553. array_merge_sort(vm, global_object, compare_func, right);
  554. if (vm.exception())
  555. return;
  556. arr_to_sort.clear();
  557. size_t left_index = 0, right_index = 0;
  558. while (left_index < left.size() && right_index < right.size()) {
  559. auto x = left[left_index];
  560. auto y = right[right_index];
  561. double comparison_result;
  562. if (x.is_undefined() && y.is_undefined()) {
  563. comparison_result = 0;
  564. } else if (x.is_undefined()) {
  565. comparison_result = 1;
  566. } else if (y.is_undefined()) {
  567. comparison_result = -1;
  568. } else if (compare_func) {
  569. auto call_result = vm.call(*compare_func, js_undefined(), left[left_index], right[right_index]);
  570. if (vm.exception())
  571. return;
  572. if (call_result.is_nan()) {
  573. comparison_result = 0;
  574. } else {
  575. comparison_result = call_result.to_double(global_object);
  576. if (vm.exception())
  577. return;
  578. }
  579. } else {
  580. // FIXME: It would probably be much better to be smarter about this and implement
  581. // the Abstract Relational Comparison in line once iterating over code points, rather
  582. // than calling it twice after creating two primitive strings.
  583. auto x_string = x.to_primitive_string(global_object);
  584. if (vm.exception())
  585. return;
  586. auto y_string = y.to_primitive_string(global_object);
  587. if (vm.exception())
  588. return;
  589. auto x_string_value = Value(x_string);
  590. auto y_string_value = Value(y_string);
  591. // Because they are called with primitive strings, these abstract_relation calls
  592. // should never result in a VM exception.
  593. auto x_lt_y_relation = abstract_relation(global_object, true, x_string_value, y_string_value);
  594. ASSERT(x_lt_y_relation != TriState::Unknown);
  595. auto y_lt_x_relation = abstract_relation(global_object, true, y_string_value, x_string_value);
  596. ASSERT(y_lt_x_relation != TriState::Unknown);
  597. if (x_lt_y_relation == TriState::True) {
  598. comparison_result = -1;
  599. } else if (y_lt_x_relation == TriState::True) {
  600. comparison_result = 1;
  601. } else {
  602. comparison_result = 0;
  603. }
  604. }
  605. if (comparison_result <= 0) {
  606. arr_to_sort.append(left[left_index]);
  607. left_index++;
  608. } else {
  609. arr_to_sort.append(right[right_index]);
  610. right_index++;
  611. }
  612. }
  613. while (left_index < left.size()) {
  614. arr_to_sort.append(left[left_index]);
  615. left_index++;
  616. }
  617. while (right_index < right.size()) {
  618. arr_to_sort.append(right[right_index]);
  619. right_index++;
  620. }
  621. }
  622. JS_DEFINE_NATIVE_FUNCTION(ArrayPrototype::sort)
  623. {
  624. auto* array = vm.this_value(global_object).to_object(global_object);
  625. if (vm.exception())
  626. return {};
  627. auto callback = vm.argument(0);
  628. if (!callback.is_undefined() && !callback.is_function()) {
  629. vm.throw_exception<TypeError>(global_object, ErrorType::NotAFunction, callback.to_string_without_side_effects());
  630. return {};
  631. }
  632. auto original_length = get_length(vm, *array);
  633. if (vm.exception())
  634. return {};
  635. MarkedValueList values_to_sort(vm.heap());
  636. for (size_t i = 0; i < original_length; ++i) {
  637. auto element_val = array->get(i);
  638. if (vm.exception())
  639. return {};
  640. if (!element_val.is_empty())
  641. values_to_sort.append(element_val);
  642. }
  643. // Perform sorting by merge sort. This isn't as efficient compared to quick sort, but
  644. // quicksort can't be used in all cases because the spec requires Array.prototype.sort()
  645. // to be stable. FIXME: when initially scanning through the array, maintain a flag
  646. // for if an unstable sort would be indistinguishable from a stable sort (such as just
  647. // just strings or numbers), and in that case use quick sort instead for better performance.
  648. array_merge_sort(vm, global_object, callback.is_undefined() ? nullptr : &callback.as_function(), values_to_sort);
  649. if (vm.exception())
  650. return {};
  651. for (size_t i = 0; i < values_to_sort.size(); ++i) {
  652. array->put(i, values_to_sort[i]);
  653. if (vm.exception())
  654. return {};
  655. }
  656. // The empty parts of the array are always sorted to the end, regardless of the
  657. // compare function. FIXME: For performance, a similar process could be used
  658. // for undefined, which are sorted to right before the empty values.
  659. for (size_t i = values_to_sort.size(); i < original_length; ++i) {
  660. array->delete_property(i);
  661. if (vm.exception())
  662. return {};
  663. }
  664. return array;
  665. }
  666. JS_DEFINE_NATIVE_FUNCTION(ArrayPrototype::last_index_of)
  667. {
  668. auto* this_object = vm.this_value(global_object).to_object(global_object);
  669. if (!this_object)
  670. return {};
  671. i32 length = get_length(vm, *this_object);
  672. if (vm.exception())
  673. return {};
  674. if (length == 0)
  675. return Value(-1);
  676. i32 from_index = length - 1;
  677. if (vm.argument_count() >= 2) {
  678. from_index = vm.argument(1).to_i32(global_object);
  679. if (vm.exception())
  680. return {};
  681. if (from_index >= 0)
  682. from_index = min(from_index, length - 1);
  683. else
  684. from_index = length + from_index;
  685. }
  686. auto search_element = vm.argument(0);
  687. for (i32 i = from_index; i >= 0; --i) {
  688. auto element = this_object->get(i);
  689. if (vm.exception())
  690. return {};
  691. if (strict_eq(element, search_element))
  692. return Value(i);
  693. }
  694. return Value(-1);
  695. }
  696. JS_DEFINE_NATIVE_FUNCTION(ArrayPrototype::includes)
  697. {
  698. auto* this_object = vm.this_value(global_object).to_object(global_object);
  699. if (!this_object)
  700. return {};
  701. i32 length = get_length(vm, *this_object);
  702. if (vm.exception())
  703. return {};
  704. if (length == 0)
  705. return Value(false);
  706. i32 from_index = 0;
  707. if (vm.argument_count() >= 2) {
  708. from_index = vm.argument(1).to_i32(global_object);
  709. if (vm.exception())
  710. return {};
  711. if (from_index >= length)
  712. return Value(false);
  713. if (from_index < 0)
  714. from_index = max(length + from_index, 0);
  715. }
  716. auto value_to_find = vm.argument(0);
  717. for (i32 i = from_index; i < length; ++i) {
  718. auto element = this_object->get(i).value_or(js_undefined());
  719. if (vm.exception())
  720. return {};
  721. if (same_value_zero(element, value_to_find))
  722. return Value(true);
  723. }
  724. return Value(false);
  725. }
  726. JS_DEFINE_NATIVE_FUNCTION(ArrayPrototype::find)
  727. {
  728. auto result = js_undefined();
  729. for_each_item(
  730. vm, global_object, "find", [&](auto, auto value, auto callback_result) {
  731. if (callback_result.to_boolean()) {
  732. result = value;
  733. return IterationDecision::Break;
  734. }
  735. return IterationDecision::Continue;
  736. },
  737. false);
  738. return result;
  739. }
  740. JS_DEFINE_NATIVE_FUNCTION(ArrayPrototype::find_index)
  741. {
  742. auto result_index = -1;
  743. for_each_item(
  744. vm, global_object, "findIndex", [&](auto index, auto, auto callback_result) {
  745. if (callback_result.to_boolean()) {
  746. result_index = index;
  747. return IterationDecision::Break;
  748. }
  749. return IterationDecision::Continue;
  750. },
  751. false);
  752. return Value(result_index);
  753. }
  754. JS_DEFINE_NATIVE_FUNCTION(ArrayPrototype::some)
  755. {
  756. auto result = false;
  757. for_each_item(vm, global_object, "some", [&](auto, auto, auto callback_result) {
  758. if (callback_result.to_boolean()) {
  759. result = true;
  760. return IterationDecision::Break;
  761. }
  762. return IterationDecision::Continue;
  763. });
  764. return Value(result);
  765. }
  766. JS_DEFINE_NATIVE_FUNCTION(ArrayPrototype::every)
  767. {
  768. auto result = true;
  769. for_each_item(vm, global_object, "every", [&](auto, auto, auto callback_result) {
  770. if (!callback_result.to_boolean()) {
  771. result = false;
  772. return IterationDecision::Break;
  773. }
  774. return IterationDecision::Continue;
  775. });
  776. return Value(result);
  777. }
  778. JS_DEFINE_NATIVE_FUNCTION(ArrayPrototype::splice)
  779. {
  780. auto* this_object = vm.this_value(global_object).to_object(global_object);
  781. if (!this_object)
  782. return {};
  783. auto initial_length = get_length(vm, *this_object);
  784. if (vm.exception())
  785. return {};
  786. auto relative_start = vm.argument(0).to_i32(global_object);
  787. if (vm.exception())
  788. return {};
  789. size_t actual_start;
  790. if (relative_start < 0)
  791. actual_start = max((ssize_t)initial_length + relative_start, (ssize_t)0);
  792. else
  793. actual_start = min((size_t)relative_start, initial_length);
  794. size_t insert_count = 0;
  795. size_t actual_delete_count = 0;
  796. if (vm.argument_count() == 1) {
  797. actual_delete_count = initial_length - actual_start;
  798. } else if (vm.argument_count() >= 2) {
  799. insert_count = vm.argument_count() - 2;
  800. i32 delete_count = vm.argument(1).to_i32(global_object);
  801. if (vm.exception())
  802. return {};
  803. actual_delete_count = min((size_t)max(delete_count, 0), initial_length - actual_start);
  804. }
  805. size_t new_length = initial_length + insert_count - actual_delete_count;
  806. if (new_length > MAX_ARRAY_LIKE_INDEX) {
  807. vm.throw_exception<TypeError>(global_object, ErrorType::ArrayMaxSize);
  808. return {};
  809. }
  810. auto removed_elements = Array::create(global_object);
  811. for (size_t i = 0; i < actual_delete_count; ++i) {
  812. auto value = this_object->get(actual_start + i);
  813. if (vm.exception())
  814. return {};
  815. removed_elements->indexed_properties().append(value);
  816. }
  817. if (insert_count < actual_delete_count) {
  818. for (size_t i = actual_start; i < initial_length - actual_delete_count; ++i) {
  819. auto from = this_object->get(i + actual_delete_count);
  820. if (vm.exception())
  821. return {};
  822. auto to = i + insert_count;
  823. if (!from.is_empty()) {
  824. this_object->put(to, from);
  825. } else {
  826. this_object->delete_property(to);
  827. }
  828. if (vm.exception())
  829. return {};
  830. }
  831. for (size_t i = initial_length; i > new_length; --i) {
  832. this_object->delete_property(i - 1);
  833. if (vm.exception())
  834. return {};
  835. }
  836. } else if (insert_count > actual_delete_count) {
  837. for (size_t i = initial_length - actual_delete_count; i > actual_start; --i) {
  838. auto from = this_object->get(i + actual_delete_count - 1);
  839. if (vm.exception())
  840. return {};
  841. auto to = i + insert_count - 1;
  842. if (!from.is_empty()) {
  843. this_object->put(to, from);
  844. } else {
  845. this_object->delete_property(to);
  846. }
  847. if (vm.exception())
  848. return {};
  849. }
  850. }
  851. for (size_t i = 0; i < insert_count; ++i) {
  852. this_object->put(actual_start + i, vm.argument(i + 2));
  853. if (vm.exception())
  854. return {};
  855. }
  856. this_object->put(vm.names.length, Value((i32)new_length));
  857. if (vm.exception())
  858. return {};
  859. return removed_elements;
  860. }
  861. JS_DEFINE_NATIVE_FUNCTION(ArrayPrototype::fill)
  862. {
  863. auto* this_object = vm.this_value(global_object).to_object(global_object);
  864. if (!this_object)
  865. return {};
  866. ssize_t length = get_length(vm, *this_object);
  867. if (vm.exception())
  868. return {};
  869. ssize_t relative_start = 0;
  870. ssize_t relative_end = length;
  871. if (vm.argument_count() >= 2) {
  872. relative_start = vm.argument(1).to_i32(global_object);
  873. if (vm.exception())
  874. return {};
  875. }
  876. if (vm.argument_count() >= 3) {
  877. relative_end = vm.argument(2).to_i32(global_object);
  878. if (vm.exception())
  879. return {};
  880. }
  881. size_t from, to;
  882. if (relative_start < 0)
  883. from = max(length + relative_start, 0L);
  884. else
  885. from = min(relative_start, length);
  886. if (relative_end < 0)
  887. to = max(length + relative_end, 0L);
  888. else
  889. to = min(relative_end, length);
  890. for (size_t i = from; i < to; i++) {
  891. this_object->put(i, vm.argument(0));
  892. if (vm.exception())
  893. return {};
  894. }
  895. return this_object;
  896. }
  897. JS_DEFINE_NATIVE_FUNCTION(ArrayPrototype::values)
  898. {
  899. auto* this_object = vm.this_value(global_object).to_object(global_object);
  900. if (!this_object)
  901. return {};
  902. return ArrayIterator::create(global_object, this_object, Object::PropertyKind::Value);
  903. }
  904. }