1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-05-31 15:28:11 +00:00

LibJS: Integrate iterator protocol into language features

Finally use Symbol.iterator protocol in language features :) currently
only used in for-of loops and spread expressions, but will have more
uses later (Maps, Sets, Array.from, etc).
This commit is contained in:
Matthew Olsson 2020-07-13 08:27:20 -07:00 committed by Andreas Kling
parent 4970c448bf
commit a51b2393f2
8 changed files with 187 additions and 127 deletions

View file

@ -25,39 +25,51 @@
*/
#include <LibJS/Interpreter.h>
#include <LibJS/Runtime/Error.h>
#include <LibJS/Runtime/GlobalObject.h>
#include <LibJS/Runtime/IteratorOperations.h>
namespace JS {
Object* get_iterator(Object& obj, String hint, Value method)
Object* get_iterator(GlobalObject& global_object, Value value, String hint, Value method)
{
auto& interpreter = obj.interpreter();
auto& interpreter = global_object.interpreter();
ASSERT(hint == "sync" || hint == "async");
if (method.is_empty()) {
if (hint == "async")
TODO();
method = obj.get(obj.interpreter().well_known_symbol_iterator());
auto object = value.to_object(interpreter, global_object);
if (!object)
return {};
method = object->get(interpreter.well_known_symbol_iterator());
if (interpreter.exception())
return {};
}
if (!method.is_function())
TODO();
auto iterator = interpreter.call(method.as_function(), &obj);
if (!method.is_function()) {
interpreter.throw_exception<TypeError>(ErrorType::NotIterable, value.to_string_without_side_effects().characters());
return nullptr;
}
auto iterator = interpreter.call(method.as_function(), value);
if (interpreter.exception())
return {};
if (!iterator.is_object())
TODO();
if (!iterator.is_object()) {
interpreter.throw_exception<TypeError>(ErrorType::NotIterable, value.to_string_without_side_effects().characters());
return nullptr;
}
return &iterator.as_object();
}
Value iterator_next(Object& iterator, Value value)
Object* iterator_next(Object& iterator, Value value)
{
auto& interpreter = iterator.interpreter();
auto next_method = iterator.get("next");
if (interpreter.exception())
return {};
ASSERT(next_method.is_function());
if (!next_method.is_function()) {
interpreter.throw_exception<TypeError>(ErrorType::IterableNextNotAFunction);
return nullptr;
}
Value result;
if (value.is_empty()) {
@ -70,37 +82,12 @@ Value iterator_next(Object& iterator, Value value)
if (interpreter.exception())
return {};
if (!result.is_object())
TODO();
if (!result.is_object()) {
interpreter.throw_exception<TypeError>(ErrorType::IterableNextBadReturn);
return nullptr;
}
return result;
}
bool is_iterator_complete(Object& iterator_result)
{
auto done = iterator_result.get("done");
if (iterator_result.interpreter().exception())
return false;
return done.to_boolean();
}
Value iterator_value(Object& iterator_result)
{
return iterator_result.get("value");
}
Value iterator_step(Object& iterator)
{
auto& interpreter = iterator.interpreter();
auto result = iterator_next(iterator);
if (interpreter.exception())
return {};
auto done = is_iterator_complete(result.as_object());
if (interpreter.exception())
return {};
if (done)
return Value(false);
return result;
return &result.as_object();
}
void iterator_close(Object& iterator)
@ -117,4 +104,35 @@ Value create_iterator_result_object(Interpreter& interpreter, GlobalObject& glob
return object;
}
void get_iterator_values(GlobalObject& global_object, Value value, AK::Function<IterationDecision(Value&)> callback)
{
auto& interpreter = global_object.interpreter();
auto iterator = get_iterator(global_object, value);
if (!iterator)
return;
while (true) {
auto next_object = iterator_next(*iterator);
if (!next_object)
return;
auto done_property = next_object->get("done");
if (interpreter.exception())
return;
if (!done_property.is_empty() && done_property.to_boolean())
return;
auto next_value = next_object->get("value");
if (interpreter.exception())
return;
auto result = callback(next_value);
if (result == IterationDecision::Break)
return;
ASSERT(result == IterationDecision::Continue);
}
}
}