Iterator.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433
  1. /*
  2. * Copyright (c) 2020, Matthew Olsson <mattco@serenityos.org>
  3. * Copyright (c) 2022-2023, Linus Groh <linusg@serenityos.org>
  4. * Copyright (c) 2023-2024, Tim Flynn <trflynn89@ladybird.org>
  5. *
  6. * SPDX-License-Identifier: BSD-2-Clause
  7. */
  8. #include <LibJS/Runtime/AbstractOperations.h>
  9. #include <LibJS/Runtime/AsyncFromSyncIteratorPrototype.h>
  10. #include <LibJS/Runtime/Error.h>
  11. #include <LibJS/Runtime/FunctionObject.h>
  12. #include <LibJS/Runtime/Iterator.h>
  13. #include <LibJS/Runtime/VM.h>
  14. #include <LibJS/Runtime/ValueInlines.h>
  15. namespace JS {
  16. GC_DEFINE_ALLOCATOR(Iterator);
  17. GC_DEFINE_ALLOCATOR(IteratorRecord);
  18. GC::Ref<Iterator> Iterator::create(Realm& realm, Object& prototype, GC::Ref<IteratorRecord> iterated)
  19. {
  20. return realm.create<Iterator>(prototype, move(iterated));
  21. }
  22. Iterator::Iterator(Object& prototype, GC::Ref<IteratorRecord> iterated)
  23. : Object(ConstructWithPrototypeTag::Tag, prototype)
  24. , m_iterated(move(iterated))
  25. {
  26. }
  27. Iterator::Iterator(Object& prototype)
  28. : Iterator(prototype, prototype.shape().realm().create<IteratorRecord>(prototype.shape().realm(), nullptr, js_undefined(), false))
  29. {
  30. }
  31. // 7.4.2 GetIteratorDirect ( obj ), https://tc39.es/ecma262/#sec-getiteratordirect
  32. ThrowCompletionOr<GC::Ref<IteratorRecord>> get_iterator_direct(VM& vm, Object& object)
  33. {
  34. // 1. Let nextMethod be ? Get(obj, "next").
  35. auto next_method = TRY(object.get(vm.names.next));
  36. // 2. Let iteratorRecord be Record { [[Iterator]]: obj, [[NextMethod]]: nextMethod, [[Done]]: false }.
  37. // 3. Return iteratorRecord.
  38. auto& realm = *vm.current_realm();
  39. return realm.create<IteratorRecord>(realm, object, next_method, false);
  40. }
  41. // 7.4.3 GetIteratorFromMethod ( obj, method ), https://tc39.es/ecma262/#sec-getiteratorfrommethod
  42. ThrowCompletionOr<GC::Ref<IteratorRecord>> get_iterator_from_method(VM& vm, Value object, GC::Ref<FunctionObject> method)
  43. {
  44. // 1. Let iterator be ? Call(method, obj).
  45. auto iterator = TRY(call(vm, *method, object));
  46. // 2. If iterator is not an Object, throw a TypeError exception.
  47. if (!iterator.is_object())
  48. return vm.throw_completion<TypeError>(ErrorType::NotIterable, object.to_string_without_side_effects());
  49. // 3. Let nextMethod be ? Get(iterator, "next").
  50. auto next_method = TRY(iterator.get(vm, vm.names.next));
  51. // 4. Let iteratorRecord be the Iterator Record { [[Iterator]]: iterator, [[NextMethod]]: nextMethod, [[Done]]: false }.
  52. auto& realm = *vm.current_realm();
  53. auto iterator_record = realm.create<IteratorRecord>(realm, iterator.as_object(), next_method, false);
  54. // 5. Return iteratorRecord.
  55. return iterator_record;
  56. }
  57. // 7.4.4 GetIterator ( obj, kind ), https://tc39.es/ecma262/#sec-getiterator
  58. ThrowCompletionOr<GC::Ref<IteratorRecord>> get_iterator(VM& vm, Value object, IteratorHint kind)
  59. {
  60. GC::Ptr<FunctionObject> method;
  61. // 1. If kind is async, then
  62. if (kind == IteratorHint::Async) {
  63. // a. Let method be ? GetMethod(obj, @@asyncIterator).
  64. method = TRY(object.get_method(vm, vm.well_known_symbol_async_iterator()));
  65. // b. If method is undefined, then
  66. if (!method) {
  67. // i. Let syncMethod be ? GetMethod(obj, @@iterator).
  68. auto sync_method = TRY(object.get_method(vm, vm.well_known_symbol_iterator()));
  69. // ii. If syncMethod is undefined, throw a TypeError exception.
  70. if (!sync_method)
  71. return vm.throw_completion<TypeError>(ErrorType::NotIterable, object.to_string_without_side_effects());
  72. // iii. Let syncIteratorRecord be ? GetIteratorFromMethod(obj, syncMethod).
  73. auto sync_iterator_record = TRY(get_iterator_from_method(vm, object, *sync_method));
  74. // iv. Return CreateAsyncFromSyncIterator(syncIteratorRecord).
  75. return create_async_from_sync_iterator(vm, sync_iterator_record);
  76. }
  77. }
  78. // 2. Else,
  79. else {
  80. // a. Let method be ? GetMethod(obj, @@iterator).
  81. method = TRY(object.get_method(vm, vm.well_known_symbol_iterator()));
  82. }
  83. // 3. If method is undefined, throw a TypeError exception.
  84. if (!method)
  85. return vm.throw_completion<TypeError>(ErrorType::NotIterable, object.to_string_without_side_effects());
  86. // 4. Return ? GetIteratorFromMethod(obj, method).
  87. return TRY(get_iterator_from_method(vm, object, *method));
  88. }
  89. // 7.4.5 GetIteratorFlattenable ( obj, primitiveHandling ), https://tc39.es/ecma262/#sec-getiteratorflattenable
  90. ThrowCompletionOr<GC::Ref<IteratorRecord>> get_iterator_flattenable(VM& vm, Value object, PrimitiveHandling primitive_handling)
  91. {
  92. // 1. If obj is not an Object, then
  93. if (!object.is_object()) {
  94. // a. If primitiveHandling is reject-primitives, throw a TypeError exception.
  95. if (primitive_handling == PrimitiveHandling::RejectPrimitives)
  96. return vm.throw_completion<TypeError>(ErrorType::NotAnObject, object.to_string_without_side_effects());
  97. // b. Assert: primitiveHandling is iterate-string-primitives.
  98. ASSERT(primitive_handling == PrimitiveHandling::IterateStringPrimitives);
  99. // c. If obj is not a String, throw a TypeError exception.
  100. if (!object.is_string())
  101. return vm.throw_completion<TypeError>(ErrorType::NotAString, object.to_string_without_side_effects());
  102. }
  103. // 2. Let method be ? GetMethod(obj, %Symbol.iterator%).
  104. auto method = TRY(object.get_method(vm, vm.well_known_symbol_iterator()));
  105. Value iterator;
  106. // 3. If method is undefined, then
  107. if (!method) {
  108. // a. Let iterator be obj.
  109. iterator = object;
  110. }
  111. // 4. Else,
  112. else {
  113. // a. Let iterator be ? Call(method, obj).
  114. iterator = TRY(call(vm, method, object));
  115. }
  116. // 5. If iterator is not an Object, throw a TypeError exception.
  117. if (!iterator.is_object())
  118. return vm.throw_completion<TypeError>(ErrorType::NotAnObject, iterator.to_string_without_side_effects());
  119. // 6. Return ? GetIteratorDirect(iterator).
  120. return TRY(get_iterator_direct(vm, iterator.as_object()));
  121. }
  122. // 7.4.6 IteratorNext ( iteratorRecord [ , value ] ), https://tc39.es/ecma262/#sec-iteratornext
  123. ThrowCompletionOr<GC::Ref<Object>> iterator_next(VM& vm, IteratorRecord& iterator_record, Optional<Value> value)
  124. {
  125. auto result = [&]() {
  126. // 1. If value is not present, then
  127. if (!value.has_value()) {
  128. // a. Let result be Completion(Call(iteratorRecord.[[NextMethod]], iteratorRecord.[[Iterator]])).
  129. return call(vm, iterator_record.next_method, iterator_record.iterator);
  130. }
  131. // 2. Else,
  132. else {
  133. // a. Let result be Completion(Call(iteratorRecord.[[NextMethod]], iteratorRecord.[[Iterator]], « value »)).
  134. return call(vm, iterator_record.next_method, iterator_record.iterator, *value);
  135. }
  136. }();
  137. // 3. If result is a throw completion, then
  138. if (result.is_throw_completion()) {
  139. // a. Set iteratorRecord.[[Done]] to true.
  140. iterator_record.done = true;
  141. // b. Return ? result.
  142. return result.release_error();
  143. }
  144. // 4. Set result to ! result.
  145. auto result_value = result.release_value();
  146. // 5. If result is not an Object, then
  147. if (!result_value.is_object()) {
  148. // a. Set iteratorRecord.[[Done]] to true.
  149. iterator_record.done = true;
  150. // b. Throw a TypeError exception.
  151. return vm.throw_completion<TypeError>(ErrorType::IterableNextBadReturn);
  152. }
  153. // 6. Return result.
  154. return result_value.as_object();
  155. }
  156. // 7.4.7 IteratorComplete ( iteratorResult ), https://tc39.es/ecma262/#sec-iteratorcomplete
  157. ThrowCompletionOr<bool> iterator_complete(VM& vm, Object& iterator_result)
  158. {
  159. // 1. Return ToBoolean(? Get(iterResult, "done")).
  160. return TRY(iterator_result.get(vm.names.done)).to_boolean();
  161. }
  162. // 7.4.8 IteratorValue ( iteratorResult ), https://tc39.es/ecma262/#sec-iteratorvalue
  163. ThrowCompletionOr<Value> iterator_value(VM& vm, Object& iterator_result)
  164. {
  165. // 1. Return ? Get(iterResult, "value").
  166. return TRY(iterator_result.get(vm.names.value));
  167. }
  168. // 7.4.9 IteratorStep ( iteratorRecord ), https://tc39.es/ecma262/#sec-iteratorstep
  169. ThrowCompletionOr<GC::Ptr<Object>> iterator_step(VM& vm, IteratorRecord& iterator_record)
  170. {
  171. // 1. Let result be ? IteratorNext(iteratorRecord).
  172. auto result = TRY(iterator_next(vm, iterator_record));
  173. // 2. Let done be Completion(IteratorComplete(result)).
  174. auto done = iterator_complete(vm, result);
  175. // 3. If done is a throw completion, then
  176. if (done.is_throw_completion()) {
  177. // a. Set iteratorRecord.[[Done]] to true.
  178. iterator_record.done = true;
  179. // b. Return ? done.
  180. return done.release_error();
  181. }
  182. // 4. Set done to ! done.
  183. auto done_value = done.release_value();
  184. // 5. If done is true, then
  185. if (done_value) {
  186. // a. Set iteratorRecord.[[Done]] to true.
  187. iterator_record.done = true;
  188. // b. Return DONE.
  189. return nullptr;
  190. }
  191. // 6. Return result.
  192. return result;
  193. }
  194. // 7.4.10 IteratorStepValue ( iteratorRecord ), https://tc39.es/ecma262/#sec-iteratorstepvalue
  195. ThrowCompletionOr<Optional<Value>> iterator_step_value(VM& vm, IteratorRecord& iterator_record)
  196. {
  197. // 1. Let result be ? IteratorStep(iteratorRecord).
  198. auto result = TRY(iterator_step(vm, iterator_record));
  199. // 2. If result is done, then
  200. if (!result) {
  201. // a. Return DONE.
  202. return OptionalNone {};
  203. }
  204. // 3. Let value be Completion(IteratorValue(result)).
  205. auto value = iterator_value(vm, *result);
  206. // 4. If value is a throw completion, then
  207. if (value.is_throw_completion()) {
  208. // a. Set iteratorRecord.[[Done]] to true.
  209. iterator_record.done = true;
  210. }
  211. // 5. Return ? value.
  212. return TRY(value);
  213. }
  214. // 7.4.11 IteratorClose ( iteratorRecord, completion , https://tc39.es/ecma262/#sec-iteratorclose
  215. // 7.4.13 AsyncIteratorClose ( iteratorRecord, completion ), https://tc39.es/ecma262/#sec-asynciteratorclose
  216. // NOTE: These only differ in that async awaits the inner value after the call.
  217. static Completion iterator_close_impl(VM& vm, IteratorRecord const& iterator_record, Completion completion, IteratorHint iterator_hint)
  218. {
  219. // 1. Assert: Type(iteratorRecord.[[Iterator]]) is Object.
  220. // 2. Let iterator be iteratorRecord.[[Iterator]].
  221. auto iterator = iterator_record.iterator;
  222. // 3. Let innerResult be Completion(GetMethod(iterator, "return")).
  223. auto inner_result = ThrowCompletionOr<Value> { js_undefined() };
  224. auto get_method_result = Value(iterator).get_method(vm, vm.names.return_);
  225. if (get_method_result.is_error())
  226. inner_result = get_method_result.release_error();
  227. // 4. If innerResult.[[Type]] is normal, then
  228. if (!inner_result.is_error()) {
  229. // a. Let return be innerResult.[[Value]].
  230. auto return_method = get_method_result.value();
  231. // b. If return is undefined, return ? completion.
  232. if (!return_method)
  233. return completion;
  234. // c. Set innerResult to Completion(Call(return, iterator)).
  235. inner_result = call(vm, return_method, iterator);
  236. // Note: If this is AsyncIteratorClose perform one extra step.
  237. if (iterator_hint == IteratorHint::Async && !inner_result.is_error()) {
  238. // d. If innerResult.[[Type]] is normal, set innerResult to Completion(Await(innerResult.[[Value]])).
  239. inner_result = await(vm, inner_result.value());
  240. }
  241. }
  242. // 5. If completion.[[Type]] is throw, return ? completion.
  243. if (completion.is_error())
  244. return completion;
  245. // 6. If innerResult.[[Type]] is throw, return ? innerResult.
  246. if (inner_result.is_throw_completion())
  247. return inner_result;
  248. // 7. If Type(innerResult.[[Value]]) is not Object, throw a TypeError exception.
  249. if (!inner_result.value().is_object())
  250. return vm.throw_completion<TypeError>(ErrorType::IterableReturnBadReturn);
  251. // 8. Return ? completion.
  252. return completion;
  253. }
  254. // 7.4.11 IteratorClose ( iteratorRecord, completion , https://tc39.es/ecma262/#sec-iteratorclose
  255. Completion iterator_close(VM& vm, IteratorRecord const& iterator_record, Completion completion)
  256. {
  257. return iterator_close_impl(vm, iterator_record, move(completion), IteratorHint::Sync);
  258. }
  259. // 7.4.13 AsyncIteratorClose ( iteratorRecord, completion ), https://tc39.es/ecma262/#sec-asynciteratorclose
  260. Completion async_iterator_close(VM& vm, IteratorRecord const& iterator_record, Completion completion)
  261. {
  262. return iterator_close_impl(vm, iterator_record, move(completion), IteratorHint::Async);
  263. }
  264. // 7.4.14 CreateIteratorResultObject ( value, done ), https://tc39.es/ecma262/#sec-createiterresultobject
  265. GC::Ref<Object> create_iterator_result_object(VM& vm, Value value, bool done)
  266. {
  267. auto& realm = *vm.current_realm();
  268. // 1. Let obj be OrdinaryObjectCreate(%Object.prototype%).
  269. auto object = Object::create_with_premade_shape(realm.intrinsics().iterator_result_object_shape());
  270. // 2. Perform ! CreateDataPropertyOrThrow(obj, "value", value).
  271. object->put_direct(realm.intrinsics().iterator_result_object_value_offset(), value);
  272. // 3. Perform ! CreateDataPropertyOrThrow(obj, "done", done).
  273. object->put_direct(realm.intrinsics().iterator_result_object_done_offset(), Value(done));
  274. // 4. Return obj.
  275. return object;
  276. }
  277. // 7.4.16 IteratorToList ( iteratorRecord ), https://tc39.es/ecma262/#sec-iteratortolist
  278. ThrowCompletionOr<GC::RootVector<Value>> iterator_to_list(VM& vm, IteratorRecord& iterator_record)
  279. {
  280. // 1. Let values be a new empty List.
  281. GC::RootVector<Value> values(vm.heap());
  282. // 2. Repeat,
  283. while (true) {
  284. // a. Let next be ? IteratorStepValue(iteratorRecord).
  285. auto next = TRY(iterator_step_value(vm, iterator_record));
  286. // b. If next is DONE, then
  287. if (!next.has_value()) {
  288. // i. Return values.
  289. return values;
  290. }
  291. // c. Append next to values.
  292. values.append(next.release_value());
  293. }
  294. }
  295. // 7.3.36 SetterThatIgnoresPrototypeProperties ( thisValue, home, p, v ), https://tc39.es/ecma262/#sec-SetterThatIgnoresPrototypeProperties
  296. ThrowCompletionOr<void> setter_that_ignores_prototype_properties(VM& vm, Value this_, Object const& home, PropertyKey const& property, Value value)
  297. {
  298. // 1. If this is not an Object, then
  299. if (!this_.is_object()) {
  300. // a. Throw a TypeError exception.
  301. return vm.throw_completion<TypeError>(ErrorType::NotAnObject, this_);
  302. }
  303. auto& this_object = this_.as_object();
  304. // 2. If this is home, then
  305. if (&this_object == &home) {
  306. // a. NOTE: Throwing here emulates assignment to a non-writable data property on the home object in strict mode code.
  307. // b. Throw a TypeError exception.
  308. return vm.throw_completion<TypeError>(ErrorType::DescWriteNonWritable, this_);
  309. }
  310. // 3. Let desc be ? this.[[GetOwnProperty]](p).
  311. auto desc = TRY(this_object.internal_get_own_property(property));
  312. // 4. If desc is undefined, then
  313. if (!desc.has_value()) {
  314. // a. Perform ? CreateDataPropertyOrThrow(this, p, v).
  315. TRY(this_object.create_data_property_or_throw(property, value));
  316. }
  317. // 5. Else,
  318. else {
  319. // a. Perform ? Set(this, p, v, true).
  320. TRY(this_object.set(property, value, Object::ShouldThrowExceptions::Yes));
  321. }
  322. // 6. Return unused.
  323. return {};
  324. }
  325. // Non-standard
  326. Completion get_iterator_values(VM& vm, Value iterable, IteratorValueCallback callback)
  327. {
  328. auto iterator_record = TRY(get_iterator(vm, iterable, IteratorHint::Sync));
  329. while (true) {
  330. auto next = TRY(iterator_step_value(vm, iterator_record));
  331. if (!next.has_value())
  332. return {};
  333. if (auto completion = callback(next.release_value()); completion.has_value())
  334. return iterator_close(vm, iterator_record, completion.release_value());
  335. }
  336. }
  337. void Iterator::visit_edges(Cell::Visitor& visitor)
  338. {
  339. Base::visit_edges(visitor);
  340. visitor.visit(m_iterated);
  341. }
  342. void IteratorRecord::visit_edges(Cell::Visitor& visitor)
  343. {
  344. Base::visit_edges(visitor);
  345. visitor.visit(iterator);
  346. visitor.visit(next_method);
  347. }
  348. }