summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Libraries/LibJS/CMakeLists.txt4
-rw-r--r--Libraries/LibJS/Forward.h4
-rw-r--r--Libraries/LibJS/Runtime/ArrayIterator.cpp49
-rw-r--r--Libraries/LibJS/Runtime/ArrayIterator.h56
-rw-r--r--Libraries/LibJS/Runtime/ArrayIteratorPrototype.cpp92
-rw-r--r--Libraries/LibJS/Runtime/ArrayIteratorPrototype.h45
-rw-r--r--Libraries/LibJS/Runtime/ArrayPrototype.cpp17
-rw-r--r--Libraries/LibJS/Runtime/ArrayPrototype.h1
-rw-r--r--Libraries/LibJS/Runtime/GlobalObject.cpp9
-rw-r--r--Libraries/LibJS/Runtime/GlobalObject.h11
-rw-r--r--Libraries/LibJS/Runtime/IteratorOperations.cpp120
-rw-r--r--Libraries/LibJS/Runtime/IteratorOperations.h45
-rw-r--r--Libraries/LibJS/Runtime/IteratorPrototype.cpp55
-rw-r--r--Libraries/LibJS/Runtime/IteratorPrototype.h45
-rw-r--r--Libraries/LibJS/Runtime/Object.h1
-rw-r--r--Libraries/LibJS/Tests/builtins/Array/Array.prototype.values.js44
-rw-r--r--Libraries/LibJS/Tests/iterators/%IteratorPrototype%.js12
-rw-r--r--Libraries/LibJS/Tests/iterators/array-iterator.js48
18 files changed, 657 insertions, 1 deletions
diff --git a/Libraries/LibJS/CMakeLists.txt b/Libraries/LibJS/CMakeLists.txt
index af35aa7a84..4b7f4d2af0 100644
--- a/Libraries/LibJS/CMakeLists.txt
+++ b/Libraries/LibJS/CMakeLists.txt
@@ -10,6 +10,8 @@ set(SOURCES
Parser.cpp
Runtime/Array.cpp
Runtime/ArrayConstructor.cpp
+ Runtime/ArrayIterator.cpp
+ Runtime/ArrayIteratorPrototype.cpp
Runtime/ArrayPrototype.cpp
Runtime/BigInt.cpp
Runtime/BigIntConstructor.cpp
@@ -34,6 +36,8 @@ set(SOURCES
Runtime/FunctionPrototype.cpp
Runtime/GlobalObject.cpp
Runtime/IndexedProperties.cpp
+ Runtime/IteratorOperations.cpp
+ Runtime/IteratorPrototype.cpp
Runtime/JSONObject.cpp
Runtime/LexicalEnvironment.cpp
Runtime/MarkedValueList.cpp
diff --git a/Libraries/LibJS/Forward.h b/Libraries/LibJS/Forward.h
index 833206e001..1883c67417 100644
--- a/Libraries/LibJS/Forward.h
+++ b/Libraries/LibJS/Forward.h
@@ -67,6 +67,10 @@
__JS_ENUMERATE(TypeError, type_error, TypeErrorPrototype, TypeErrorConstructor) \
__JS_ENUMERATE(URIError, uri_error, URIErrorPrototype, URIErrorConstructor)
+#define JS_ENUMERATE_ITERATOR_PROTOTYPES \
+ __JS_ENUMERATE(Iterator, iterator) \
+ __JS_ENUMERATE(ArrayIterator, array_iterator)
+
#define JS_ENUMERATE_BUILTIN_TYPES \
JS_ENUMERATE_NATIVE_OBJECTS \
JS_ENUMERATE_ERROR_SUBCLASSES
diff --git a/Libraries/LibJS/Runtime/ArrayIterator.cpp b/Libraries/LibJS/Runtime/ArrayIterator.cpp
new file mode 100644
index 0000000000..6c01af00ab
--- /dev/null
+++ b/Libraries/LibJS/Runtime/ArrayIterator.cpp
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2020, Matthew Olsson <matthewcolsson@gmail.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibJS/Interpreter.h>
+#include <LibJS/Runtime/ArrayIterator.h>
+#include <LibJS/Runtime/GlobalObject.h>
+
+namespace JS {
+
+ArrayIterator* ArrayIterator::create(GlobalObject& global_object, Value array, Object::PropertyKind iteration_kind)
+{
+ return global_object.heap().allocate<ArrayIterator>(global_object, *global_object.array_iterator_prototype(), array, iteration_kind);
+}
+
+ArrayIterator::ArrayIterator(Object& prototype, Value array, Object::PropertyKind iteration_kind)
+ : Object(prototype)
+ , m_array(array)
+ , m_iteration_kind(iteration_kind)
+{
+}
+
+ArrayIterator::~ArrayIterator()
+{
+}
+
+}
diff --git a/Libraries/LibJS/Runtime/ArrayIterator.h b/Libraries/LibJS/Runtime/ArrayIterator.h
new file mode 100644
index 0000000000..408349f188
--- /dev/null
+++ b/Libraries/LibJS/Runtime/ArrayIterator.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2020, Matthew Olsson <matthewcolsson@gmail.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <LibJS/Runtime/Object.h>
+
+namespace JS {
+
+class ArrayIterator final : public Object {
+ JS_OBJECT(ArrayIterator, Object);
+
+public:
+ static ArrayIterator* create(GlobalObject&, Value array, Object::PropertyKind iteration_kind);
+
+ explicit ArrayIterator(Object& prototype, Value array, Object::PropertyKind iteration_kind);
+ virtual ~ArrayIterator() override;
+
+ Value array() const { return m_array; }
+ Object::PropertyKind iteration_kind() const { return m_iteration_kind; }
+ size_t index() const { return m_index; }
+
+private:
+ friend class ArrayIteratorPrototype;
+
+ virtual bool is_array_iterator_object() const override { return true; }
+
+ Value m_array;
+ Object::PropertyKind m_iteration_kind;
+ size_t m_index { 0 };
+};
+
+}
diff --git a/Libraries/LibJS/Runtime/ArrayIteratorPrototype.cpp b/Libraries/LibJS/Runtime/ArrayIteratorPrototype.cpp
new file mode 100644
index 0000000000..e5e381d34d
--- /dev/null
+++ b/Libraries/LibJS/Runtime/ArrayIteratorPrototype.cpp
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 2020, Matthew Olsson <matthewcolsson@gmail.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibJS/Runtime/Array.h>
+#include <LibJS/Runtime/ArrayIterator.h>
+#include <LibJS/Runtime/ArrayIteratorPrototype.h>
+#include <LibJS/Runtime/Error.h>
+#include <LibJS/Runtime/GlobalObject.h>
+#include <LibJS/Runtime/IteratorOperations.h>
+
+namespace JS {
+
+ArrayIteratorPrototype::ArrayIteratorPrototype(GlobalObject& global_object)
+ : Object(*global_object.iterator_prototype())
+{
+}
+
+void ArrayIteratorPrototype::initialize(Interpreter& interpreter, GlobalObject& global_object)
+{
+ Object::initialize(interpreter, global_object);
+ define_native_function("next", next, 0, Attribute::Writable | Attribute::Configurable);
+}
+
+ArrayIteratorPrototype::~ArrayIteratorPrototype()
+{
+}
+
+JS_DEFINE_NATIVE_FUNCTION(ArrayIteratorPrototype::next)
+{
+ auto this_value = interpreter.this_value(global_object);
+ if (!this_value.is_object() || !this_value.as_object().is_array_iterator_object())
+ return interpreter.throw_exception<TypeError>(ErrorType::NotAn, "Array Iterator");
+
+ auto& this_object = this_value.as_object();
+
+ auto& iterator = static_cast<ArrayIterator&>(this_object);
+ auto target_array = iterator.array();
+ if (target_array.is_undefined())
+ return create_iterator_result_object(interpreter, global_object, js_undefined(), true);
+ ASSERT(target_array.is_object());
+ auto& array = target_array.as_object();
+
+ auto index = iterator.index();
+ auto iteration_kind = iterator.iteration_kind();
+ // FIXME: Typed array check
+ auto length = array.indexed_properties().array_like_size();
+
+ if (index >= length) {
+ iterator.m_array = js_undefined();
+ return create_iterator_result_object(interpreter, global_object, js_undefined(), true);
+ }
+
+ iterator.m_index++;
+ if (iteration_kind == Object::PropertyKind::Key)
+ return create_iterator_result_object(interpreter, global_object, Value(static_cast<i32>(index)), false);
+
+ auto value = array.get(index);
+ if (interpreter.exception())
+ return {};
+ if (iteration_kind == Object::PropertyKind::Value)
+ return create_iterator_result_object(interpreter, global_object, value, false);
+
+ auto* entry_array = Array::create(global_object);
+ entry_array->define_property(0, Value(static_cast<i32>(index)));
+ entry_array->define_property(1, value);
+ return create_iterator_result_object(interpreter, global_object, entry_array, false);
+}
+
+}
diff --git a/Libraries/LibJS/Runtime/ArrayIteratorPrototype.h b/Libraries/LibJS/Runtime/ArrayIteratorPrototype.h
new file mode 100644
index 0000000000..a04c9aae08
--- /dev/null
+++ b/Libraries/LibJS/Runtime/ArrayIteratorPrototype.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2020, Matthew Olsson <matthewcolsson@gmail.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <LibJS/Runtime/Object.h>
+
+namespace JS {
+
+class ArrayIteratorPrototype final : public Object {
+ JS_OBJECT(ArrayIteratorPrototype, Object)
+
+public:
+ ArrayIteratorPrototype(GlobalObject&);
+ virtual void initialize(Interpreter&, GlobalObject&) override;
+ virtual ~ArrayIteratorPrototype() override;
+
+private:
+ JS_DECLARE_NATIVE_FUNCTION(next);
+};
+
+}
diff --git a/Libraries/LibJS/Runtime/ArrayPrototype.cpp b/Libraries/LibJS/Runtime/ArrayPrototype.cpp
index 7c376b56fa..2a3200690d 100644
--- a/Libraries/LibJS/Runtime/ArrayPrototype.cpp
+++ b/Libraries/LibJS/Runtime/ArrayPrototype.cpp
@@ -28,9 +28,9 @@
#include <AK/Function.h>
#include <AK/StringBuilder.h>
-#include <LibJS/Heap/Heap.h>
#include <LibJS/Interpreter.h>
#include <LibJS/Runtime/Array.h>
+#include <LibJS/Runtime/ArrayIterator.h>
#include <LibJS/Runtime/ArrayPrototype.h>
#include <LibJS/Runtime/Error.h>
#include <LibJS/Runtime/Function.h>
@@ -75,7 +75,13 @@ void ArrayPrototype::initialize(Interpreter& interpreter, GlobalObject& global_o
define_native_function("every", every, 1, attr);
define_native_function("splice", splice, 2, attr);
define_native_function("fill", fill, 1, attr);
+ define_native_function("values", values, 0, attr);
define_property("length", Value(0), Attribute::Configurable);
+
+ // Use define_property here instead of define_native_function so that
+ // Object.is(Array.prototype[Symbol.iterator], Array.prototype.values)
+ // evaluates to true
+ define_property(interpreter.get_well_known_symbol("iterator"), get("values"), attr);
}
ArrayPrototype::~ArrayPrototype()
@@ -850,4 +856,13 @@ JS_DEFINE_NATIVE_FUNCTION(ArrayPrototype::fill)
return this_object;
}
+JS_DEFINE_NATIVE_FUNCTION(ArrayPrototype::values)
+{
+ auto* this_object = interpreter.this_value(global_object).to_object(interpreter, global_object);
+ if (!this_object)
+ return {};
+
+ return ArrayIterator::create(global_object, this_object, Object::PropertyKind::Value);
+}
+
}
diff --git a/Libraries/LibJS/Runtime/ArrayPrototype.h b/Libraries/LibJS/Runtime/ArrayPrototype.h
index 989603dafa..a286725136 100644
--- a/Libraries/LibJS/Runtime/ArrayPrototype.h
+++ b/Libraries/LibJS/Runtime/ArrayPrototype.h
@@ -64,6 +64,7 @@ private:
JS_DECLARE_NATIVE_FUNCTION(every);
JS_DECLARE_NATIVE_FUNCTION(splice);
JS_DECLARE_NATIVE_FUNCTION(fill);
+ JS_DECLARE_NATIVE_FUNCTION(values);
};
}
diff --git a/Libraries/LibJS/Runtime/GlobalObject.cpp b/Libraries/LibJS/Runtime/GlobalObject.cpp
index 9dd2b4e9d7..6096eb1eb7 100644
--- a/Libraries/LibJS/Runtime/GlobalObject.cpp
+++ b/Libraries/LibJS/Runtime/GlobalObject.cpp
@@ -28,6 +28,7 @@
#include <AK/LogStream.h>
#include <LibJS/Interpreter.h>
#include <LibJS/Runtime/ArrayConstructor.h>
+#include <LibJS/Runtime/ArrayIteratorPrototype.h>
#include <LibJS/Runtime/ArrayPrototype.h>
#include <LibJS/Runtime/BigIntConstructor.h>
#include <LibJS/Runtime/BigIntPrototype.h>
@@ -41,6 +42,7 @@
#include <LibJS/Runtime/FunctionConstructor.h>
#include <LibJS/Runtime/FunctionPrototype.h>
#include <LibJS/Runtime/GlobalObject.h>
+#include <LibJS/Runtime/IteratorPrototype.h>
#include <LibJS/Runtime/JSONObject.h>
#include <LibJS/Runtime/MathObject.h>
#include <LibJS/Runtime/NativeFunction.h>
@@ -84,6 +86,13 @@ void GlobalObject::initialize()
JS_ENUMERATE_BUILTIN_TYPES
#undef __JS_ENUMERATE
+#define __JS_ENUMERATE(ClassName, snake_name) \
+ if (!m_##snake_name##_prototype) \
+ m_##snake_name##_prototype = heap().allocate<ClassName##Prototype>(*this, *this);
+ JS_ENUMERATE_ITERATOR_PROTOTYPES
+#undef __JS_ENUMERATE
+
+
u8 attr = Attribute::Writable | Attribute::Configurable;
define_native_function("gc", gc, 0, attr);
define_native_function("isNaN", is_nan, 1, attr);
diff --git a/Libraries/LibJS/Runtime/GlobalObject.h b/Libraries/LibJS/Runtime/GlobalObject.h
index 862522a0f1..36472a7f2f 100644
--- a/Libraries/LibJS/Runtime/GlobalObject.h
+++ b/Libraries/LibJS/Runtime/GlobalObject.h
@@ -49,6 +49,11 @@ public:
JS_ENUMERATE_BUILTIN_TYPES
#undef __JS_ENUMERATE
+#define __JS_ENUMERATE(ClassName, snake_name) \
+ Object* snake_name##_prototype() { return m_##snake_name##_prototype; }
+ JS_ENUMERATE_ITERATOR_PROTOTYPES
+#undef __JS_ENUMERATE
+
protected:
virtual void visit_children(Visitor&) override;
@@ -68,6 +73,12 @@ private:
Object* m_##snake_name##_prototype { nullptr };
JS_ENUMERATE_BUILTIN_TYPES
#undef __JS_ENUMERATE
+
+#define __JS_ENUMERATE(ClassName, snake_name) \
+ Object* m_##snake_name##_prototype { nullptr };
+ JS_ENUMERATE_ITERATOR_PROTOTYPES
+#undef __JS_ENUMERATE
+
};
template<typename ConstructorType>
diff --git a/Libraries/LibJS/Runtime/IteratorOperations.cpp b/Libraries/LibJS/Runtime/IteratorOperations.cpp
new file mode 100644
index 0000000000..3bee12548b
--- /dev/null
+++ b/Libraries/LibJS/Runtime/IteratorOperations.cpp
@@ -0,0 +1,120 @@
+/*
+ * Copyright (c) 2020, Matthew Olsson <matthewcolsson@gmail.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibJS/Interpreter.h>
+#include <LibJS/Runtime/IteratorOperations.h>
+
+namespace JS {
+
+Object* get_iterator(Object& obj, String hint, Value method)
+{
+ auto& interpreter = obj.interpreter();
+ ASSERT(hint == "sync" || hint == "async");
+ if (method.is_empty()) {
+ if (hint == "async")
+ TODO();
+ method = obj.get(obj.interpreter().get_well_known_symbol("iterator"));
+ if (interpreter.exception())
+ return {};
+ }
+ if (!method.is_function())
+ TODO();
+ auto iterator = interpreter.call(method.as_function(), &obj);
+ if (interpreter.exception())
+ return {};
+ if (!iterator.is_object())
+ TODO();
+ return &iterator.as_object();
+}
+
+Value 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());
+
+ Value result;
+ if (value.is_empty()) {
+ result = interpreter.call(next_method.as_function(), &iterator);
+ } else {
+ MarkedValueList arguments(iterator.heap());
+ arguments.append(value);
+ result = interpreter.call(next_method.as_function(), &iterator, move(arguments));
+ }
+
+ if (interpreter.exception())
+ return {};
+ if (!result.is_object())
+ TODO();
+
+ 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;
+}
+
+void iterator_close(Object& iterator)
+{
+ (void)iterator;
+ TODO();
+}
+
+Value create_iterator_result_object(Interpreter& interpreter, GlobalObject& global_object, Value value, bool done)
+{
+ auto* object = Object::create_empty(interpreter, global_object);
+ object->put("value", value);
+ object->put("done", Value(done));
+ return object;
+}
+
+}
diff --git a/Libraries/LibJS/Runtime/IteratorOperations.h b/Libraries/LibJS/Runtime/IteratorOperations.h
new file mode 100644
index 0000000000..9b9caae4e9
--- /dev/null
+++ b/Libraries/LibJS/Runtime/IteratorOperations.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2020, Matthew Olsson <matthewcolsson@gmail.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <LibJS/Runtime/Object.h>
+
+namespace JS {
+
+// Common iterator operations defined in ECMA262 7.4
+// https://tc39.es/ecma262/#sec-operations-on-iterator-objects
+
+Object* get_iterator(Object& obj, String hint = "sync", Value method = {});
+bool is_iterator_complete(Object& iterator_result);
+Value create_iterator_result_object(Interpreter&, GlobalObject&, Value value, bool done);
+
+Value iterator_next(Object& iterator, Value value = {});
+Value iterator_value(Object& iterator_result);
+Value iterator_step(Object& iterator);
+void iterator_close(Object& iterator);
+
+}
diff --git a/Libraries/LibJS/Runtime/IteratorPrototype.cpp b/Libraries/LibJS/Runtime/IteratorPrototype.cpp
new file mode 100644
index 0000000000..e1fe68e125
--- /dev/null
+++ b/Libraries/LibJS/Runtime/IteratorPrototype.cpp
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2020, Matthew Olsson <matthewcolsson@gmail.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <LibJS/Runtime/GlobalObject.h>
+#include <LibJS/Runtime/IteratorPrototype.h>
+
+namespace JS {
+
+IteratorPrototype::IteratorPrototype(GlobalObject& global_object)
+ : Object(*global_object.object_prototype())
+{
+}
+
+void IteratorPrototype::initialize(Interpreter& interpreter, GlobalObject& global_object)
+{
+ Object::initialize(interpreter, global_object);
+ define_native_function(interpreter.get_well_known_symbol("iterator"), symbol_iterator, 0, Attribute::Writable | Attribute::Enumerable);
+}
+
+IteratorPrototype::~IteratorPrototype()
+{
+}
+
+JS_DEFINE_NATIVE_FUNCTION(IteratorPrototype::symbol_iterator)
+{
+ auto* this_object = interpreter.this_value(global_object).to_object(interpreter, global_object);
+ if (!this_object)
+ return {};
+ return this_object;
+}
+
+}
diff --git a/Libraries/LibJS/Runtime/IteratorPrototype.h b/Libraries/LibJS/Runtime/IteratorPrototype.h
new file mode 100644
index 0000000000..1cfd954632
--- /dev/null
+++ b/Libraries/LibJS/Runtime/IteratorPrototype.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2020, Matthew Olsson <matthewcolsson@gmail.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#include <LibJS/Runtime/Object.h>
+
+namespace JS {
+
+class IteratorPrototype : public Object {
+ JS_OBJECT(IteratorPrototype, Object)
+
+public:
+ IteratorPrototype(GlobalObject&);
+ virtual void initialize(Interpreter&, GlobalObject&) override;
+ virtual ~IteratorPrototype() override;
+
+private:
+ JS_DECLARE_NATIVE_FUNCTION(symbol_iterator);
+};
+
+}
diff --git a/Libraries/LibJS/Runtime/Object.h b/Libraries/LibJS/Runtime/Object.h
index 507795ce1e..ff325b845b 100644
--- a/Libraries/LibJS/Runtime/Object.h
+++ b/Libraries/LibJS/Runtime/Object.h
@@ -123,6 +123,7 @@ public:
virtual bool is_number_object() const { return false; }
virtual bool is_symbol_object() const { return false; }
virtual bool is_bigint_object() const { return false; }
+ virtual bool is_array_iterator_object() const { return false; }
virtual const char* class_name() const override { return "Object"; }
virtual void visit_children(Cell::Visitor&) override;
diff --git a/Libraries/LibJS/Tests/builtins/Array/Array.prototype.values.js b/Libraries/LibJS/Tests/builtins/Array/Array.prototype.values.js
new file mode 100644
index 0000000000..c382fe837d
--- /dev/null
+++ b/Libraries/LibJS/Tests/builtins/Array/Array.prototype.values.js
@@ -0,0 +1,44 @@
+test("length", () => {
+ expect(Array.prototype.values.length).toBe(0);
+});
+
+test("basic functionality", () => {
+ const a = [1, 2, 3];
+ const it = a.values();
+ expect(it.next()).toEqual({ value: 1, done: false });
+ expect(it.next()).toEqual({ value: 2, done: false });
+ expect(it.next()).toEqual({ value: 3, done: false });
+ expect(it.next()).toEqual({ value: undefined, done: true });
+ expect(it.next()).toEqual({ value: undefined, done: true });
+ expect(it.next()).toEqual({ value: undefined, done: true });
+});
+
+test("works when applied to non-object", () => {
+ [true, false, 9, 2n, Symbol()].forEach(primitive => {
+ const it = [].values.call(primitive);
+ expect(it.next()).toEqual({ value: undefined, done: true });
+ expect(it.next()).toEqual({ value: undefined, done: true });
+ expect(it.next()).toEqual({ value: undefined, done: true });
+ });
+});
+
+test("item added to array before exhaustion is accessible", () => {
+ const a = [1, 2];
+ const it = a.values();
+ expect(it.next()).toEqual({ value: 1, done: false });
+ expect(it.next()).toEqual({ value: 2, done: false });
+ a.push(3);
+ expect(it.next()).toEqual({ value: 3, done: false });
+ expect(it.next()).toEqual({ value: undefined, done: true });
+ expect(it.next()).toEqual({ value: undefined, done: true });
+});
+
+test("item added to array after exhaustion is inaccesible", () => {
+ const a = [1, 2];
+ const it = a.values();
+ expect(it.next()).toEqual({ value: 1, done: false });
+ expect(it.next()).toEqual({ value: 2, done: false });
+ expect(it.next()).toEqual({ value: undefined, done: true });
+ a.push(3);
+ expect(it.next()).toEqual({ value: undefined, done: true });
+});
diff --git a/Libraries/LibJS/Tests/iterators/%IteratorPrototype%.js b/Libraries/LibJS/Tests/iterators/%IteratorPrototype%.js
new file mode 100644
index 0000000000..efdb32e51a
--- /dev/null
+++ b/Libraries/LibJS/Tests/iterators/%IteratorPrototype%.js
@@ -0,0 +1,12 @@
+const getIteratorPrototype = () =>
+ Object.getPrototypeOf(Object.getPrototypeOf([][Symbol.iterator]()));
+
+test("prototype of %IteratorPrototype% is %ObjectPrototype%", () => {
+ let itProto = getIteratorPrototype();
+ expect(Object.getPrototypeOf(itProto)).toBe(Object.getPrototypeOf({}));
+});
+
+test("@@iterator of %IteratorPrototype% is itself", () => {
+ let itProto = getIteratorPrototype();
+ expect(itProto[Symbol.iterator]()).toBe(itProto);
+});
diff --git a/Libraries/LibJS/Tests/iterators/array-iterator.js b/Libraries/LibJS/Tests/iterators/array-iterator.js
new file mode 100644
index 0000000000..117030ebdf
--- /dev/null
+++ b/Libraries/LibJS/Tests/iterators/array-iterator.js
@@ -0,0 +1,48 @@
+test("length", () => {
+ expect(Array.prototype[Symbol.iterator].length).toBe(0);
+});
+
+test("same function as Array.prototype.values", () => {
+ expect(Array.prototype[Symbol.iterator]).toBe(Array.prototype.values);
+});
+
+test("basic functionality", () => {
+ const a = [1, 2, 3];
+ const it = a[Symbol.iterator]();
+ expect(it.next()).toEqual({ value: 1, done: false });
+ expect(it.next()).toEqual({ value: 2, done: false });
+ expect(it.next()).toEqual({ value: 3, done: false });
+ expect(it.next()).toEqual({ value: undefined, done: true });
+ expect(it.next()).toEqual({ value: undefined, done: true });
+ expect(it.next()).toEqual({ value: undefined, done: true });
+});
+
+test("works when applied to non-object", () => {
+ [true, false, 9, 2n, Symbol()].forEach(primitive => {
+ const it = [][Symbol.iterator].call(primitive);
+ expect(it.next()).toEqual({ value: undefined, done: true });
+ expect(it.next()).toEqual({ value: undefined, done: true });
+ expect(it.next()).toEqual({ value: undefined, done: true });
+ });
+});
+
+test("item added to array before exhaustion is accessible", () => {
+ const a = [1, 2];
+ const it = a[Symbol.iterator]();
+ expect(it.next()).toEqual({ value: 1, done: false });
+ expect(it.next()).toEqual({ value: 2, done: false });
+ a.push(3);
+ expect(it.next()).toEqual({ value: 3, done: false });
+ expect(it.next()).toEqual({ value: undefined, done: true });
+ expect(it.next()).toEqual({ value: undefined, done: true });
+});
+
+test("item added to array after exhaustion is inaccesible", () => {
+ const a = [1, 2];
+ const it = a[Symbol.iterator]();
+ expect(it.next()).toEqual({ value: 1, done: false });
+ expect(it.next()).toEqual({ value: 2, done: false });
+ expect(it.next()).toEqual({ value: undefined, done: true });
+ a.push(3);
+ expect(it.next()).toEqual({ value: undefined, done: true });
+});