summaryrefslogtreecommitdiff
path: root/Userland/Libraries/LibJS/Runtime
diff options
context:
space:
mode:
Diffstat (limited to 'Userland/Libraries/LibJS/Runtime')
-rw-r--r--Userland/Libraries/LibJS/Runtime/Accessor.h82
-rw-r--r--Userland/Libraries/LibJS/Runtime/Array.cpp86
-rw-r--r--Userland/Libraries/LibJS/Runtime/Array.h51
-rw-r--r--Userland/Libraries/LibJS/Runtime/ArrayBuffer.cpp47
-rw-r--r--Userland/Libraries/LibJS/Runtime/ArrayBuffer.h51
-rw-r--r--Userland/Libraries/LibJS/Runtime/ArrayBufferConstructor.cpp85
-rw-r--r--Userland/Libraries/LibJS/Runtime/ArrayBufferConstructor.h50
-rw-r--r--Userland/Libraries/LibJS/Runtime/ArrayBufferPrototype.cpp87
-rw-r--r--Userland/Libraries/LibJS/Runtime/ArrayBufferPrototype.h46
-rw-r--r--Userland/Libraries/LibJS/Runtime/ArrayConstructor.cpp141
-rw-r--r--Userland/Libraries/LibJS/Runtime/ArrayConstructor.h52
-rw-r--r--Userland/Libraries/LibJS/Runtime/ArrayIterator.cpp54
-rw-r--r--Userland/Libraries/LibJS/Runtime/ArrayIterator.h56
-rw-r--r--Userland/Libraries/LibJS/Runtime/ArrayIteratorPrototype.cpp95
-rw-r--r--Userland/Libraries/LibJS/Runtime/ArrayIteratorPrototype.h45
-rw-r--r--Userland/Libraries/LibJS/Runtime/ArrayPrototype.cpp1037
-rw-r--r--Userland/Libraries/LibJS/Runtime/ArrayPrototype.h71
-rw-r--r--Userland/Libraries/LibJS/Runtime/BigInt.cpp48
-rw-r--r--Userland/Libraries/LibJS/Runtime/BigInt.h50
-rw-r--r--Userland/Libraries/LibJS/Runtime/BigIntConstructor.cpp92
-rw-r--r--Userland/Libraries/LibJS/Runtime/BigIntConstructor.h51
-rw-r--r--Userland/Libraries/LibJS/Runtime/BigIntObject.cpp54
-rw-r--r--Userland/Libraries/LibJS/Runtime/BigIntObject.h55
-rw-r--r--Userland/Libraries/LibJS/Runtime/BigIntPrototype.cpp89
-rw-r--r--Userland/Libraries/LibJS/Runtime/BigIntPrototype.h47
-rw-r--r--Userland/Libraries/LibJS/Runtime/BooleanConstructor.cpp62
-rw-r--r--Userland/Libraries/LibJS/Runtime/BooleanConstructor.h48
-rw-r--r--Userland/Libraries/LibJS/Runtime/BooleanObject.cpp47
-rw-r--r--Userland/Libraries/LibJS/Runtime/BooleanObject.h49
-rw-r--r--Userland/Libraries/LibJS/Runtime/BooleanPrototype.cpp77
-rw-r--r--Userland/Libraries/LibJS/Runtime/BooleanPrototype.h46
-rw-r--r--Userland/Libraries/LibJS/Runtime/BoundFunction.cpp79
-rw-r--r--Userland/Libraries/LibJS/Runtime/BoundFunction.h68
-rw-r--r--Userland/Libraries/LibJS/Runtime/Cell.cpp58
-rw-r--r--Userland/Libraries/LibJS/Runtime/Cell.h87
-rw-r--r--Userland/Libraries/LibJS/Runtime/CommonPropertyNames.h255
-rw-r--r--Userland/Libraries/LibJS/Runtime/ConsoleObject.cpp106
-rw-r--r--Userland/Libraries/LibJS/Runtime/ConsoleObject.h53
-rw-r--r--Userland/Libraries/LibJS/Runtime/Date.cpp124
-rw-r--r--Userland/Libraries/LibJS/Runtime/Date.h93
-rw-r--r--Userland/Libraries/LibJS/Runtime/DateConstructor.cpp252
-rw-r--r--Userland/Libraries/LibJS/Runtime/DateConstructor.h52
-rw-r--r--Userland/Libraries/LibJS/Runtime/DatePrototype.cpp299
-rw-r--r--Userland/Libraries/LibJS/Runtime/DatePrototype.h68
-rw-r--r--Userland/Libraries/LibJS/Runtime/Error.cpp62
-rw-r--r--Userland/Libraries/LibJS/Runtime/Error.h68
-rw-r--r--Userland/Libraries/LibJS/Runtime/ErrorConstructor.cpp98
-rw-r--r--Userland/Libraries/LibJS/Runtime/ErrorConstructor.h69
-rw-r--r--Userland/Libraries/LibJS/Runtime/ErrorPrototype.cpp140
-rw-r--r--Userland/Libraries/LibJS/Runtime/ErrorPrototype.h65
-rw-r--r--Userland/Libraries/LibJS/Runtime/ErrorTypes.cpp36
-rw-r--r--Userland/Libraries/LibJS/Runtime/ErrorTypes.h193
-rw-r--r--Userland/Libraries/LibJS/Runtime/Exception.cpp63
-rw-r--r--Userland/Libraries/LibJS/Runtime/Exception.h54
-rw-r--r--Userland/Libraries/LibJS/Runtime/Function.cpp99
-rw-r--r--Userland/Libraries/LibJS/Runtime/Function.h79
-rw-r--r--Userland/Libraries/LibJS/Runtime/FunctionConstructor.cpp105
-rw-r--r--Userland/Libraries/LibJS/Runtime/FunctionConstructor.h48
-rw-r--r--Userland/Libraries/LibJS/Runtime/FunctionPrototype.cpp189
-rw-r--r--Userland/Libraries/LibJS/Runtime/FunctionPrototype.h49
-rw-r--r--Userland/Libraries/LibJS/Runtime/GlobalObject.cpp310
-rw-r--r--Userland/Libraries/LibJS/Runtime/GlobalObject.h133
-rw-r--r--Userland/Libraries/LibJS/Runtime/IndexedProperties.cpp396
-rw-r--r--Userland/Libraries/LibJS/Runtime/IndexedProperties.h190
-rw-r--r--Userland/Libraries/LibJS/Runtime/IteratorOperations.cpp135
-rw-r--r--Userland/Libraries/LibJS/Runtime/IteratorOperations.h46
-rw-r--r--Userland/Libraries/LibJS/Runtime/IteratorPrototype.cpp56
-rw-r--r--Userland/Libraries/LibJS/Runtime/IteratorPrototype.h45
-rw-r--r--Userland/Libraries/LibJS/Runtime/JSONObject.cpp507
-rw-r--r--Userland/Libraries/LibJS/Runtime/JSONObject.h70
-rw-r--r--Userland/Libraries/LibJS/Runtime/LexicalEnvironment.cpp134
-rw-r--r--Userland/Libraries/LibJS/Runtime/LexicalEnvironment.h100
-rw-r--r--Userland/Libraries/LibJS/Runtime/MarkedValueList.cpp50
-rw-r--r--Userland/Libraries/LibJS/Runtime/MarkedValueList.h58
-rw-r--r--Userland/Libraries/LibJS/Runtime/MathObject.cpp504
-rw-r--r--Userland/Libraries/LibJS/Runtime/MathObject.h78
-rw-r--r--Userland/Libraries/LibJS/Runtime/NativeFunction.cpp81
-rw-r--r--Userland/Libraries/LibJS/Runtime/NativeFunction.h63
-rw-r--r--Userland/Libraries/LibJS/Runtime/NativeProperty.cpp56
-rw-r--r--Userland/Libraries/LibJS/Runtime/NativeProperty.h49
-rw-r--r--Userland/Libraries/LibJS/Runtime/NumberConstructor.cpp115
-rw-r--r--Userland/Libraries/LibJS/Runtime/NumberConstructor.h53
-rw-r--r--Userland/Libraries/LibJS/Runtime/NumberObject.cpp50
-rw-r--r--Userland/Libraries/LibJS/Runtime/NumberObject.h50
-rw-r--r--Userland/Libraries/LibJS/Runtime/NumberPrototype.cpp146
-rw-r--r--Userland/Libraries/LibJS/Runtime/NumberPrototype.h44
-rw-r--r--Userland/Libraries/LibJS/Runtime/Object.cpp927
-rw-r--r--Userland/Libraries/LibJS/Runtime/Object.h168
-rw-r--r--Userland/Libraries/LibJS/Runtime/ObjectConstructor.cpp242
-rw-r--r--Userland/Libraries/LibJS/Runtime/ObjectConstructor.h60
-rw-r--r--Userland/Libraries/LibJS/Runtime/ObjectPrototype.cpp166
-rw-r--r--Userland/Libraries/LibJS/Runtime/ObjectPrototype.h52
-rw-r--r--Userland/Libraries/LibJS/Runtime/PrimitiveString.cpp57
-rw-r--r--Userland/Libraries/LibJS/Runtime/PrimitiveString.h50
-rw-r--r--Userland/Libraries/LibJS/Runtime/PropertyAttributes.h104
-rw-r--r--Userland/Libraries/LibJS/Runtime/PropertyName.h163
-rw-r--r--Userland/Libraries/LibJS/Runtime/ProxyConstructor.cpp80
-rw-r--r--Userland/Libraries/LibJS/Runtime/ProxyConstructor.h48
-rw-r--r--Userland/Libraries/LibJS/Runtime/ProxyObject.cpp559
-rw-r--r--Userland/Libraries/LibJS/Runtime/ProxyObject.h75
-rw-r--r--Userland/Libraries/LibJS/Runtime/Reference.cpp106
-rw-r--r--Userland/Libraries/LibJS/Runtime/Reference.h101
-rw-r--r--Userland/Libraries/LibJS/Runtime/ReflectObject.cpp280
-rw-r--r--Userland/Libraries/LibJS/Runtime/ReflectObject.h57
-rw-r--r--Userland/Libraries/LibJS/Runtime/RegExpConstructor.cpp74
-rw-r--r--Userland/Libraries/LibJS/Runtime/RegExpConstructor.h48
-rw-r--r--Userland/Libraries/LibJS/Runtime/RegExpObject.cpp170
-rw-r--r--Userland/Libraries/LibJS/Runtime/RegExpObject.h66
-rw-r--r--Userland/Libraries/LibJS/Runtime/RegExpPrototype.cpp254
-rw-r--r--Userland/Libraries/LibJS/Runtime/RegExpPrototype.h57
-rw-r--r--Userland/Libraries/LibJS/Runtime/ScopeObject.cpp49
-rw-r--r--Userland/Libraries/LibJS/Runtime/ScopeObject.h60
-rw-r--r--Userland/Libraries/LibJS/Runtime/ScriptFunction.cpp187
-rw-r--r--Userland/Libraries/LibJS/Runtime/ScriptFunction.h77
-rw-r--r--Userland/Libraries/LibJS/Runtime/Shape.cpp227
-rw-r--r--Userland/Libraries/LibJS/Runtime/Shape.h132
-rw-r--r--Userland/Libraries/LibJS/Runtime/StringConstructor.cpp135
-rw-r--r--Userland/Libraries/LibJS/Runtime/StringConstructor.h51
-rw-r--r--Userland/Libraries/LibJS/Runtime/StringIterator.cpp49
-rw-r--r--Userland/Libraries/LibJS/Runtime/StringIterator.h54
-rw-r--r--Userland/Libraries/LibJS/Runtime/StringIteratorPrototype.cpp80
-rw-r--r--Userland/Libraries/LibJS/Runtime/StringIteratorPrototype.h45
-rw-r--r--Userland/Libraries/LibJS/Runtime/StringObject.cpp57
-rw-r--r--Userland/Libraries/LibJS/Runtime/StringObject.h54
-rw-r--r--Userland/Libraries/LibJS/Runtime/StringOrSymbol.h196
-rw-r--r--Userland/Libraries/LibJS/Runtime/StringPrototype.cpp629
-rw-r--r--Userland/Libraries/LibJS/Runtime/StringPrototype.h70
-rw-r--r--Userland/Libraries/LibJS/Runtime/Symbol.cpp53
-rw-r--r--Userland/Libraries/LibJS/Runtime/Symbol.h56
-rw-r--r--Userland/Libraries/LibJS/Runtime/SymbolConstructor.cpp99
-rw-r--r--Userland/Libraries/LibJS/Runtime/SymbolConstructor.h51
-rw-r--r--Userland/Libraries/LibJS/Runtime/SymbolObject.cpp57
-rw-r--r--Userland/Libraries/LibJS/Runtime/SymbolObject.h60
-rw-r--r--Userland/Libraries/LibJS/Runtime/SymbolPrototype.cpp97
-rw-r--r--Userland/Libraries/LibJS/Runtime/SymbolPrototype.h48
-rw-r--r--Userland/Libraries/LibJS/Runtime/TypedArray.cpp163
-rw-r--r--Userland/Libraries/LibJS/Runtime/TypedArray.h180
-rw-r--r--Userland/Libraries/LibJS/Runtime/TypedArrayConstructor.cpp65
-rw-r--r--Userland/Libraries/LibJS/Runtime/TypedArrayConstructor.h49
-rw-r--r--Userland/Libraries/LibJS/Runtime/TypedArrayPrototype.cpp70
-rw-r--r--Userland/Libraries/LibJS/Runtime/TypedArrayPrototype.h45
-rw-r--r--Userland/Libraries/LibJS/Runtime/Uint8ClampedArray.cpp86
-rw-r--r--Userland/Libraries/LibJS/Runtime/Uint8ClampedArray.h57
-rw-r--r--Userland/Libraries/LibJS/Runtime/VM.cpp365
-rw-r--r--Userland/Libraries/LibJS/Runtime/VM.h290
-rw-r--r--Userland/Libraries/LibJS/Runtime/Value.cpp1221
-rw-r--r--Userland/Libraries/LibJS/Runtime/Value.h357
-rw-r--r--Userland/Libraries/LibJS/Runtime/WithScope.cpp67
-rw-r--r--Userland/Libraries/LibJS/Runtime/WithScope.h50
149 files changed, 19183 insertions, 0 deletions
diff --git a/Userland/Libraries/LibJS/Runtime/Accessor.h b/Userland/Libraries/LibJS/Runtime/Accessor.h
new file mode 100644
index 0000000000..ce2d9efb57
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/Accessor.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2020, Matthew Olsson <matthewcolsson@gmail.com>
+ * Copyright (c) 2020, Linus Groh <mail@linusgroh.de>
+ * 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/Function.h>
+#include <LibJS/Runtime/VM.h>
+
+namespace JS {
+
+class Accessor final : public Cell {
+public:
+ static Accessor* create(VM& vm, Function* getter, Function* setter)
+ {
+ return vm.heap().allocate_without_global_object<Accessor>(getter, setter);
+ }
+
+ Accessor(Function* getter, Function* setter)
+ : m_getter(getter)
+ , m_setter(setter)
+ {
+ }
+
+ Function* getter() const { return m_getter; }
+ void set_getter(Function* getter) { m_getter = getter; }
+
+ Function* setter() const { return m_setter; }
+ void set_setter(Function* setter) { m_setter = setter; }
+
+ Value call_getter(Value this_value)
+ {
+ if (!m_getter)
+ return js_undefined();
+ return vm().call(*m_getter, this_value);
+ }
+
+ void call_setter(Value this_value, Value setter_value)
+ {
+ if (!m_setter)
+ return;
+ // FIXME: It might be nice if we had a way to communicate to our caller if an exception happened after this.
+ [[maybe_unused]] auto rc = vm().call(*m_setter, this_value, setter_value);
+ }
+
+ void visit_edges(Cell::Visitor& visitor) override
+ {
+ visitor.visit(m_getter);
+ visitor.visit(m_setter);
+ }
+
+private:
+ const char* class_name() const override { return "Accessor"; };
+
+ Function* m_getter { nullptr };
+ Function* m_setter { nullptr };
+};
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/Array.cpp b/Userland/Libraries/LibJS/Runtime/Array.cpp
new file mode 100644
index 0000000000..6e1f5f5300
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/Array.cpp
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * 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 <AK/Function.h>
+#include <LibJS/Runtime/Array.h>
+#include <LibJS/Runtime/ArrayPrototype.h>
+#include <LibJS/Runtime/Error.h>
+#include <LibJS/Runtime/GlobalObject.h>
+
+namespace JS {
+
+Array* Array::create(GlobalObject& global_object)
+{
+ return global_object.heap().allocate<Array>(global_object, *global_object.array_prototype());
+}
+
+Array::Array(Object& prototype)
+ : Object(prototype)
+{
+ auto& vm = this->vm();
+ define_native_property(vm.names.length, length_getter, length_setter, Attribute::Writable);
+}
+
+Array::~Array()
+{
+}
+
+Array* Array::typed_this(VM& vm, GlobalObject& global_object)
+{
+ auto* this_object = vm.this_value(global_object).to_object(global_object);
+ if (!this_object)
+ return {};
+ if (!this_object->is_array()) {
+ vm.throw_exception<TypeError>(global_object, ErrorType::NotAn, "Array");
+ return nullptr;
+ }
+ return static_cast<Array*>(this_object);
+}
+
+JS_DEFINE_NATIVE_GETTER(Array::length_getter)
+{
+ auto* array = typed_this(vm, global_object);
+ if (!array)
+ return {};
+ return Value(static_cast<i32>(array->indexed_properties().array_like_size()));
+}
+
+JS_DEFINE_NATIVE_SETTER(Array::length_setter)
+{
+ auto* array = typed_this(vm, global_object);
+ if (!array)
+ return;
+ auto length = value.to_number(global_object);
+ if (vm.exception())
+ return;
+ if (length.is_nan() || length.is_infinity() || length.as_double() < 0) {
+ vm.throw_exception<RangeError>(global_object, ErrorType::InvalidLength, "array");
+ return;
+ }
+ array->indexed_properties().set_array_like_size(length.as_double());
+}
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/Array.h b/Userland/Libraries/LibJS/Runtime/Array.h
new file mode 100644
index 0000000000..2ee4727c35
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/Array.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * 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 Array final : public Object {
+ JS_OBJECT(Array, Object);
+
+public:
+ static Array* create(GlobalObject&);
+
+ explicit Array(Object& prototype);
+ virtual ~Array() override;
+
+ static Array* typed_this(VM&, GlobalObject&);
+
+private:
+ virtual bool is_array() const override { return true; }
+
+ JS_DECLARE_NATIVE_GETTER(length_getter);
+ JS_DECLARE_NATIVE_SETTER(length_setter);
+};
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/ArrayBuffer.cpp b/Userland/Libraries/LibJS/Runtime/ArrayBuffer.cpp
new file mode 100644
index 0000000000..007e61c0fa
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/ArrayBuffer.cpp
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2020, Linus Groh <mail@linusgroh.de>
+ * 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/ArrayBuffer.h>
+#include <LibJS/Runtime/GlobalObject.h>
+
+namespace JS {
+
+ArrayBuffer* ArrayBuffer::create(GlobalObject& global_object, size_t byte_size)
+{
+ return global_object.heap().allocate<ArrayBuffer>(global_object, byte_size, *global_object.array_buffer_prototype());
+}
+
+ArrayBuffer::ArrayBuffer(size_t byte_size, Object& prototype)
+ : Object(prototype)
+ , m_buffer(ByteBuffer::create_zeroed(byte_size))
+{
+}
+
+ArrayBuffer::~ArrayBuffer()
+{
+}
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/ArrayBuffer.h b/Userland/Libraries/LibJS/Runtime/ArrayBuffer.h
new file mode 100644
index 0000000000..d2724cd96e
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/ArrayBuffer.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2020, Linus Groh <mail@linusgroh.de>
+ * 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 <AK/ByteBuffer.h>
+#include <LibJS/Runtime/Object.h>
+
+namespace JS {
+
+class ArrayBuffer : public Object {
+ JS_OBJECT(ArrayBuffer, Object);
+
+public:
+ static ArrayBuffer* create(GlobalObject&, size_t);
+
+ ArrayBuffer(size_t, Object& prototype);
+ virtual ~ArrayBuffer() override;
+
+ size_t byte_length() const { return m_buffer.size(); }
+ ByteBuffer& buffer() { return m_buffer; }
+ const ByteBuffer& buffer() const { return m_buffer; }
+
+private:
+ ByteBuffer m_buffer;
+};
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/ArrayBufferConstructor.cpp b/Userland/Libraries/LibJS/Runtime/ArrayBufferConstructor.cpp
new file mode 100644
index 0000000000..b869dcd5f3
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/ArrayBufferConstructor.cpp
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2020, Linus Groh <mail@linusgroh.de>
+ * 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/ArrayBuffer.h>
+#include <LibJS/Runtime/ArrayBufferConstructor.h>
+#include <LibJS/Runtime/Error.h>
+#include <LibJS/Runtime/GlobalObject.h>
+#include <LibJS/Runtime/TypedArray.h>
+
+namespace JS {
+
+ArrayBufferConstructor::ArrayBufferConstructor(GlobalObject& global_object)
+ : NativeFunction(vm().names.ArrayBuffer, *global_object.function_prototype())
+{
+}
+
+void ArrayBufferConstructor::initialize(GlobalObject& global_object)
+{
+ auto& vm = this->vm();
+ NativeFunction::initialize(global_object);
+ u8 attr = Attribute::Writable | Attribute::Configurable;
+ define_property(vm.names.prototype, global_object.array_buffer_prototype(), 0);
+ define_property(vm.names.length, Value(1), Attribute::Configurable);
+ define_native_function(vm.names.isView, is_view, 1, attr);
+}
+
+ArrayBufferConstructor::~ArrayBufferConstructor()
+{
+}
+
+Value ArrayBufferConstructor::call()
+{
+ auto& vm = this->vm();
+ vm.throw_exception<TypeError>(global_object(), ErrorType::ConstructorWithoutNew, vm.names.ArrayBuffer);
+ return {};
+}
+
+Value ArrayBufferConstructor::construct(Function&)
+{
+ auto& vm = this->vm();
+ auto byte_length = vm.argument(0).to_index(global_object());
+ if (vm.exception()) {
+ // Re-throw more specific RangeError
+ vm.clear_exception();
+ vm.throw_exception<RangeError>(global_object(), ErrorType::InvalidLength, "array buffer");
+ return {};
+ }
+ return ArrayBuffer::create(global_object(), byte_length);
+}
+
+JS_DEFINE_NATIVE_FUNCTION(ArrayBufferConstructor::is_view)
+{
+ auto arg = vm.argument(0);
+ if (!arg.is_object())
+ return Value(false);
+ if (arg.as_object().is_typed_array())
+ return Value(true);
+ // FIXME: Check for DataView as well
+ return Value(false);
+}
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/ArrayBufferConstructor.h b/Userland/Libraries/LibJS/Runtime/ArrayBufferConstructor.h
new file mode 100644
index 0000000000..455fa7e7e2
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/ArrayBufferConstructor.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2020, Linus Groh <mail@linusgroh.de>
+ * 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/NativeFunction.h>
+
+namespace JS {
+
+class ArrayBufferConstructor final : public NativeFunction {
+ JS_OBJECT(ArrayBufferConstructor, NativeFunction);
+
+public:
+ explicit ArrayBufferConstructor(GlobalObject&);
+ virtual void initialize(GlobalObject&) override;
+ virtual ~ArrayBufferConstructor() override;
+
+ virtual Value call() override;
+ virtual Value construct(Function& new_target) override;
+
+private:
+ virtual bool has_constructor() const override { return true; }
+
+ JS_DECLARE_NATIVE_FUNCTION(is_view);
+};
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/ArrayBufferPrototype.cpp b/Userland/Libraries/LibJS/Runtime/ArrayBufferPrototype.cpp
new file mode 100644
index 0000000000..846acbd562
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/ArrayBufferPrototype.cpp
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2020, Linus Groh <mail@linusgroh.de>
+ * 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 <AK/Function.h>
+#include <LibJS/Runtime/ArrayBuffer.h>
+#include <LibJS/Runtime/ArrayBufferPrototype.h>
+#include <LibJS/Runtime/GlobalObject.h>
+
+namespace JS {
+
+ArrayBufferPrototype::ArrayBufferPrototype(GlobalObject& global_object)
+ : Object(*global_object.object_prototype())
+{
+}
+
+void ArrayBufferPrototype::initialize(GlobalObject& global_object)
+{
+ auto& vm = this->vm();
+ Object::initialize(global_object);
+ u8 attr = Attribute::Writable | Attribute::Configurable;
+ define_native_function(vm.names.slice, slice, 2, attr);
+ // FIXME: This should be an accessor property
+ define_native_property(vm.names.byteLength, byte_length_getter, {}, Attribute::Configurable);
+
+ define_property(vm.well_known_symbol_to_string_tag(), js_string(vm.heap(), "ArrayBuffer"), Attribute::Configurable);
+}
+
+ArrayBufferPrototype::~ArrayBufferPrototype()
+{
+}
+
+static ArrayBuffer* array_buffer_object_from(VM& vm, GlobalObject& global_object)
+{
+ // ArrayBuffer.prototype.* deliberately don't coerce |this| value to object.
+ auto this_value = vm.this_value(global_object);
+ if (!this_value.is_object())
+ return nullptr;
+ auto& this_object = this_value.as_object();
+ if (!is<ArrayBuffer>(this_object)) {
+ vm.throw_exception<TypeError>(global_object, ErrorType::NotAn, "ArrayBuffer");
+ return nullptr;
+ }
+ return static_cast<ArrayBuffer*>(&this_object);
+}
+
+JS_DEFINE_NATIVE_FUNCTION(ArrayBufferPrototype::slice)
+{
+ auto array_buffer_object = array_buffer_object_from(vm, global_object);
+ if (!array_buffer_object)
+ return {};
+ TODO();
+}
+
+JS_DEFINE_NATIVE_GETTER(ArrayBufferPrototype::byte_length_getter)
+{
+ auto array_buffer_object = array_buffer_object_from(vm, global_object);
+ if (!array_buffer_object)
+ return {};
+ // FIXME: Check for shared buffer
+ // FIXME: Check for detached buffer
+ return Value((double)array_buffer_object->byte_length());
+}
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/ArrayBufferPrototype.h b/Userland/Libraries/LibJS/Runtime/ArrayBufferPrototype.h
new file mode 100644
index 0000000000..96ed852830
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/ArrayBufferPrototype.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2020, Linus Groh <mail@linusgroh.de>
+ * 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 ArrayBufferPrototype final : public Object {
+ JS_OBJECT(ArrayBufferPrototype, Object);
+
+public:
+ explicit ArrayBufferPrototype(GlobalObject&);
+ virtual void initialize(GlobalObject&) override;
+ virtual ~ArrayBufferPrototype() override;
+
+private:
+ JS_DECLARE_NATIVE_FUNCTION(slice);
+ JS_DECLARE_NATIVE_GETTER(byte_length_getter);
+};
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/ArrayConstructor.cpp b/Userland/Libraries/LibJS/Runtime/ArrayConstructor.cpp
new file mode 100644
index 0000000000..153e2a4fe2
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/ArrayConstructor.cpp
@@ -0,0 +1,141 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * Copyright (c) 2020, Linus Groh <mail@linusgroh.de>
+ * 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 <AK/Function.h>
+#include <LibJS/Heap/Heap.h>
+#include <LibJS/Runtime/Array.h>
+#include <LibJS/Runtime/ArrayConstructor.h>
+#include <LibJS/Runtime/Error.h>
+#include <LibJS/Runtime/GlobalObject.h>
+#include <LibJS/Runtime/IteratorOperations.h>
+#include <LibJS/Runtime/Shape.h>
+
+namespace JS {
+
+ArrayConstructor::ArrayConstructor(GlobalObject& global_object)
+ : NativeFunction(vm().names.Array, *global_object.function_prototype())
+{
+}
+
+ArrayConstructor::~ArrayConstructor()
+{
+}
+
+void ArrayConstructor::initialize(GlobalObject& global_object)
+{
+ auto& vm = this->vm();
+ NativeFunction::initialize(global_object);
+
+ define_property(vm.names.prototype, global_object.array_prototype(), 0);
+ define_property(vm.names.length, Value(1), Attribute::Configurable);
+
+ u8 attr = Attribute::Writable | Attribute::Configurable;
+ define_native_function(vm.names.from, from, 1, attr);
+ define_native_function(vm.names.isArray, is_array, 1, attr);
+ define_native_function(vm.names.of, of, 0, attr);
+}
+
+Value ArrayConstructor::call()
+{
+ if (vm().argument_count() <= 0)
+ return Array::create(global_object());
+
+ if (vm().argument_count() == 1 && vm().argument(0).is_number()) {
+ auto array_length_value = vm().argument(0);
+ if (!array_length_value.is_integer() || array_length_value.as_i32() < 0) {
+ vm().throw_exception<RangeError>(global_object(), ErrorType::InvalidLength, "array");
+ return {};
+ }
+ auto* array = Array::create(global_object());
+ array->indexed_properties().set_array_like_size(array_length_value.as_i32());
+ return array;
+ }
+
+ auto* array = Array::create(global_object());
+ for (size_t i = 0; i < vm().argument_count(); ++i)
+ array->indexed_properties().append(vm().argument(i));
+ return array;
+}
+
+Value ArrayConstructor::construct(Function&)
+{
+ return call();
+}
+
+JS_DEFINE_NATIVE_FUNCTION(ArrayConstructor::from)
+{
+ auto value = vm.argument(0);
+ auto object = value.to_object(global_object);
+ if (!object)
+ return {};
+
+ auto* array = Array::create(global_object);
+
+ // Array.from() lets you create Arrays from:
+ if (auto size = object->indexed_properties().array_like_size()) {
+ // * array-like objects (objects with a length property and indexed elements)
+ MarkedValueList elements(vm.heap());
+ elements.ensure_capacity(size);
+ for (size_t i = 0; i < size; ++i) {
+ elements.append(object->get(i));
+ if (vm.exception())
+ return {};
+ }
+ array->set_indexed_property_elements(move(elements));
+ } else {
+ // * iterable objects
+ get_iterator_values(global_object, value, [&](Value element) {
+ if (vm.exception())
+ return IterationDecision::Break;
+ array->indexed_properties().append(element);
+ return IterationDecision::Continue;
+ });
+ if (vm.exception())
+ return {};
+ }
+
+ // FIXME: if interpreter.argument_count() >= 2: mapFn
+ // FIXME: if interpreter.argument_count() >= 3: thisArg
+
+ return array;
+}
+
+JS_DEFINE_NATIVE_FUNCTION(ArrayConstructor::is_array)
+{
+ auto value = vm.argument(0);
+ return Value(value.is_array());
+}
+
+JS_DEFINE_NATIVE_FUNCTION(ArrayConstructor::of)
+{
+ auto* array = Array::create(global_object);
+ for (size_t i = 0; i < vm.argument_count(); ++i)
+ array->indexed_properties().append(vm.argument(i));
+ return array;
+}
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/ArrayConstructor.h b/Userland/Libraries/LibJS/Runtime/ArrayConstructor.h
new file mode 100644
index 0000000000..5c85dfc04b
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/ArrayConstructor.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * 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/NativeFunction.h>
+
+namespace JS {
+
+class ArrayConstructor final : public NativeFunction {
+ JS_OBJECT(ArrayConstructor, NativeFunction);
+
+public:
+ explicit ArrayConstructor(GlobalObject&);
+ virtual void initialize(GlobalObject&) override;
+ virtual ~ArrayConstructor() override;
+
+ virtual Value call() override;
+ virtual Value construct(Function& new_target) override;
+
+private:
+ virtual bool has_constructor() const override { return true; }
+
+ JS_DECLARE_NATIVE_FUNCTION(from);
+ JS_DECLARE_NATIVE_FUNCTION(is_array);
+ JS_DECLARE_NATIVE_FUNCTION(of);
+};
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/ArrayIterator.cpp b/Userland/Libraries/LibJS/Runtime/ArrayIterator.cpp
new file mode 100644
index 0000000000..338ec0a0bc
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/ArrayIterator.cpp
@@ -0,0 +1,54 @@
+/*
+ * 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/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()
+{
+}
+
+void ArrayIterator::visit_edges(Cell::Visitor& visitor)
+{
+ Base::visit_edges(visitor);
+ visitor.visit(m_array);
+}
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/ArrayIterator.h b/Userland/Libraries/LibJS/Runtime/ArrayIterator.h
new file mode 100644
index 0000000000..30ec25b70f
--- /dev/null
+++ b/Userland/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 void visit_edges(Cell::Visitor&) override;
+
+ Value m_array;
+ Object::PropertyKind m_iteration_kind;
+ size_t m_index { 0 };
+};
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/ArrayIteratorPrototype.cpp b/Userland/Libraries/LibJS/Runtime/ArrayIteratorPrototype.cpp
new file mode 100644
index 0000000000..c3cbd179b0
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/ArrayIteratorPrototype.cpp
@@ -0,0 +1,95 @@
+/*
+ * 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(GlobalObject& global_object)
+{
+ auto& vm = this->vm();
+ Object::initialize(global_object);
+
+ define_native_function(vm.names.next, next, 0, Attribute::Configurable | Attribute::Writable);
+ define_property(global_object.vm().well_known_symbol_to_string_tag(), js_string(global_object.heap(), "Array Iterator"), Attribute::Configurable);
+}
+
+ArrayIteratorPrototype::~ArrayIteratorPrototype()
+{
+}
+
+JS_DEFINE_NATIVE_FUNCTION(ArrayIteratorPrototype::next)
+{
+ auto this_value = vm.this_value(global_object);
+ if (!this_value.is_object() || !is<ArrayIterator>(this_value.as_object())) {
+ vm.throw_exception<TypeError>(global_object, ErrorType::NotAn, "Array Iterator");
+ return {};
+ }
+ 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(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(global_object, js_undefined(), true);
+ }
+
+ iterator.m_index++;
+ if (iteration_kind == Object::PropertyKind::Key)
+ return create_iterator_result_object(global_object, Value(static_cast<i32>(index)), false);
+
+ auto value = array.get(index);
+ if (vm.exception())
+ return {};
+ if (iteration_kind == Object::PropertyKind::Value)
+ return create_iterator_result_object(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(global_object, entry_array, false);
+}
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/ArrayIteratorPrototype.h b/Userland/Libraries/LibJS/Runtime/ArrayIteratorPrototype.h
new file mode 100644
index 0000000000..0e79a55ca5
--- /dev/null
+++ b/Userland/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(GlobalObject&) override;
+ virtual ~ArrayIteratorPrototype() override;
+
+private:
+ JS_DECLARE_NATIVE_FUNCTION(next);
+};
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/ArrayPrototype.cpp b/Userland/Libraries/LibJS/Runtime/ArrayPrototype.cpp
new file mode 100644
index 0000000000..8b155e9a95
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/ArrayPrototype.cpp
@@ -0,0 +1,1037 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * Copyright (c) 2020, Linus Groh <mail@linusgroh.de>
+ * Copyright (c) 2020, Marcin Gasperowicz <xnooga@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 <AK/Function.h>
+#include <AK/HashTable.h>
+#include <AK/ScopeGuard.h>
+#include <AK/StringBuilder.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>
+#include <LibJS/Runtime/GlobalObject.h>
+#include <LibJS/Runtime/ObjectPrototype.h>
+#include <LibJS/Runtime/Value.h>
+
+namespace JS {
+
+static HashTable<Object*> s_array_join_seen_objects;
+
+ArrayPrototype::ArrayPrototype(GlobalObject& global_object)
+ : Object(*global_object.object_prototype())
+{
+}
+
+void ArrayPrototype::initialize(GlobalObject& global_object)
+{
+ auto& vm = this->vm();
+ Object::initialize(global_object);
+ u8 attr = Attribute::Writable | Attribute::Configurable;
+
+ define_native_function(vm.names.filter, filter, 1, attr);
+ define_native_function(vm.names.forEach, for_each, 1, attr);
+ define_native_function(vm.names.map, map, 1, attr);
+ define_native_function(vm.names.pop, pop, 0, attr);
+ define_native_function(vm.names.push, push, 1, attr);
+ define_native_function(vm.names.shift, shift, 0, attr);
+ define_native_function(vm.names.toString, to_string, 0, attr);
+ define_native_function(vm.names.toLocaleString, to_locale_string, 0, attr);
+ define_native_function(vm.names.unshift, unshift, 1, attr);
+ define_native_function(vm.names.join, join, 1, attr);
+ define_native_function(vm.names.concat, concat, 1, attr);
+ define_native_function(vm.names.slice, slice, 2, attr);
+ define_native_function(vm.names.indexOf, index_of, 1, attr);
+ define_native_function(vm.names.reduce, reduce, 1, attr);
+ define_native_function(vm.names.reduceRight, reduce_right, 1, attr);
+ define_native_function(vm.names.reverse, reverse, 0, attr);
+ define_native_function(vm.names.sort, sort, 1, attr);
+ define_native_function(vm.names.lastIndexOf, last_index_of, 1, attr);
+ define_native_function(vm.names.includes, includes, 1, attr);
+ define_native_function(vm.names.find, find, 1, attr);
+ define_native_function(vm.names.findIndex, find_index, 1, attr);
+ define_native_function(vm.names.some, some, 1, attr);
+ define_native_function(vm.names.every, every, 1, attr);
+ define_native_function(vm.names.splice, splice, 2, attr);
+ define_native_function(vm.names.fill, fill, 1, attr);
+ define_native_function(vm.names.values, values, 0, attr);
+ define_property(vm.names.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(vm.well_known_symbol_iterator(), get(vm.names.values), attr);
+}
+
+ArrayPrototype::~ArrayPrototype()
+{
+}
+
+static Function* callback_from_args(GlobalObject& global_object, const String& name)
+{
+ auto& vm = global_object.vm();
+ if (vm.argument_count() < 1) {
+ vm.throw_exception<TypeError>(global_object, ErrorType::ArrayPrototypeOneArg, name);
+ return nullptr;
+ }
+ auto callback = vm.argument(0);
+ if (!callback.is_function()) {
+ vm.throw_exception<TypeError>(global_object, ErrorType::NotAFunction, callback.to_string_without_side_effects());
+ return nullptr;
+ }
+ return &callback.as_function();
+}
+
+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)
+{
+ auto* this_object = vm.this_value(global_object).to_object(global_object);
+ if (!this_object)
+ return;
+
+ auto initial_length = length_of_array_like(global_object, *this_object);
+ if (vm.exception())
+ return;
+
+ auto* callback_function = callback_from_args(global_object, name);
+ if (!callback_function)
+ return;
+
+ auto this_value = vm.argument(1);
+
+ for (size_t i = 0; i < initial_length; ++i) {
+ auto value = this_object->get(i);
+ if (vm.exception())
+ return;
+ if (value.is_empty()) {
+ if (skip_empty)
+ continue;
+ value = js_undefined();
+ }
+
+ auto callback_result = vm.call(*callback_function, this_value, value, Value((i32)i), this_object);
+ if (vm.exception())
+ return;
+
+ if (callback(i, value, callback_result) == IterationDecision::Break)
+ break;
+ }
+}
+
+JS_DEFINE_NATIVE_FUNCTION(ArrayPrototype::filter)
+{
+ auto* new_array = Array::create(global_object);
+ for_each_item(vm, global_object, "filter", [&](auto, auto value, auto callback_result) {
+ if (callback_result.to_boolean())
+ new_array->indexed_properties().append(value);
+ return IterationDecision::Continue;
+ });
+ return Value(new_array);
+}
+
+JS_DEFINE_NATIVE_FUNCTION(ArrayPrototype::for_each)
+{
+ for_each_item(vm, global_object, "forEach", [](auto, auto, auto) {
+ return IterationDecision::Continue;
+ });
+ return js_undefined();
+}
+
+JS_DEFINE_NATIVE_FUNCTION(ArrayPrototype::map)
+{
+ auto* this_object = vm.this_value(global_object).to_object(global_object);
+ if (!this_object)
+ return {};
+ auto initial_length = length_of_array_like(global_object, *this_object);
+ if (vm.exception())
+ return {};
+ auto* new_array = Array::create(global_object);
+ new_array->indexed_properties().set_array_like_size(initial_length);
+ for_each_item(vm, global_object, "map", [&](auto index, auto, auto callback_result) {
+ if (vm.exception())
+ return IterationDecision::Break;
+ new_array->define_property(index, callback_result);
+ return IterationDecision::Continue;
+ });
+ return Value(new_array);
+}
+
+JS_DEFINE_NATIVE_FUNCTION(ArrayPrototype::push)
+{
+ auto* this_object = vm.this_value(global_object).to_object(global_object);
+ if (!this_object)
+ return {};
+ if (this_object->is_array()) {
+ auto* array = static_cast<Array*>(this_object);
+ for (size_t i = 0; i < vm.argument_count(); ++i)
+ array->indexed_properties().append(vm.argument(i));
+ return Value(static_cast<i32>(array->indexed_properties().array_like_size()));
+ }
+ auto length = length_of_array_like(global_object, *this_object);
+ if (vm.exception())
+ return {};
+ auto argument_count = vm.argument_count();
+ auto new_length = length + argument_count;
+ if (new_length > MAX_ARRAY_LIKE_INDEX) {
+ vm.throw_exception<TypeError>(global_object, ErrorType::ArrayMaxSize);
+ return {};
+ }
+ for (size_t i = 0; i < argument_count; ++i) {
+ this_object->put(length + i, vm.argument(i));
+ if (vm.exception())
+ return {};
+ }
+ auto new_length_value = Value((i32)new_length);
+ this_object->put(vm.names.length, new_length_value);
+ if (vm.exception())
+ return {};
+ return new_length_value;
+}
+
+JS_DEFINE_NATIVE_FUNCTION(ArrayPrototype::unshift)
+{
+ auto* array = Array::typed_this(vm, global_object);
+ if (!array)
+ return {};
+ for (size_t i = 0; i < vm.argument_count(); ++i)
+ array->indexed_properties().insert(i, vm.argument(i));
+ return Value(static_cast<i32>(array->indexed_properties().array_like_size()));
+}
+
+JS_DEFINE_NATIVE_FUNCTION(ArrayPrototype::pop)
+{
+ auto* this_object = vm.this_value(global_object).to_object(global_object);
+ if (!this_object)
+ return {};
+ if (this_object->is_array()) {
+ auto* array = static_cast<Array*>(this_object);
+ if (array->indexed_properties().is_empty())
+ return js_undefined();
+ return array->indexed_properties().take_last(array).value.value_or(js_undefined());
+ }
+ auto length = length_of_array_like(global_object, *this_object);
+ if (vm.exception())
+ return {};
+ if (length == 0) {
+ this_object->put(vm.names.length, Value(0));
+ return js_undefined();
+ }
+ auto index = length - 1;
+ auto element = this_object->get(index).value_or(js_undefined());
+ if (vm.exception())
+ return {};
+ this_object->delete_property(index);
+ if (vm.exception())
+ return {};
+ this_object->put(vm.names.length, Value((i32)index));
+ if (vm.exception())
+ return {};
+ return element;
+}
+
+JS_DEFINE_NATIVE_FUNCTION(ArrayPrototype::shift)
+{
+ auto* array = Array::typed_this(vm, global_object);
+ if (!array)
+ return {};
+ if (array->indexed_properties().is_empty())
+ return js_undefined();
+ auto result = array->indexed_properties().take_first(array);
+ if (vm.exception())
+ return {};
+ return result.value.value_or(js_undefined());
+}
+
+JS_DEFINE_NATIVE_FUNCTION(ArrayPrototype::to_string)
+{
+ auto* this_object = vm.this_value(global_object).to_object(global_object);
+ if (!this_object)
+ return {};
+ auto join_function = this_object->get(vm.names.join);
+ if (vm.exception())
+ return {};
+ if (!join_function.is_function())
+ return ObjectPrototype::to_string(vm, global_object);
+ return vm.call(join_function.as_function(), this_object);
+}
+
+JS_DEFINE_NATIVE_FUNCTION(ArrayPrototype::to_locale_string)
+{
+ auto* this_object = vm.this_value(global_object).to_object(global_object);
+ if (!this_object)
+ return {};
+
+ if (s_array_join_seen_objects.contains(this_object))
+ return js_string(vm, "");
+ s_array_join_seen_objects.set(this_object);
+ ArmedScopeGuard unsee_object_guard = [&] {
+ s_array_join_seen_objects.remove(this_object);
+ };
+
+ auto length = length_of_array_like(global_object, *this_object);
+ if (vm.exception())
+ return {};
+
+ String separator = ","; // NOTE: This is implementation-specific.
+ StringBuilder builder;
+ for (size_t i = 0; i < length; ++i) {
+ if (i > 0)
+ builder.append(separator);
+ auto value = this_object->get(i).value_or(js_undefined());
+ if (vm.exception())
+ return {};
+ if (value.is_nullish())
+ continue;
+ auto* value_object = value.to_object(global_object);
+ if (!value_object)
+ return {};
+ auto locale_string_result = value_object->invoke("toLocaleString");
+ if (vm.exception())
+ return {};
+ auto string = locale_string_result.to_string(global_object);
+ if (vm.exception())
+ return {};
+ builder.append(string);
+ }
+ return js_string(vm, builder.to_string());
+}
+
+JS_DEFINE_NATIVE_FUNCTION(ArrayPrototype::join)
+{
+ auto* this_object = vm.this_value(global_object).to_object(global_object);
+ if (!this_object)
+ return {};
+
+ // This is not part of the spec, but all major engines do some kind of circular reference checks.
+ // FWIW: engine262, a "100% spec compliant" ECMA-262 impl, aborts with "too much recursion".
+ // Same applies to Array.prototype.toLocaleString().
+ if (s_array_join_seen_objects.contains(this_object))
+ return js_string(vm, "");
+ s_array_join_seen_objects.set(this_object);
+ ArmedScopeGuard unsee_object_guard = [&] {
+ s_array_join_seen_objects.remove(this_object);
+ };
+
+ auto length = length_of_array_like(global_object, *this_object);
+ if (vm.exception())
+ return {};
+ String separator = ",";
+ if (!vm.argument(0).is_undefined()) {
+ separator = vm.argument(0).to_string(global_object);
+ if (vm.exception())
+ return {};
+ }
+ StringBuilder builder;
+ for (size_t i = 0; i < length; ++i) {
+ if (i > 0)
+ builder.append(separator);
+ auto value = this_object->get(i).value_or(js_undefined());
+ if (vm.exception())
+ return {};
+ if (value.is_nullish())
+ continue;
+ auto string = value.to_string(global_object);
+ if (vm.exception())
+ return {};
+ builder.append(string);
+ }
+
+ return js_string(vm, builder.to_string());
+}
+
+JS_DEFINE_NATIVE_FUNCTION(ArrayPrototype::concat)
+{
+ auto* array = Array::typed_this(vm, global_object);
+ if (!array)
+ return {};
+
+ auto* new_array = Array::create(global_object);
+ new_array->indexed_properties().append_all(array, array->indexed_properties());
+ if (vm.exception())
+ return {};
+
+ for (size_t i = 0; i < vm.argument_count(); ++i) {
+ auto argument = vm.argument(i);
+ if (argument.is_array()) {
+ auto& argument_object = argument.as_object();
+ new_array->indexed_properties().append_all(&argument_object, argument_object.indexed_properties());
+ if (vm.exception())
+ return {};
+ } else {
+ new_array->indexed_properties().append(argument);
+ }
+ }
+
+ return Value(new_array);
+}
+
+JS_DEFINE_NATIVE_FUNCTION(ArrayPrototype::slice)
+{
+ auto* array = Array::typed_this(vm, global_object);
+ if (!array)
+ return {};
+
+ auto* new_array = Array::create(global_object);
+ if (vm.argument_count() == 0) {
+ new_array->indexed_properties().append_all(array, array->indexed_properties());
+ if (vm.exception())
+ return {};
+ return new_array;
+ }
+
+ ssize_t array_size = static_cast<ssize_t>(array->indexed_properties().array_like_size());
+ auto start_slice = vm.argument(0).to_i32(global_object);
+ if (vm.exception())
+ return {};
+ auto end_slice = array_size;
+
+ if (start_slice > array_size)
+ return new_array;
+
+ if (start_slice < 0)
+ start_slice = end_slice + start_slice;
+
+ if (vm.argument_count() >= 2) {
+ end_slice = vm.argument(1).to_i32(global_object);
+ if (vm.exception())
+ return {};
+ if (end_slice < 0)
+ end_slice = array_size + end_slice;
+ else if (end_slice > array_size)
+ end_slice = array_size;
+ }
+
+ for (ssize_t i = start_slice; i < end_slice; ++i) {
+ new_array->indexed_properties().append(array->get(i));
+ if (vm.exception())
+ return {};
+ }
+
+ return new_array;
+}
+
+JS_DEFINE_NATIVE_FUNCTION(ArrayPrototype::index_of)
+{
+ auto* this_object = vm.this_value(global_object).to_object(global_object);
+ if (!this_object)
+ return {};
+ i32 length = length_of_array_like(global_object, *this_object);
+ if (vm.exception())
+ return {};
+ if (length == 0)
+ return Value(-1);
+ i32 from_index = 0;
+ if (vm.argument_count() >= 2) {
+ from_index = vm.argument(1).to_i32(global_object);
+ if (vm.exception())
+ return {};
+ if (from_index >= length)
+ return Value(-1);
+ if (from_index < 0)
+ from_index = max(length + from_index, 0);
+ }
+ auto search_element = vm.argument(0);
+ for (i32 i = from_index; i < length; ++i) {
+ auto element = this_object->get(i);
+ if (vm.exception())
+ return {};
+ if (strict_eq(element, search_element))
+ return Value(i);
+ }
+ return Value(-1);
+}
+
+JS_DEFINE_NATIVE_FUNCTION(ArrayPrototype::reduce)
+{
+ auto* this_object = vm.this_value(global_object).to_object(global_object);
+ if (!this_object)
+ return {};
+
+ auto initial_length = length_of_array_like(global_object, *this_object);
+ if (vm.exception())
+ return {};
+
+ auto* callback_function = callback_from_args(global_object, "reduce");
+ if (!callback_function)
+ return {};
+
+ size_t start = 0;
+
+ auto accumulator = js_undefined();
+ if (vm.argument_count() > 1) {
+ accumulator = vm.argument(1);
+ } else {
+ bool start_found = false;
+ while (!start_found && start < initial_length) {
+ auto value = this_object->get(start);
+ if (vm.exception())
+ return {};
+ start_found = !value.is_empty();
+ if (start_found)
+ accumulator = value;
+ start += 1;
+ }
+ if (!start_found) {
+ vm.throw_exception<TypeError>(global_object, ErrorType::ReduceNoInitial);
+ return {};
+ }
+ }
+
+ auto this_value = js_undefined();
+
+ for (size_t i = start; i < initial_length; ++i) {
+ auto value = this_object->get(i);
+ if (vm.exception())
+ return {};
+ if (value.is_empty())
+ continue;
+
+ accumulator = vm.call(*callback_function, this_value, accumulator, value, Value((i32)i), this_object);
+ if (vm.exception())
+ return {};
+ }
+
+ return accumulator;
+}
+
+JS_DEFINE_NATIVE_FUNCTION(ArrayPrototype::reduce_right)
+{
+ auto* this_object = vm.this_value(global_object).to_object(global_object);
+ if (!this_object)
+ return {};
+
+ auto initial_length = length_of_array_like(global_object, *this_object);
+ if (vm.exception())
+ return {};
+
+ auto* callback_function = callback_from_args(global_object, "reduceRight");
+ if (!callback_function)
+ return {};
+
+ int start = initial_length - 1;
+
+ auto accumulator = js_undefined();
+ if (vm.argument_count() > 1) {
+ accumulator = vm.argument(1);
+ } else {
+ bool start_found = false;
+ while (!start_found && start >= 0) {
+ auto value = this_object->get(start);
+ if (vm.exception())
+ return {};
+ start_found = !value.is_empty();
+ if (start_found)
+ accumulator = value;
+ start -= 1;
+ }
+ if (!start_found) {
+ vm.throw_exception<TypeError>(global_object, ErrorType::ReduceNoInitial);
+ return {};
+ }
+ }
+
+ auto this_value = js_undefined();
+
+ for (int i = start; i >= 0; --i) {
+ auto value = this_object->get(i);
+ if (vm.exception())
+ return {};
+ if (value.is_empty())
+ continue;
+
+ accumulator = vm.call(*callback_function, this_value, accumulator, value, Value((i32)i), this_object);
+ if (vm.exception())
+ return {};
+ }
+
+ return accumulator;
+}
+
+JS_DEFINE_NATIVE_FUNCTION(ArrayPrototype::reverse)
+{
+ auto* array = Array::typed_this(vm, global_object);
+ if (!array)
+ return {};
+
+ if (array->indexed_properties().is_empty())
+ return array;
+
+ MarkedValueList array_reverse(vm.heap());
+ auto size = array->indexed_properties().array_like_size();
+ array_reverse.ensure_capacity(size);
+
+ for (ssize_t i = size - 1; i >= 0; --i) {
+ array_reverse.append(array->get(i));
+ if (vm.exception())
+ return {};
+ }
+
+ array->set_indexed_property_elements(move(array_reverse));
+
+ return array;
+}
+
+static void array_merge_sort(VM& vm, GlobalObject& global_object, Function* compare_func, MarkedValueList& arr_to_sort)
+{
+ // FIXME: it would probably be better to switch to insertion sort for small arrays for
+ // better performance
+ if (arr_to_sort.size() <= 1)
+ return;
+
+ MarkedValueList left(vm.heap());
+ MarkedValueList right(vm.heap());
+
+ left.ensure_capacity(arr_to_sort.size() / 2);
+ right.ensure_capacity(arr_to_sort.size() / 2 + (arr_to_sort.size() & 1));
+
+ for (size_t i = 0; i < arr_to_sort.size(); ++i) {
+ if (i < arr_to_sort.size() / 2) {
+ left.append(arr_to_sort[i]);
+ } else {
+ right.append(arr_to_sort[i]);
+ }
+ }
+
+ array_merge_sort(vm, global_object, compare_func, left);
+ if (vm.exception())
+ return;
+ array_merge_sort(vm, global_object, compare_func, right);
+ if (vm.exception())
+ return;
+
+ arr_to_sort.clear();
+
+ size_t left_index = 0, right_index = 0;
+
+ while (left_index < left.size() && right_index < right.size()) {
+ auto x = left[left_index];
+ auto y = right[right_index];
+
+ double comparison_result;
+
+ if (x.is_undefined() && y.is_undefined()) {
+ comparison_result = 0;
+ } else if (x.is_undefined()) {
+ comparison_result = 1;
+ } else if (y.is_undefined()) {
+ comparison_result = -1;
+ } else if (compare_func) {
+ auto call_result = vm.call(*compare_func, js_undefined(), left[left_index], right[right_index]);
+ if (vm.exception())
+ return;
+
+ if (call_result.is_nan()) {
+ comparison_result = 0;
+ } else {
+ comparison_result = call_result.to_double(global_object);
+ if (vm.exception())
+ return;
+ }
+ } else {
+ // FIXME: It would probably be much better to be smarter about this and implement
+ // the Abstract Relational Comparison in line once iterating over code points, rather
+ // than calling it twice after creating two primitive strings.
+
+ auto x_string = x.to_primitive_string(global_object);
+ if (vm.exception())
+ return;
+ auto y_string = y.to_primitive_string(global_object);
+ if (vm.exception())
+ return;
+
+ auto x_string_value = Value(x_string);
+ auto y_string_value = Value(y_string);
+
+ // Because they are called with primitive strings, these abstract_relation calls
+ // should never result in a VM exception.
+ auto x_lt_y_relation = abstract_relation(global_object, true, x_string_value, y_string_value);
+ ASSERT(x_lt_y_relation != TriState::Unknown);
+ auto y_lt_x_relation = abstract_relation(global_object, true, y_string_value, x_string_value);
+ ASSERT(y_lt_x_relation != TriState::Unknown);
+
+ if (x_lt_y_relation == TriState::True) {
+ comparison_result = -1;
+ } else if (y_lt_x_relation == TriState::True) {
+ comparison_result = 1;
+ } else {
+ comparison_result = 0;
+ }
+ }
+
+ if (comparison_result <= 0) {
+ arr_to_sort.append(left[left_index]);
+ left_index++;
+ } else {
+ arr_to_sort.append(right[right_index]);
+ right_index++;
+ }
+ }
+
+ while (left_index < left.size()) {
+ arr_to_sort.append(left[left_index]);
+ left_index++;
+ }
+
+ while (right_index < right.size()) {
+ arr_to_sort.append(right[right_index]);
+ right_index++;
+ }
+}
+
+JS_DEFINE_NATIVE_FUNCTION(ArrayPrototype::sort)
+{
+ auto* array = vm.this_value(global_object).to_object(global_object);
+ if (vm.exception())
+ return {};
+
+ auto callback = vm.argument(0);
+ if (!callback.is_undefined() && !callback.is_function()) {
+ vm.throw_exception<TypeError>(global_object, ErrorType::NotAFunction, callback.to_string_without_side_effects());
+ return {};
+ }
+
+ auto original_length = length_of_array_like(global_object, *array);
+ if (vm.exception())
+ return {};
+
+ MarkedValueList values_to_sort(vm.heap());
+
+ for (size_t i = 0; i < original_length; ++i) {
+ auto element_val = array->get(i);
+ if (vm.exception())
+ return {};
+
+ if (!element_val.is_empty())
+ values_to_sort.append(element_val);
+ }
+
+ // Perform sorting by merge sort. This isn't as efficient compared to quick sort, but
+ // quicksort can't be used in all cases because the spec requires Array.prototype.sort()
+ // to be stable. FIXME: when initially scanning through the array, maintain a flag
+ // for if an unstable sort would be indistinguishable from a stable sort (such as just
+ // just strings or numbers), and in that case use quick sort instead for better performance.
+ array_merge_sort(vm, global_object, callback.is_undefined() ? nullptr : &callback.as_function(), values_to_sort);
+ if (vm.exception())
+ return {};
+
+ for (size_t i = 0; i < values_to_sort.size(); ++i) {
+ array->put(i, values_to_sort[i]);
+ if (vm.exception())
+ return {};
+ }
+
+ // The empty parts of the array are always sorted to the end, regardless of the
+ // compare function. FIXME: For performance, a similar process could be used
+ // for undefined, which are sorted to right before the empty values.
+ for (size_t i = values_to_sort.size(); i < original_length; ++i) {
+ array->delete_property(i);
+ if (vm.exception())
+ return {};
+ }
+
+ return array;
+}
+
+JS_DEFINE_NATIVE_FUNCTION(ArrayPrototype::last_index_of)
+{
+ auto* this_object = vm.this_value(global_object).to_object(global_object);
+ if (!this_object)
+ return {};
+ i32 length = length_of_array_like(global_object, *this_object);
+ if (vm.exception())
+ return {};
+ if (length == 0)
+ return Value(-1);
+ i32 from_index = length - 1;
+ if (vm.argument_count() >= 2) {
+ from_index = vm.argument(1).to_i32(global_object);
+ if (vm.exception())
+ return {};
+ if (from_index >= 0)
+ from_index = min(from_index, length - 1);
+ else
+ from_index = length + from_index;
+ }
+ auto search_element = vm.argument(0);
+ for (i32 i = from_index; i >= 0; --i) {
+ auto element = this_object->get(i);
+ if (vm.exception())
+ return {};
+ if (strict_eq(element, search_element))
+ return Value(i);
+ }
+ return Value(-1);
+}
+
+JS_DEFINE_NATIVE_FUNCTION(ArrayPrototype::includes)
+{
+ auto* this_object = vm.this_value(global_object).to_object(global_object);
+ if (!this_object)
+ return {};
+ i32 length = length_of_array_like(global_object, *this_object);
+ if (vm.exception())
+ return {};
+ if (length == 0)
+ return Value(false);
+ i32 from_index = 0;
+ if (vm.argument_count() >= 2) {
+ from_index = vm.argument(1).to_i32(global_object);
+ if (vm.exception())
+ return {};
+ if (from_index >= length)
+ return Value(false);
+ if (from_index < 0)
+ from_index = max(length + from_index, 0);
+ }
+ auto value_to_find = vm.argument(0);
+ for (i32 i = from_index; i < length; ++i) {
+ auto element = this_object->get(i).value_or(js_undefined());
+ if (vm.exception())
+ return {};
+ if (same_value_zero(element, value_to_find))
+ return Value(true);
+ }
+ return Value(false);
+}
+
+JS_DEFINE_NATIVE_FUNCTION(ArrayPrototype::find)
+{
+ auto result = js_undefined();
+ for_each_item(
+ vm, global_object, "find", [&](auto, auto value, auto callback_result) {
+ if (callback_result.to_boolean()) {
+ result = value;
+ return IterationDecision::Break;
+ }
+ return IterationDecision::Continue;
+ },
+ false);
+ return result;
+}
+
+JS_DEFINE_NATIVE_FUNCTION(ArrayPrototype::find_index)
+{
+ auto result_index = -1;
+ for_each_item(
+ vm, global_object, "findIndex", [&](auto index, auto, auto callback_result) {
+ if (callback_result.to_boolean()) {
+ result_index = index;
+ return IterationDecision::Break;
+ }
+ return IterationDecision::Continue;
+ },
+ false);
+ return Value(result_index);
+}
+
+JS_DEFINE_NATIVE_FUNCTION(ArrayPrototype::some)
+{
+ auto result = false;
+ for_each_item(vm, global_object, "some", [&](auto, auto, auto callback_result) {
+ if (callback_result.to_boolean()) {
+ result = true;
+ return IterationDecision::Break;
+ }
+ return IterationDecision::Continue;
+ });
+ return Value(result);
+}
+
+JS_DEFINE_NATIVE_FUNCTION(ArrayPrototype::every)
+{
+ auto result = true;
+ for_each_item(vm, global_object, "every", [&](auto, auto, auto callback_result) {
+ if (!callback_result.to_boolean()) {
+ result = false;
+ return IterationDecision::Break;
+ }
+ return IterationDecision::Continue;
+ });
+ return Value(result);
+}
+
+JS_DEFINE_NATIVE_FUNCTION(ArrayPrototype::splice)
+{
+ auto* this_object = vm.this_value(global_object).to_object(global_object);
+ if (!this_object)
+ return {};
+
+ auto initial_length = length_of_array_like(global_object, *this_object);
+ if (vm.exception())
+ return {};
+
+ auto relative_start = vm.argument(0).to_i32(global_object);
+ if (vm.exception())
+ return {};
+
+ size_t actual_start;
+
+ if (relative_start < 0)
+ actual_start = max((ssize_t)initial_length + relative_start, (ssize_t)0);
+ else
+ actual_start = min((size_t)relative_start, initial_length);
+
+ size_t insert_count = 0;
+ size_t actual_delete_count = 0;
+
+ if (vm.argument_count() == 1) {
+ actual_delete_count = initial_length - actual_start;
+ } else if (vm.argument_count() >= 2) {
+ insert_count = vm.argument_count() - 2;
+ i32 delete_count = vm.argument(1).to_i32(global_object);
+ if (vm.exception())
+ return {};
+
+ actual_delete_count = min((size_t)max(delete_count, 0), initial_length - actual_start);
+ }
+
+ size_t new_length = initial_length + insert_count - actual_delete_count;
+
+ if (new_length > MAX_ARRAY_LIKE_INDEX) {
+ vm.throw_exception<TypeError>(global_object, ErrorType::ArrayMaxSize);
+ return {};
+ }
+
+ auto removed_elements = Array::create(global_object);
+
+ for (size_t i = 0; i < actual_delete_count; ++i) {
+ auto value = this_object->get(actual_start + i);
+ if (vm.exception())
+ return {};
+
+ removed_elements->indexed_properties().append(value);
+ }
+
+ if (insert_count < actual_delete_count) {
+ for (size_t i = actual_start; i < initial_length - actual_delete_count; ++i) {
+ auto from = this_object->get(i + actual_delete_count);
+ if (vm.exception())
+ return {};
+
+ auto to = i + insert_count;
+
+ if (!from.is_empty()) {
+ this_object->put(to, from);
+ } else {
+ this_object->delete_property(to);
+ }
+ if (vm.exception())
+ return {};
+ }
+
+ for (size_t i = initial_length; i > new_length; --i) {
+ this_object->delete_property(i - 1);
+ if (vm.exception())
+ return {};
+ }
+ } else if (insert_count > actual_delete_count) {
+ for (size_t i = initial_length - actual_delete_count; i > actual_start; --i) {
+ auto from = this_object->get(i + actual_delete_count - 1);
+ if (vm.exception())
+ return {};
+
+ auto to = i + insert_count - 1;
+
+ if (!from.is_empty()) {
+ this_object->put(to, from);
+ } else {
+ this_object->delete_property(to);
+ }
+ if (vm.exception())
+ return {};
+ }
+ }
+
+ for (size_t i = 0; i < insert_count; ++i) {
+ this_object->put(actual_start + i, vm.argument(i + 2));
+ if (vm.exception())
+ return {};
+ }
+
+ this_object->put(vm.names.length, Value((i32)new_length));
+ if (vm.exception())
+ return {};
+
+ return removed_elements;
+}
+
+JS_DEFINE_NATIVE_FUNCTION(ArrayPrototype::fill)
+{
+ auto* this_object = vm.this_value(global_object).to_object(global_object);
+ if (!this_object)
+ return {};
+
+ ssize_t length = length_of_array_like(global_object, *this_object);
+ if (vm.exception())
+ return {};
+
+ ssize_t relative_start = 0;
+ ssize_t relative_end = length;
+
+ if (vm.argument_count() >= 2) {
+ relative_start = vm.argument(1).to_i32(global_object);
+ if (vm.exception())
+ return {};
+ }
+
+ if (vm.argument_count() >= 3) {
+ relative_end = vm.argument(2).to_i32(global_object);
+ if (vm.exception())
+ return {};
+ }
+
+ size_t from, to;
+
+ if (relative_start < 0)
+ from = max(length + relative_start, 0L);
+ else
+ from = min(relative_start, length);
+
+ if (relative_end < 0)
+ to = max(length + relative_end, 0L);
+ else
+ to = min(relative_end, length);
+
+ for (size_t i = from; i < to; i++) {
+ this_object->put(i, vm.argument(0));
+ if (vm.exception())
+ return {};
+ }
+
+ return this_object;
+}
+
+JS_DEFINE_NATIVE_FUNCTION(ArrayPrototype::values)
+{
+ auto* this_object = vm.this_value(global_object).to_object(global_object);
+ if (!this_object)
+ return {};
+
+ return ArrayIterator::create(global_object, this_object, Object::PropertyKind::Value);
+}
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/ArrayPrototype.h b/Userland/Libraries/LibJS/Runtime/ArrayPrototype.h
new file mode 100644
index 0000000000..bbdb82903e
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/ArrayPrototype.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * Copyright (c) 2020, Linus Groh <mail@linusgroh.de>
+ * 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 ArrayPrototype final : public Object {
+ JS_OBJECT(ArrayPrototype, Object);
+
+public:
+ ArrayPrototype(GlobalObject&);
+ virtual void initialize(GlobalObject&) override;
+ virtual ~ArrayPrototype() override;
+
+private:
+ JS_DECLARE_NATIVE_FUNCTION(filter);
+ JS_DECLARE_NATIVE_FUNCTION(for_each);
+ JS_DECLARE_NATIVE_FUNCTION(map);
+ JS_DECLARE_NATIVE_FUNCTION(pop);
+ JS_DECLARE_NATIVE_FUNCTION(push);
+ JS_DECLARE_NATIVE_FUNCTION(shift);
+ JS_DECLARE_NATIVE_FUNCTION(to_string);
+ JS_DECLARE_NATIVE_FUNCTION(to_locale_string);
+ JS_DECLARE_NATIVE_FUNCTION(unshift);
+ JS_DECLARE_NATIVE_FUNCTION(join);
+ JS_DECLARE_NATIVE_FUNCTION(concat);
+ JS_DECLARE_NATIVE_FUNCTION(slice);
+ JS_DECLARE_NATIVE_FUNCTION(index_of);
+ JS_DECLARE_NATIVE_FUNCTION(reduce);
+ JS_DECLARE_NATIVE_FUNCTION(reduce_right);
+ JS_DECLARE_NATIVE_FUNCTION(reverse);
+ JS_DECLARE_NATIVE_FUNCTION(sort);
+ JS_DECLARE_NATIVE_FUNCTION(last_index_of);
+ JS_DECLARE_NATIVE_FUNCTION(includes);
+ JS_DECLARE_NATIVE_FUNCTION(find);
+ JS_DECLARE_NATIVE_FUNCTION(find_index);
+ JS_DECLARE_NATIVE_FUNCTION(some);
+ JS_DECLARE_NATIVE_FUNCTION(every);
+ JS_DECLARE_NATIVE_FUNCTION(splice);
+ JS_DECLARE_NATIVE_FUNCTION(fill);
+ JS_DECLARE_NATIVE_FUNCTION(values);
+};
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/BigInt.cpp b/Userland/Libraries/LibJS/Runtime/BigInt.cpp
new file mode 100644
index 0000000000..59a0951ff7
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/BigInt.cpp
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2020, Linus Groh <mail@linusgroh.de>
+ * 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 <LibCrypto/BigInt/SignedBigInteger.h>
+#include <LibJS/Heap/Heap.h>
+#include <LibJS/Runtime/BigInt.h>
+
+namespace JS {
+
+BigInt::BigInt(Crypto::SignedBigInteger big_integer)
+ : m_big_integer(move(big_integer))
+{
+ ASSERT(!m_big_integer.is_invalid());
+}
+
+BigInt::~BigInt()
+{
+}
+
+BigInt* js_bigint(Heap& heap, Crypto::SignedBigInteger big_integer)
+{
+ return heap.allocate_without_global_object<BigInt>(move(big_integer));
+}
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/BigInt.h b/Userland/Libraries/LibJS/Runtime/BigInt.h
new file mode 100644
index 0000000000..09a700c4b0
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/BigInt.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2020, Linus Groh <mail@linusgroh.de>
+ * 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 <LibCrypto/BigInt/SignedBigInteger.h>
+#include <LibJS/Runtime/Cell.h>
+
+namespace JS {
+
+class BigInt final : public Cell {
+public:
+ BigInt(Crypto::SignedBigInteger);
+ virtual ~BigInt();
+
+ const Crypto::SignedBigInteger& big_integer() const { return m_big_integer; }
+ const String to_string() const { return String::formatted("{}n", m_big_integer.to_base10()); }
+
+private:
+ virtual const char* class_name() const override { return "BigInt"; }
+
+ Crypto::SignedBigInteger m_big_integer;
+};
+
+BigInt* js_bigint(Heap&, Crypto::SignedBigInteger);
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/BigIntConstructor.cpp b/Userland/Libraries/LibJS/Runtime/BigIntConstructor.cpp
new file mode 100644
index 0000000000..5eec127b63
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/BigIntConstructor.cpp
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 2020, Linus Groh <mail@linusgroh.de>
+ * 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 <AK/String.h>
+#include <LibCrypto/BigInt/SignedBigInteger.h>
+#include <LibJS/Runtime/BigIntConstructor.h>
+#include <LibJS/Runtime/BigIntObject.h>
+#include <LibJS/Runtime/Error.h>
+#include <LibJS/Runtime/GlobalObject.h>
+#include <LibJS/Runtime/VM.h>
+
+namespace JS {
+
+BigIntConstructor::BigIntConstructor(GlobalObject& global_object)
+ : NativeFunction(vm().names.BigInt, *global_object.function_prototype())
+{
+}
+
+void BigIntConstructor::initialize(GlobalObject& global_object)
+{
+ auto& vm = this->vm();
+ NativeFunction::initialize(global_object);
+ define_property(vm.names.prototype, global_object.bigint_prototype(), 0);
+ define_property(vm.names.length, Value(1), Attribute::Configurable);
+
+ u8 attr = Attribute::Writable | Attribute::Configurable;
+ define_native_function(vm.names.asIntN, as_int_n, 2, attr);
+ define_native_function(vm.names.asUintN, as_uint_n, 2, attr);
+}
+
+BigIntConstructor::~BigIntConstructor()
+{
+}
+
+Value BigIntConstructor::call()
+{
+ auto primitive = vm().argument(0).to_primitive(Value::PreferredType::Number);
+ if (vm().exception())
+ return {};
+ if (primitive.is_number()) {
+ if (!primitive.is_integer()) {
+ vm().throw_exception<RangeError>(global_object(), ErrorType::BigIntIntArgument);
+ return {};
+ }
+ return js_bigint(heap(), Crypto::SignedBigInteger { primitive.as_i32() });
+ }
+ auto* bigint = vm().argument(0).to_bigint(global_object());
+ if (vm().exception())
+ return {};
+ return bigint;
+}
+
+Value BigIntConstructor::construct(Function&)
+{
+ vm().throw_exception<TypeError>(global_object(), ErrorType::NotAConstructor, "BigInt");
+ return {};
+}
+
+JS_DEFINE_NATIVE_FUNCTION(BigIntConstructor::as_int_n)
+{
+ TODO();
+}
+
+JS_DEFINE_NATIVE_FUNCTION(BigIntConstructor::as_uint_n)
+{
+ TODO();
+}
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/BigIntConstructor.h b/Userland/Libraries/LibJS/Runtime/BigIntConstructor.h
new file mode 100644
index 0000000000..5a4fc6bef7
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/BigIntConstructor.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2020, Linus Groh <mail@linusgroh.de>
+ * 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/NativeFunction.h>
+
+namespace JS {
+
+class BigIntConstructor final : public NativeFunction {
+ JS_OBJECT(BigIntConstructor, NativeFunction);
+
+public:
+ explicit BigIntConstructor(GlobalObject&);
+ virtual void initialize(GlobalObject&) override;
+ virtual ~BigIntConstructor() override;
+
+ virtual Value call() override;
+ virtual Value construct(Function& new_target) override;
+
+private:
+ virtual bool has_constructor() const override { return true; }
+
+ JS_DECLARE_NATIVE_FUNCTION(as_int_n);
+ JS_DECLARE_NATIVE_FUNCTION(as_uint_n);
+};
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/BigIntObject.cpp b/Userland/Libraries/LibJS/Runtime/BigIntObject.cpp
new file mode 100644
index 0000000000..eea70af64e
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/BigIntObject.cpp
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2020, Linus Groh <mail@linusgroh.de>
+ * 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/Heap/Heap.h>
+#include <LibJS/Runtime/BigIntObject.h>
+#include <LibJS/Runtime/GlobalObject.h>
+
+namespace JS {
+
+BigIntObject* BigIntObject::create(GlobalObject& global_object, BigInt& bigint)
+{
+ return global_object.heap().allocate<BigIntObject>(global_object, bigint, *global_object.bigint_prototype());
+}
+
+BigIntObject::BigIntObject(BigInt& bigint, Object& prototype)
+ : Object(prototype)
+ , m_bigint(bigint)
+{
+}
+
+BigIntObject::~BigIntObject()
+{
+}
+
+void BigIntObject::visit_edges(Cell::Visitor& visitor)
+{
+ Object::visit_edges(visitor);
+ visitor.visit(&m_bigint);
+}
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/BigIntObject.h b/Userland/Libraries/LibJS/Runtime/BigIntObject.h
new file mode 100644
index 0000000000..f58183e4c3
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/BigIntObject.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2020, Linus Groh <mail@linusgroh.de>
+ * 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/BigInt.h>
+#include <LibJS/Runtime/Object.h>
+
+namespace JS {
+
+class BigIntObject final : public Object {
+ JS_OBJECT(BigIntObject, Object);
+
+public:
+ static BigIntObject* create(GlobalObject&, BigInt&);
+
+ BigIntObject(BigInt&, Object& prototype);
+ virtual ~BigIntObject();
+
+ const BigInt& bigint() const { return m_bigint; }
+ virtual Value value_of() const override
+ {
+ return Value(&m_bigint);
+ }
+
+private:
+ virtual void visit_edges(Visitor&) override;
+
+ BigInt& m_bigint;
+};
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/BigIntPrototype.cpp b/Userland/Libraries/LibJS/Runtime/BigIntPrototype.cpp
new file mode 100644
index 0000000000..84b6617fe5
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/BigIntPrototype.cpp
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2020, Linus Groh <mail@linusgroh.de>
+ * 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 <AK/Function.h>
+#include <LibJS/Runtime/BigIntObject.h>
+#include <LibJS/Runtime/BigIntPrototype.h>
+#include <LibJS/Runtime/Error.h>
+#include <LibJS/Runtime/GlobalObject.h>
+
+namespace JS {
+
+BigIntPrototype::BigIntPrototype(GlobalObject& global_object)
+ : Object(*global_object.object_prototype())
+{
+}
+
+void BigIntPrototype::initialize(GlobalObject& global_object)
+{
+ auto& vm = this->vm();
+ Object::initialize(global_object);
+ u8 attr = Attribute::Writable | Attribute::Configurable;
+ define_native_function(vm.names.toString, to_string, 0, attr);
+ define_native_function(vm.names.toLocaleString, to_locale_string, 0, attr);
+ define_native_function(vm.names.valueOf, value_of, 0, attr);
+
+ define_property(vm.well_known_symbol_to_string_tag(), js_string(global_object.heap(), "BigInt"), Attribute::Configurable);
+}
+
+BigIntPrototype::~BigIntPrototype()
+{
+}
+
+static BigIntObject* bigint_object_from(VM& vm, GlobalObject& global_object)
+{
+ auto* this_object = vm.this_value(global_object).to_object(global_object);
+ if (!this_object)
+ return nullptr;
+ if (!is<BigIntObject>(this_object)) {
+ vm.throw_exception<TypeError>(global_object, ErrorType::NotA, "BigInt");
+ return nullptr;
+ }
+ return static_cast<BigIntObject*>(this_object);
+}
+
+JS_DEFINE_NATIVE_FUNCTION(BigIntPrototype::to_string)
+{
+ auto* bigint_object = bigint_object_from(vm, global_object);
+ if (!bigint_object)
+ return {};
+ return js_string(vm, bigint_object->bigint().big_integer().to_base10());
+}
+
+JS_DEFINE_NATIVE_FUNCTION(BigIntPrototype::to_locale_string)
+{
+ return to_string(vm, global_object);
+}
+
+JS_DEFINE_NATIVE_FUNCTION(BigIntPrototype::value_of)
+{
+ auto* bigint_object = bigint_object_from(vm, global_object);
+ if (!bigint_object)
+ return {};
+ return bigint_object->value_of();
+}
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/BigIntPrototype.h b/Userland/Libraries/LibJS/Runtime/BigIntPrototype.h
new file mode 100644
index 0000000000..c0e54a7c2a
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/BigIntPrototype.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2020, Linus Groh <mail@linusgroh.de>
+ * 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 BigIntPrototype final : public Object {
+ JS_OBJECT(BigIntPrototype, Object);
+
+public:
+ explicit BigIntPrototype(GlobalObject&);
+ virtual void initialize(GlobalObject&) override;
+ virtual ~BigIntPrototype() override;
+
+private:
+ JS_DECLARE_NATIVE_FUNCTION(to_string);
+ JS_DECLARE_NATIVE_FUNCTION(to_locale_string);
+ JS_DECLARE_NATIVE_FUNCTION(value_of);
+};
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/BooleanConstructor.cpp b/Userland/Libraries/LibJS/Runtime/BooleanConstructor.cpp
new file mode 100644
index 0000000000..33e71c238d
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/BooleanConstructor.cpp
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2020, Jack Karamanian <karamanian.jack@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/Heap/Heap.h>
+#include <LibJS/Runtime/BooleanConstructor.h>
+#include <LibJS/Runtime/BooleanObject.h>
+#include <LibJS/Runtime/BooleanPrototype.h>
+#include <LibJS/Runtime/GlobalObject.h>
+
+namespace JS {
+
+BooleanConstructor::BooleanConstructor(GlobalObject& global_object)
+ : NativeFunction(vm().names.Boolean, *global_object.function_prototype())
+{
+}
+
+void BooleanConstructor::initialize(GlobalObject& global_object)
+{
+ auto& vm = this->vm();
+ NativeFunction::initialize(global_object);
+ define_property(vm.names.prototype, Value(global_object.boolean_prototype()), 0);
+ define_property(vm.names.length, Value(1), Attribute::Configurable);
+}
+
+BooleanConstructor::~BooleanConstructor()
+{
+}
+
+Value BooleanConstructor::call()
+{
+ return Value(vm().argument(0).to_boolean());
+}
+
+Value BooleanConstructor::construct(Function&)
+{
+ return BooleanObject::create(global_object(), vm().argument(0).to_boolean());
+}
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/BooleanConstructor.h b/Userland/Libraries/LibJS/Runtime/BooleanConstructor.h
new file mode 100644
index 0000000000..1c68814b21
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/BooleanConstructor.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2020, Jack Karamanian <karamanian.jack@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/NativeFunction.h>
+
+namespace JS {
+
+class BooleanConstructor final : public NativeFunction {
+ JS_OBJECT(BooleanConstructor, NativeFunction);
+
+public:
+ explicit BooleanConstructor(GlobalObject&);
+ virtual void initialize(GlobalObject&) override;
+ virtual ~BooleanConstructor() override;
+
+ virtual Value call() override;
+ virtual Value construct(Function& new_target) override;
+
+private:
+ virtual bool has_constructor() const override { return true; }
+};
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/BooleanObject.cpp b/Userland/Libraries/LibJS/Runtime/BooleanObject.cpp
new file mode 100644
index 0000000000..b6a4cef400
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/BooleanObject.cpp
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2020, Jack Karamanian <karamanian.jack@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/BooleanObject.h>
+#include <LibJS/Runtime/GlobalObject.h>
+
+namespace JS {
+
+BooleanObject* BooleanObject::create(GlobalObject& global_object, bool value)
+{
+ return global_object.heap().allocate<BooleanObject>(global_object, value, *global_object.boolean_prototype());
+}
+
+BooleanObject::BooleanObject(bool value, Object& prototype)
+ : Object(prototype)
+ , m_value(value)
+{
+}
+
+BooleanObject::~BooleanObject()
+{
+}
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/BooleanObject.h b/Userland/Libraries/LibJS/Runtime/BooleanObject.h
new file mode 100644
index 0000000000..4b9775205a
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/BooleanObject.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2020, Jack Karamanian <karamanian.jack@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 BooleanObject : public Object {
+ JS_OBJECT(BooleanObject, Object);
+
+public:
+ static BooleanObject* create(GlobalObject&, bool);
+
+ BooleanObject(bool, Object& prototype);
+ virtual ~BooleanObject() override;
+
+ virtual Value value_of() const override
+ {
+ return Value(m_value);
+ }
+
+private:
+ bool m_value { false };
+};
+}
diff --git a/Userland/Libraries/LibJS/Runtime/BooleanPrototype.cpp b/Userland/Libraries/LibJS/Runtime/BooleanPrototype.cpp
new file mode 100644
index 0000000000..dd69ff57dc
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/BooleanPrototype.cpp
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2020, Jack Karamanian <karamanian.jack@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 <AK/Function.h>
+#include <LibJS/Runtime/BooleanPrototype.h>
+#include <LibJS/Runtime/Error.h>
+#include <LibJS/Runtime/GlobalObject.h>
+
+namespace JS {
+
+BooleanPrototype::BooleanPrototype(GlobalObject& global_object)
+ : BooleanObject(false, *global_object.object_prototype())
+{
+}
+
+void BooleanPrototype::initialize(GlobalObject& global_object)
+{
+ auto& vm = this->vm();
+ BooleanObject::initialize(global_object);
+ define_native_function(vm.names.toString, to_string, 0, Attribute::Writable | Attribute::Configurable);
+ define_native_function(vm.names.valueOf, value_of, 0, Attribute::Writable | Attribute::Configurable);
+}
+
+BooleanPrototype::~BooleanPrototype()
+{
+}
+
+JS_DEFINE_NATIVE_FUNCTION(BooleanPrototype::to_string)
+{
+ auto this_value = vm.this_value(global_object);
+ if (this_value.is_boolean())
+ return js_string(vm, this_value.as_bool() ? "true" : "false");
+ if (!this_value.is_object() || !is<BooleanObject>(this_value.as_object())) {
+ vm.throw_exception<TypeError>(global_object, ErrorType::NotA, "Boolean");
+ return {};
+ }
+
+ bool bool_value = static_cast<const BooleanObject&>(this_value.as_object()).value_of().as_bool();
+ return js_string(vm, bool_value ? "true" : "false");
+}
+
+JS_DEFINE_NATIVE_FUNCTION(BooleanPrototype::value_of)
+{
+ auto this_value = vm.this_value(global_object);
+ if (this_value.is_boolean())
+ return this_value;
+ if (!this_value.is_object() || !is<BooleanObject>(this_value.as_object())) {
+ vm.throw_exception<TypeError>(global_object, ErrorType::NotA, "Boolean");
+ return {};
+ }
+
+ return static_cast<const BooleanObject&>(this_value.as_object()).value_of();
+}
+}
diff --git a/Userland/Libraries/LibJS/Runtime/BooleanPrototype.h b/Userland/Libraries/LibJS/Runtime/BooleanPrototype.h
new file mode 100644
index 0000000000..10b46f4efb
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/BooleanPrototype.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2020, Jack Karamanian <karamanian.jack@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/BooleanObject.h>
+
+namespace JS {
+
+class BooleanPrototype final : public BooleanObject {
+ JS_OBJECT(BooleanPrototype, BooleanObject);
+
+public:
+ explicit BooleanPrototype(GlobalObject&);
+ virtual void initialize(GlobalObject&) override;
+ virtual ~BooleanPrototype() override;
+
+private:
+ JS_DECLARE_NATIVE_FUNCTION(to_string);
+ JS_DECLARE_NATIVE_FUNCTION(value_of);
+};
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/BoundFunction.cpp b/Userland/Libraries/LibJS/Runtime/BoundFunction.cpp
new file mode 100644
index 0000000000..891eebc57b
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/BoundFunction.cpp
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2020, Jack Karamanian <karamanian.jack@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/BoundFunction.h>
+#include <LibJS/Runtime/GlobalObject.h>
+
+namespace JS {
+
+BoundFunction::BoundFunction(GlobalObject& global_object, Function& target_function, Value bound_this, Vector<Value> arguments, i32 length, Object* constructor_prototype)
+ : Function::Function(*global_object.function_prototype(), bound_this, move(arguments))
+ , m_target_function(&target_function)
+ , m_constructor_prototype(constructor_prototype)
+ , m_name(String::formatted("bound {}", target_function.name()))
+ , m_length(length)
+{
+}
+
+void BoundFunction::initialize(GlobalObject& global_object)
+{
+ auto& vm = this->vm();
+ Function::initialize(global_object);
+ define_property(vm.names.length, Value(m_length), Attribute::Configurable);
+}
+
+BoundFunction::~BoundFunction()
+{
+}
+
+Value BoundFunction::call()
+{
+ return m_target_function->call();
+}
+
+Value BoundFunction::construct(Function& new_target)
+{
+ if (auto this_value = vm().this_value(global_object()); m_constructor_prototype && this_value.is_object()) {
+ this_value.as_object().set_prototype(m_constructor_prototype);
+ if (vm().exception())
+ return {};
+ }
+ return m_target_function->construct(new_target);
+}
+
+LexicalEnvironment* BoundFunction::create_environment()
+{
+ return m_target_function->create_environment();
+}
+
+void BoundFunction::visit_edges(Visitor& visitor)
+{
+ Function::visit_edges(visitor);
+ visitor.visit(m_target_function);
+ visitor.visit(m_constructor_prototype);
+}
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/BoundFunction.h b/Userland/Libraries/LibJS/Runtime/BoundFunction.h
new file mode 100644
index 0000000000..8d4c3a443c
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/BoundFunction.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2020, Jack Karamanian <karamanian.jack@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/Function.h>
+
+namespace JS {
+
+class BoundFunction final : public Function {
+ JS_OBJECT(BoundFunction, Function);
+
+public:
+ BoundFunction(GlobalObject&, Function& target_function, Value bound_this, Vector<Value> arguments, i32 length, Object* constructor_prototype);
+ virtual void initialize(GlobalObject&) override;
+ virtual ~BoundFunction();
+
+ virtual Value call() override;
+
+ virtual Value construct(Function& new_target) override;
+
+ virtual LexicalEnvironment* create_environment() override;
+
+ virtual void visit_edges(Visitor&) override;
+
+ virtual const FlyString& name() const override
+ {
+ return m_name;
+ }
+
+ Function& target_function() const
+ {
+ return *m_target_function;
+ }
+
+ virtual bool is_strict_mode() const override { return m_target_function->is_strict_mode(); }
+
+private:
+ Function* m_target_function = nullptr;
+ Object* m_constructor_prototype = nullptr;
+ FlyString m_name;
+ i32 m_length { 0 };
+};
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/Cell.cpp b/Userland/Libraries/LibJS/Runtime/Cell.cpp
new file mode 100644
index 0000000000..22a9979a35
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/Cell.cpp
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * 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/Heap/Heap.h>
+#include <LibJS/Heap/HeapBlock.h>
+#include <LibJS/Runtime/Cell.h>
+#include <LibJS/Runtime/Object.h>
+#include <LibJS/Runtime/PrimitiveString.h>
+#include <LibJS/Runtime/Value.h>
+
+namespace JS {
+
+void Cell::Visitor::visit(Cell* cell)
+{
+ if (cell)
+ visit_impl(cell);
+}
+
+void Cell::Visitor::visit(Value value)
+{
+ if (value.is_cell())
+ visit_impl(value.as_cell());
+}
+
+Heap& Cell::heap() const
+{
+ return HeapBlock::from_cell(this)->heap();
+}
+
+VM& Cell::vm() const
+{
+ return heap().vm();
+}
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/Cell.h b/Userland/Libraries/LibJS/Runtime/Cell.h
new file mode 100644
index 0000000000..df4a19f19a
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/Cell.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * 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 <AK/Format.h>
+#include <AK/Forward.h>
+#include <AK/Noncopyable.h>
+#include <AK/String.h>
+#include <AK/TypeCasts.h>
+#include <LibJS/Forward.h>
+
+namespace JS {
+
+class Cell {
+ AK_MAKE_NONCOPYABLE(Cell);
+ AK_MAKE_NONMOVABLE(Cell);
+
+public:
+ virtual void initialize(GlobalObject&) { }
+ virtual ~Cell() { }
+
+ bool is_marked() const { return m_mark; }
+ void set_marked(bool b) { m_mark = b; }
+
+ bool is_live() const { return m_live; }
+ void set_live(bool b) { m_live = b; }
+
+ virtual const char* class_name() const = 0;
+
+ class Visitor {
+ public:
+ void visit(Cell*);
+ void visit(Value);
+
+ protected:
+ virtual void visit_impl(Cell*) = 0;
+ };
+
+ virtual void visit_edges(Visitor&) { }
+
+ Heap& heap() const;
+ VM& vm() const;
+
+protected:
+ Cell() { }
+
+private:
+ bool m_mark { false };
+ bool m_live { true };
+};
+
+}
+
+template<>
+struct AK::Formatter<JS::Cell> : AK::Formatter<FormatString> {
+ void format(FormatBuilder& builder, const JS::Cell* cell)
+ {
+ if (!cell)
+ Formatter<FormatString>::format(builder, "Cell{nullptr}");
+ else
+ Formatter<FormatString>::format(builder, "{}({})", cell->class_name(), cell);
+ }
+};
diff --git a/Userland/Libraries/LibJS/Runtime/CommonPropertyNames.h b/Userland/Libraries/LibJS/Runtime/CommonPropertyNames.h
new file mode 100644
index 0000000000..2d1d4d1d2e
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/CommonPropertyNames.h
@@ -0,0 +1,255 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * 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 <AK/FlyString.h>
+#include <LibJS/Forward.h>
+
+namespace JS {
+
+#define ENUMERATE_STANDARD_PROPERTY_NAMES(P) \
+ P(BYTES_PER_ELEMENT) \
+ P(BigInt) \
+ P(Boolean) \
+ P(E) \
+ P(EPSILON) \
+ P(Infinity) \
+ P(JSON) \
+ P(LN10) \
+ P(LN2) \
+ P(LOG10E) \
+ P(LOG2E) \
+ P(MAX_SAFE_INTEGER) \
+ P(MIN_SAFE_INTEGER) \
+ P(Math) \
+ P(NEGATIVE_INFINITY) \
+ P(NaN) \
+ P(Number) \
+ P(PI) \
+ P(POSITIVE_INFINITY) \
+ P(Proxy) \
+ P(Reflect) \
+ P(RegExp) \
+ P(SQRT1_2) \
+ P(SQRT2) \
+ P(String) \
+ P(Symbol) \
+ P(UTC) \
+ P(abs) \
+ P(acos) \
+ P(acosh) \
+ P(apply) \
+ P(arguments) \
+ P(asIntN) \
+ P(asUintN) \
+ P(asin) \
+ P(asinh) \
+ P(atan) \
+ P(atan2) \
+ P(atanh) \
+ P(bind) \
+ P(byteLength) \
+ P(call) \
+ P(cbrt) \
+ P(ceil) \
+ P(charAt) \
+ P(charCodeAt) \
+ P(clear) \
+ P(clz32) \
+ P(concat) \
+ P(configurable) \
+ P(console) \
+ P(construct) \
+ P(constructor) \
+ P(cos) \
+ P(cosh) \
+ P(count) \
+ P(countReset) \
+ P(debug) \
+ P(defineProperty) \
+ P(deleteProperty) \
+ P(description) \
+ P(done) \
+ P(dotAll) \
+ P(endsWith) \
+ P(entries) \
+ P(enumerable) \
+ P(error) \
+ P(every) \
+ P(exec) \
+ P(exp) \
+ P(expm1) \
+ P(fill) \
+ P(filter) \
+ P(find) \
+ P(findIndex) \
+ P(flags) \
+ P(floor) \
+ P(forEach) \
+ P(from) \
+ P(fromCharCode) \
+ P(fround) \
+ P(gc) \
+ P(get) \
+ P(getDate) \
+ P(getDay) \
+ P(getFullYear) \
+ P(getHours) \
+ P(getMilliseconds) \
+ P(getMinutes) \
+ P(getMonth) \
+ P(getOwnPropertyDescriptor) \
+ P(getOwnPropertyNames) \
+ P(getPrototypeOf) \
+ P(getSeconds) \
+ P(getTime) \
+ P(getUTCDate) \
+ P(getUTCDay) \
+ P(getUTCFullYear) \
+ P(getUTCHours) \
+ P(getUTCMilliseconds) \
+ P(getUTCMinutes) \
+ P(getUTCMonth) \
+ P(getUTCSeconds) \
+ P(global) \
+ P(globalThis) \
+ P(groups) \
+ P(has) \
+ P(hasOwnProperty) \
+ P(hypot) \
+ P(ignoreCase) \
+ P(imul) \
+ P(includes) \
+ P(index) \
+ P(indexOf) \
+ P(info) \
+ P(input) \
+ P(is) \
+ P(isArray) \
+ P(isExtensible) \
+ P(isFinite) \
+ P(isInteger) \
+ P(isNaN) \
+ P(isPrototypeOf) \
+ P(isSafeInteger) \
+ P(isView) \
+ P(join) \
+ P(keyFor) \
+ P(keys) \
+ P(lastIndex) \
+ P(lastIndexOf) \
+ P(length) \
+ P(log) \
+ P(log1p) \
+ P(log2) \
+ P(log10) \
+ P(map) \
+ P(max) \
+ P(message) \
+ P(min) \
+ P(multiline) \
+ P(name) \
+ P(next) \
+ P(now) \
+ P(of) \
+ P(ownKeys) \
+ P(padEnd) \
+ P(padStart) \
+ P(parse) \
+ P(parseFloat) \
+ P(parseInt) \
+ P(pop) \
+ P(pow) \
+ P(preventExtensions) \
+ P(propertyIsEnumerable) \
+ P(prototype) \
+ P(push) \
+ P(random) \
+ P(raw) \
+ P(reduce) \
+ P(reduceRight) \
+ P(repeat) \
+ P(reverse) \
+ P(round) \
+ P(set) \
+ P(setPrototypeOf) \
+ P(shift) \
+ P(sign) \
+ P(sin) \
+ P(sinh) \
+ P(slice) \
+ P(some) \
+ P(sort) \
+ P(source) \
+ P(splice) \
+ P(sqrt) \
+ P(startsWith) \
+ P(sticky) \
+ P(stringify) \
+ P(substr) \
+ P(substring) \
+ P(tan) \
+ P(tanh) \
+ P(test) \
+ P(toDateString) \
+ P(toISOString) \
+ P(toJSON) \
+ P(toLocaleDateString) \
+ P(toLocaleString) \
+ P(toLocaleTimeString) \
+ P(toLowerCase) \
+ P(toString) \
+ P(toTimeString) \
+ P(toUpperCase) \
+ P(trace) \
+ P(trim) \
+ P(trimEnd) \
+ P(trimStart) \
+ P(trunc) \
+ P(undefined) \
+ P(unicode) \
+ P(unshift) \
+ P(value) \
+ P(valueOf) \
+ P(values) \
+ P(warn) \
+ P(writable)
+
+struct CommonPropertyNames {
+ FlyString for_ { "for" };
+#define __ENUMERATE(x) FlyString x { #x };
+ ENUMERATE_STANDARD_PROPERTY_NAMES(__ENUMERATE)
+#undef __ENUMERATE
+#define __JS_ENUMERATE(x, a, b, c, t) FlyString x { #x };
+ JS_ENUMERATE_BUILTIN_TYPES
+#undef __JS_ENUMERATE
+#define __JS_ENUMERATE(x, a) FlyString x { #x };
+ JS_ENUMERATE_WELL_KNOWN_SYMBOLS
+#undef __JS_ENUMERATE
+};
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/ConsoleObject.cpp b/Userland/Libraries/LibJS/Runtime/ConsoleObject.cpp
new file mode 100644
index 0000000000..f87b29c0a1
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/ConsoleObject.cpp
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * Copyright (c) 2020, Linus Groh <mail@linusgroh.de>
+ * Copyright (c) 2020, Emanuele Torre <torreemanuele6@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 <AK/FlyString.h>
+#include <AK/Function.h>
+#include <LibJS/Console.h>
+#include <LibJS/Runtime/ConsoleObject.h>
+#include <LibJS/Runtime/GlobalObject.h>
+
+namespace JS {
+
+ConsoleObject::ConsoleObject(GlobalObject& global_object)
+ : Object(*global_object.object_prototype())
+{
+}
+
+void ConsoleObject::initialize(GlobalObject& global_object)
+{
+ auto& vm = this->vm();
+ Object::initialize(global_object);
+ define_native_function(vm.names.log, log);
+ define_native_function(vm.names.debug, debug);
+ define_native_function(vm.names.info, info);
+ define_native_function(vm.names.warn, warn);
+ define_native_function(vm.names.error, error);
+ define_native_function(vm.names.trace, trace);
+ define_native_function(vm.names.count, count);
+ define_native_function(vm.names.countReset, count_reset);
+ define_native_function(vm.names.clear, clear);
+}
+
+ConsoleObject::~ConsoleObject()
+{
+}
+
+JS_DEFINE_NATIVE_FUNCTION(ConsoleObject::log)
+{
+ return global_object.console().log();
+}
+
+JS_DEFINE_NATIVE_FUNCTION(ConsoleObject::debug)
+{
+ return global_object.console().debug();
+}
+
+JS_DEFINE_NATIVE_FUNCTION(ConsoleObject::info)
+{
+ return global_object.console().info();
+}
+
+JS_DEFINE_NATIVE_FUNCTION(ConsoleObject::warn)
+{
+ return global_object.console().warn();
+}
+
+JS_DEFINE_NATIVE_FUNCTION(ConsoleObject::error)
+{
+ return global_object.console().error();
+}
+
+JS_DEFINE_NATIVE_FUNCTION(ConsoleObject::trace)
+{
+ return global_object.console().trace();
+}
+
+JS_DEFINE_NATIVE_FUNCTION(ConsoleObject::count)
+{
+ return global_object.console().count();
+}
+
+JS_DEFINE_NATIVE_FUNCTION(ConsoleObject::count_reset)
+{
+ return global_object.console().count_reset();
+}
+
+JS_DEFINE_NATIVE_FUNCTION(ConsoleObject::clear)
+{
+ return global_object.console().clear();
+}
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/ConsoleObject.h b/Userland/Libraries/LibJS/Runtime/ConsoleObject.h
new file mode 100644
index 0000000000..07848aa44f
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/ConsoleObject.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * 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 ConsoleObject final : public Object {
+ JS_OBJECT(ConsoleObject, Object);
+
+public:
+ explicit ConsoleObject(GlobalObject&);
+ virtual void initialize(GlobalObject&) override;
+ virtual ~ConsoleObject() override;
+
+private:
+ JS_DECLARE_NATIVE_FUNCTION(log);
+ JS_DECLARE_NATIVE_FUNCTION(debug);
+ JS_DECLARE_NATIVE_FUNCTION(info);
+ JS_DECLARE_NATIVE_FUNCTION(warn);
+ JS_DECLARE_NATIVE_FUNCTION(error);
+ JS_DECLARE_NATIVE_FUNCTION(trace);
+ JS_DECLARE_NATIVE_FUNCTION(count);
+ JS_DECLARE_NATIVE_FUNCTION(count_reset);
+ JS_DECLARE_NATIVE_FUNCTION(clear);
+};
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/Date.cpp b/Userland/Libraries/LibJS/Runtime/Date.cpp
new file mode 100644
index 0000000000..ced1a58009
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/Date.cpp
@@ -0,0 +1,124 @@
+/*
+ * Copyright (c) 2020, Linus Groh <mail@linusgroh.de>
+ * 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 <AK/StringBuilder.h>
+#include <LibCore/DateTime.h>
+#include <LibJS/Heap/Heap.h>
+#include <LibJS/Runtime/Date.h>
+#include <LibJS/Runtime/GlobalObject.h>
+
+namespace JS {
+
+Date* Date::create(GlobalObject& global_object, Core::DateTime datetime, u16 milliseconds)
+{
+ return global_object.heap().allocate<Date>(global_object, datetime, milliseconds, *global_object.date_prototype());
+}
+
+Date::Date(Core::DateTime datetime, u16 milliseconds, Object& prototype)
+ : Object(prototype)
+ , m_datetime(datetime)
+ , m_milliseconds(milliseconds)
+{
+}
+
+Date::~Date()
+{
+}
+
+tm Date::to_utc_tm() const
+{
+ time_t timestamp = m_datetime.timestamp();
+ struct tm tm;
+ gmtime_r(&timestamp, &tm);
+ return tm;
+}
+
+int Date::utc_date() const
+{
+ return to_utc_tm().tm_mday;
+}
+
+int Date::utc_day() const
+{
+ return to_utc_tm().tm_wday;
+}
+
+int Date::utc_full_year() const
+{
+ return to_utc_tm().tm_year + 1900;
+}
+
+int Date::utc_hours() const
+{
+ return to_utc_tm().tm_hour;
+}
+
+int Date::utc_minutes() const
+{
+ return to_utc_tm().tm_min;
+}
+
+int Date::utc_month() const
+{
+ return to_utc_tm().tm_mon;
+}
+
+int Date::utc_seconds() const
+{
+ return to_utc_tm().tm_sec;
+}
+
+String Date::iso_date_string() const
+{
+ auto tm = to_utc_tm();
+ int year = tm.tm_year + 1900;
+ int month = tm.tm_mon + 1;
+
+ StringBuilder builder;
+ if (year < 0)
+ builder.appendf("-%06d", -year);
+ else if (year > 9999)
+ builder.appendf("+%06d", year);
+ else
+ builder.appendf("%04d", year);
+ builder.append('-');
+ builder.appendf("%02d", month);
+ builder.append('-');
+ builder.appendf("%02d", tm.tm_mday);
+ builder.append('T');
+ builder.appendf("%02d", tm.tm_hour);
+ builder.append(':');
+ builder.appendf("%02d", tm.tm_min);
+ builder.append(':');
+ builder.appendf("%02d", tm.tm_sec);
+ builder.append('.');
+ builder.appendf("%03d", m_milliseconds);
+ builder.append('Z');
+
+ return builder.build();
+}
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/Date.h b/Userland/Libraries/LibJS/Runtime/Date.h
new file mode 100644
index 0000000000..368cc91bc2
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/Date.h
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2020, Linus Groh <mail@linusgroh.de>
+ * 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 <LibCore/DateTime.h>
+#include <LibJS/Runtime/Object.h>
+
+namespace JS {
+
+class Date final : public Object {
+ JS_OBJECT(Date, Object);
+
+public:
+ static Date* create(GlobalObject&, Core::DateTime, u16 milliseconds);
+
+ Date(Core::DateTime datetime, u16 milliseconds, Object& prototype);
+ virtual ~Date() override;
+
+ Core::DateTime& datetime() { return m_datetime; }
+ const Core::DateTime& datetime() const { return m_datetime; }
+
+ int date() const { return datetime().day(); }
+ int day() const { return datetime().weekday(); }
+ int full_year() const { return datetime().year(); }
+ int hours() const { return datetime().hour(); }
+ u16 milliseconds() const { return m_milliseconds; }
+ int minutes() const { return datetime().minute(); }
+ int month() const { return datetime().month() - 1; }
+ int seconds() const { return datetime().second(); }
+ double time() const { return datetime().timestamp() * 1000.0 + milliseconds(); }
+ int year() const { return datetime().day(); }
+
+ int utc_date() const;
+ int utc_day() const;
+ int utc_full_year() const;
+ int utc_hours() const;
+ int utc_milliseconds() const { return milliseconds(); }
+ int utc_minutes() const;
+ int utc_month() const;
+ int utc_seconds() const;
+
+ String date_string() const { return m_datetime.to_string("%a %b %d %Y"); }
+ // FIXME: Deal with timezones once SerenityOS has a working tzset(3)
+ String time_string() const { return m_datetime.to_string("%T GMT+0000 (UTC)"); }
+ String string() const
+ {
+ return String::formatted("{} {}", date_string(), time_string());
+ }
+
+ String iso_date_string() const;
+
+ // FIXME: One day, implement real locale support. Until then, everyone gets what the Clock MenuApplet displays.
+ String locale_date_string() const { return m_datetime.to_string("%Y-%m-%d"); }
+ String locale_string() const { return m_datetime.to_string(); }
+ String locale_time_string() const { return m_datetime.to_string("%H:%M:%S"); }
+
+ virtual Value value_of() const override
+ {
+ return Value(static_cast<double>(m_datetime.timestamp() * 1000 + m_milliseconds));
+ }
+
+private:
+ tm to_utc_tm() const;
+
+ Core::DateTime m_datetime;
+ u16 m_milliseconds;
+};
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/DateConstructor.cpp b/Userland/Libraries/LibJS/Runtime/DateConstructor.cpp
new file mode 100644
index 0000000000..37b4d87865
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/DateConstructor.cpp
@@ -0,0 +1,252 @@
+/*
+ * Copyright (c) 2020, Linus Groh <mail@linusgroh.de>
+ * Copyright (c) 2020, Nico Weber <thakis@chromium.org>
+ * 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 <AK/GenericLexer.h>
+#include <LibCore/DateTime.h>
+#include <LibJS/Runtime/Date.h>
+#include <LibJS/Runtime/DateConstructor.h>
+#include <LibJS/Runtime/GlobalObject.h>
+#include <LibJS/Runtime/VM.h>
+#include <ctype.h>
+#include <sys/time.h>
+#include <time.h>
+
+namespace JS {
+
+static Value parse_simplified_iso8601(const String& iso_8601)
+{
+ // Date.parse() is allowed to accept many formats. We strictly only accept things matching
+ // http://www.ecma-international.org/ecma-262/#sec-date-time-string-format
+ GenericLexer lexer(iso_8601);
+ auto lex_n_digits = [&](size_t n, int& out) {
+ if (lexer.tell_remaining() < n)
+ return false;
+ int r = 0;
+ for (size_t i = 0; i < n; ++i) {
+ char ch = lexer.consume();
+ if (!isdigit(ch))
+ return false;
+ r = 10 * r + ch - '0';
+ }
+ out = r;
+ return true;
+ };
+
+ int year = -1, month = -1, day = -1;
+ int hours = -1, minutes = -1, seconds = -1, milliseconds = -1;
+ char timezone = -1;
+ int timezone_hours = -1, timezone_minutes = -1;
+ auto lex_year = [&]() {
+ if (lexer.consume_specific('+'))
+ return lex_n_digits(6, year);
+ if (lexer.consume_specific('-')) {
+ int absolute_year;
+ if (!lex_n_digits(6, absolute_year))
+ return false;
+ year = -absolute_year;
+ return true;
+ }
+ return lex_n_digits(4, year);
+ };
+ auto lex_month = [&]() { return lex_n_digits(2, month) && month >= 1 && month <= 12; };
+ auto lex_day = [&]() { return lex_n_digits(2, day) && day >= 1 && day <= 31; };
+ auto lex_date = [&]() { return lex_year() && (!lexer.consume_specific('-') || (lex_month() && (!lexer.consume_specific('-') || lex_day()))); };
+
+ auto lex_hours_minutes = [&](int& out_h, int& out_m) {
+ int h, m;
+ if (lex_n_digits(2, h) && h >= 0 && h <= 24 && lexer.consume_specific(':') && lex_n_digits(2, m) && m >= 0 && m <= 59) {
+ out_h = h;
+ out_m = m;
+ return true;
+ }
+ return false;
+ };
+ auto lex_seconds = [&]() { return lex_n_digits(2, seconds) && seconds >= 0 && seconds <= 59; };
+ auto lex_milliseconds = [&]() { return lex_n_digits(3, milliseconds); };
+ auto lex_seconds_milliseconds = [&]() { return lex_seconds() && (!lexer.consume_specific('.') || lex_milliseconds()); };
+ auto lex_timezone = [&]() {
+ if (lexer.consume_specific('+')) {
+ timezone = '+';
+ return lex_hours_minutes(timezone_hours, timezone_minutes);
+ }
+ if (lexer.consume_specific('-')) {
+ timezone = '-';
+ return lex_hours_minutes(timezone_hours, timezone_minutes);
+ }
+ if (lexer.consume_specific('Z'))
+ timezone = 'Z';
+ return true;
+ };
+ auto lex_time = [&]() { return lex_hours_minutes(hours, minutes) && (!lexer.consume_specific(':') || lex_seconds_milliseconds()) && lex_timezone(); };
+
+ if (!lex_date() || (lexer.consume_specific('T') && !lex_time()) || !lexer.is_eof()) {
+ return js_nan();
+ }
+
+ // We parsed a valid date simplified ISO 8601 string. Values not present in the string are -1.
+ ASSERT(year != -1); // A valid date string always has at least a year.
+ struct tm tm = {};
+ tm.tm_year = year - 1900;
+ tm.tm_mon = month == -1 ? 0 : month - 1;
+ tm.tm_mday = day == -1 ? 1 : day;
+ tm.tm_hour = hours == -1 ? 0 : hours;
+ tm.tm_min = minutes == -1 ? 0 : minutes;
+ tm.tm_sec = seconds == -1 ? 0 : seconds;
+
+ // http://www.ecma-international.org/ecma-262/#sec-date.parse:
+ // "When the UTC offset representation is absent, date-only forms are interpreted as a UTC time and date-time forms are interpreted as a local time."
+ time_t timestamp;
+ if (timezone != -1 || hours == -1)
+ timestamp = timegm(&tm);
+ else
+ timestamp = mktime(&tm);
+
+ if (timezone == '-')
+ timestamp += (timezone_hours * 60 + timezone_minutes) * 60;
+ else if (timezone == '+')
+ timestamp -= (timezone_hours * 60 + timezone_minutes) * 60;
+
+ // FIXME: reject timestamp if resulting value wouldn't fit in a double
+
+ if (milliseconds == -1)
+ milliseconds = 0;
+ return Value(1000.0 * timestamp + milliseconds);
+}
+
+DateConstructor::DateConstructor(GlobalObject& global_object)
+ : NativeFunction(vm().names.Date, *global_object.function_prototype())
+{
+}
+
+void DateConstructor::initialize(GlobalObject& global_object)
+{
+ auto& vm = this->vm();
+ NativeFunction::initialize(global_object);
+ define_property(vm.names.prototype, global_object.date_prototype(), 0);
+ define_property(vm.names.length, Value(7), Attribute::Configurable);
+
+ define_native_function(vm.names.now, now, 0, Attribute::Writable | Attribute::Configurable);
+ define_native_function(vm.names.parse, parse, 1, Attribute::Writable | Attribute::Configurable);
+ define_native_function(vm.names.UTC, utc, 1, Attribute::Writable | Attribute::Configurable);
+}
+
+DateConstructor::~DateConstructor()
+{
+}
+
+Value DateConstructor::call()
+{
+ auto date = construct(*this);
+ if (!date.is_object())
+ return {};
+ return js_string(heap(), static_cast<Date&>(date.as_object()).string());
+}
+
+Value DateConstructor::construct(Function&)
+{
+ if (vm().argument_count() == 0) {
+ struct timeval tv;
+ gettimeofday(&tv, nullptr);
+ auto datetime = Core::DateTime::now();
+ auto milliseconds = static_cast<u16>(tv.tv_usec / 1000);
+ return Date::create(global_object(), datetime, milliseconds);
+ }
+ if (vm().argument_count() == 1) {
+ auto value = vm().argument(0);
+ if (value.is_string())
+ value = parse_simplified_iso8601(value.as_string().string());
+ // A timestamp since the epoch, in UTC.
+ // FIXME: Date() probably should use a double as internal representation, so that NaN arguments and larger offsets are handled correctly.
+ double value_as_double = value.to_double(global_object());
+ auto datetime = Core::DateTime::from_timestamp(static_cast<time_t>(value_as_double / 1000));
+ auto milliseconds = static_cast<u16>(fmod(value_as_double, 1000));
+ return Date::create(global_object(), datetime, milliseconds);
+ }
+ // A date/time in components, in local time.
+ // FIXME: This doesn't construct an "Invalid Date" object if one of the parameters is NaN.
+ auto arg_or = [this](size_t i, i32 fallback) { return vm().argument_count() > i ? vm().argument(i).to_i32(global_object()) : fallback; };
+ int year = vm().argument(0).to_i32(global_object());
+ int month_index = vm().argument(1).to_i32(global_object());
+ int day = arg_or(2, 1);
+ int hours = arg_or(3, 0);
+ int minutes = arg_or(4, 0);
+ int seconds = arg_or(5, 0);
+ int milliseconds = arg_or(6, 0);
+
+ seconds += milliseconds / 1000;
+ milliseconds %= 1000;
+ if (milliseconds < 0) {
+ seconds -= 1;
+ milliseconds += 1000;
+ }
+
+ if (year >= 0 && year <= 99)
+ year += 1900;
+ int month = month_index + 1;
+ auto datetime = Core::DateTime::create(year, month, day, hours, minutes, seconds);
+ return Date::create(global_object(), datetime, milliseconds);
+}
+
+JS_DEFINE_NATIVE_FUNCTION(DateConstructor::now)
+{
+ struct timeval tv;
+ gettimeofday(&tv, nullptr);
+ return Value(tv.tv_sec * 1000.0 + tv.tv_usec / 1000.0);
+}
+
+JS_DEFINE_NATIVE_FUNCTION(DateConstructor::parse)
+{
+ if (!vm.argument_count())
+ return js_nan();
+
+ auto iso_8601 = vm.argument(0).to_string(global_object);
+ if (vm.exception())
+ return js_nan();
+
+ return parse_simplified_iso8601(iso_8601);
+}
+
+JS_DEFINE_NATIVE_FUNCTION(DateConstructor::utc)
+{
+ auto arg_or = [&vm, &global_object](size_t i, i32 fallback) { return vm.argument_count() > i ? vm.argument(i).to_i32(global_object) : fallback; };
+ int year = vm.argument(0).to_i32(global_object);
+ if (year >= 0 && year <= 99)
+ year += 1900;
+
+ struct tm tm = {};
+ tm.tm_year = year - 1900;
+ tm.tm_mon = arg_or(1, 0); // 0-based in both tm and JavaScript
+ tm.tm_mday = arg_or(2, 1);
+ tm.tm_hour = arg_or(3, 0);
+ tm.tm_min = arg_or(4, 0);
+ tm.tm_sec = arg_or(5, 0);
+ // timegm() doesn't read tm.tm_wday and tm.tm_yday, no need to fill them in.
+
+ int milliseconds = arg_or(6, 0);
+ return Value(1000.0 * timegm(&tm) + milliseconds);
+}
+}
diff --git a/Userland/Libraries/LibJS/Runtime/DateConstructor.h b/Userland/Libraries/LibJS/Runtime/DateConstructor.h
new file mode 100644
index 0000000000..cc16ee8e4a
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/DateConstructor.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2020, Linus Groh <mail@linusgroh.de>
+ * 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/NativeFunction.h>
+
+namespace JS {
+
+class DateConstructor final : public NativeFunction {
+ JS_OBJECT(DateConstructor, NativeFunction);
+
+public:
+ explicit DateConstructor(GlobalObject&);
+ virtual void initialize(GlobalObject&) override;
+ virtual ~DateConstructor() override;
+
+ virtual Value call() override;
+ virtual Value construct(Function& new_target) override;
+
+private:
+ virtual bool has_constructor() const override { return true; }
+
+ JS_DECLARE_NATIVE_FUNCTION(now);
+ JS_DECLARE_NATIVE_FUNCTION(parse);
+ JS_DECLARE_NATIVE_FUNCTION(utc);
+};
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/DatePrototype.cpp b/Userland/Libraries/LibJS/Runtime/DatePrototype.cpp
new file mode 100644
index 0000000000..72fea9ca25
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/DatePrototype.cpp
@@ -0,0 +1,299 @@
+/*
+ * Copyright (c) 2020, Linus Groh <mail@linusgroh.de>
+ * 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 <AK/Function.h>
+#include <AK/String.h>
+#include <LibCore/DateTime.h>
+#include <LibJS/Runtime/Date.h>
+#include <LibJS/Runtime/DatePrototype.h>
+#include <LibJS/Runtime/Error.h>
+#include <LibJS/Runtime/GlobalObject.h>
+#include <LibJS/Runtime/Value.h>
+
+namespace JS {
+
+static Date* typed_this(VM& vm, GlobalObject& global_object)
+{
+ auto* this_object = vm.this_value(global_object).to_object(global_object);
+ if (!this_object)
+ return nullptr;
+ if (!is<Date>(this_object)) {
+ vm.throw_exception<TypeError>(global_object, ErrorType::NotA, "Date");
+ return nullptr;
+ }
+ return static_cast<Date*>(this_object);
+}
+
+DatePrototype::DatePrototype(GlobalObject& global_object)
+ : Object(*global_object.object_prototype())
+{
+}
+
+void DatePrototype::initialize(GlobalObject& global_object)
+{
+ auto& vm = this->vm();
+ Object::initialize(global_object);
+ u8 attr = Attribute::Writable | Attribute::Configurable;
+ define_native_function(vm.names.getDate, get_date, 0, attr);
+ define_native_function(vm.names.getDay, get_day, 0, attr);
+ define_native_function(vm.names.getFullYear, get_full_year, 0, attr);
+ define_native_function(vm.names.getHours, get_hours, 0, attr);
+ define_native_function(vm.names.getMilliseconds, get_milliseconds, 0, attr);
+ define_native_function(vm.names.getMinutes, get_minutes, 0, attr);
+ define_native_function(vm.names.getMonth, get_month, 0, attr);
+ define_native_function(vm.names.getSeconds, get_seconds, 0, attr);
+ define_native_function(vm.names.getTime, get_time, 0, attr);
+ define_native_function(vm.names.getUTCDate, get_utc_date, 0, attr);
+ define_native_function(vm.names.getUTCDay, get_utc_day, 0, attr);
+ define_native_function(vm.names.getUTCFullYear, get_utc_full_year, 0, attr);
+ define_native_function(vm.names.getUTCHours, get_utc_hours, 0, attr);
+ define_native_function(vm.names.getUTCMilliseconds, get_utc_milliseconds, 0, attr);
+ define_native_function(vm.names.getUTCMinutes, get_utc_minutes, 0, attr);
+ define_native_function(vm.names.getUTCMonth, get_utc_month, 0, attr);
+ define_native_function(vm.names.getUTCSeconds, get_utc_seconds, 0, attr);
+ define_native_function(vm.names.toDateString, to_date_string, 0, attr);
+ define_native_function(vm.names.toISOString, to_iso_string, 0, attr);
+ define_native_function(vm.names.toLocaleDateString, to_locale_date_string, 0, attr);
+ define_native_function(vm.names.toLocaleString, to_locale_string, 0, attr);
+ define_native_function(vm.names.toLocaleTimeString, to_locale_time_string, 0, attr);
+ define_native_function(vm.names.toTimeString, to_time_string, 0, attr);
+ define_native_function(vm.names.toString, to_string, 0, attr);
+
+ // Aliases.
+ define_native_function(vm.names.valueOf, get_time, 0, attr);
+ // toJSON() isn't quite an alias for toISOString():
+ // - it returns null instead of throwing RangeError
+ // - its .length is 1, not 0
+ // - it can be transferred to other prototypes
+}
+
+DatePrototype::~DatePrototype()
+{
+}
+
+JS_DEFINE_NATIVE_FUNCTION(DatePrototype::get_date)
+{
+ auto* this_object = typed_this(vm, global_object);
+ if (!this_object)
+ return {};
+ return Value(static_cast<double>(this_object->date()));
+}
+
+JS_DEFINE_NATIVE_FUNCTION(DatePrototype::get_day)
+{
+ auto* this_object = typed_this(vm, global_object);
+ if (!this_object)
+ return {};
+ return Value(static_cast<double>(this_object->day()));
+}
+
+JS_DEFINE_NATIVE_FUNCTION(DatePrototype::get_full_year)
+{
+ auto* this_object = typed_this(vm, global_object);
+ if (!this_object)
+ return {};
+ return Value(static_cast<double>(this_object->full_year()));
+}
+
+JS_DEFINE_NATIVE_FUNCTION(DatePrototype::get_hours)
+{
+ auto* this_object = typed_this(vm, global_object);
+ if (!this_object)
+ return {};
+ return Value(static_cast<double>(this_object->hours()));
+}
+
+JS_DEFINE_NATIVE_FUNCTION(DatePrototype::get_milliseconds)
+{
+ auto* this_object = typed_this(vm, global_object);
+ if (!this_object)
+ return {};
+ return Value(static_cast<double>(this_object->milliseconds()));
+}
+
+JS_DEFINE_NATIVE_FUNCTION(DatePrototype::get_minutes)
+{
+ auto* this_object = typed_this(vm, global_object);
+ if (!this_object)
+ return {};
+ return Value(static_cast<double>(this_object->minutes()));
+}
+
+JS_DEFINE_NATIVE_FUNCTION(DatePrototype::get_month)
+{
+ auto* this_object = typed_this(vm, global_object);
+ if (!this_object)
+ return {};
+ return Value(static_cast<double>(this_object->month()));
+}
+
+JS_DEFINE_NATIVE_FUNCTION(DatePrototype::get_seconds)
+{
+ auto* this_object = typed_this(vm, global_object);
+ if (!this_object)
+ return {};
+ return Value(static_cast<double>(this_object->seconds()));
+}
+
+JS_DEFINE_NATIVE_FUNCTION(DatePrototype::get_time)
+{
+ auto* this_object = typed_this(vm, global_object);
+ if (!this_object)
+ return {};
+ return Value(this_object->time());
+}
+
+JS_DEFINE_NATIVE_FUNCTION(DatePrototype::get_utc_date)
+{
+ auto* this_object = typed_this(vm, global_object);
+ if (!this_object)
+ return {};
+ return Value(static_cast<double>(this_object->utc_date()));
+}
+
+JS_DEFINE_NATIVE_FUNCTION(DatePrototype::get_utc_day)
+{
+ auto* this_object = typed_this(vm, global_object);
+ if (!this_object)
+ return {};
+ return Value(static_cast<double>(this_object->utc_day()));
+}
+
+JS_DEFINE_NATIVE_FUNCTION(DatePrototype::get_utc_full_year)
+{
+ auto* this_object = typed_this(vm, global_object);
+ if (!this_object)
+ return {};
+ return Value(static_cast<double>(this_object->utc_full_year()));
+}
+
+JS_DEFINE_NATIVE_FUNCTION(DatePrototype::get_utc_hours)
+{
+ auto* this_object = typed_this(vm, global_object);
+ if (!this_object)
+ return {};
+ return Value(static_cast<double>(this_object->utc_hours()));
+}
+
+JS_DEFINE_NATIVE_FUNCTION(DatePrototype::get_utc_milliseconds)
+{
+ auto* this_object = typed_this(vm, global_object);
+ if (!this_object)
+ return {};
+ return Value(static_cast<double>(this_object->utc_milliseconds()));
+}
+
+JS_DEFINE_NATIVE_FUNCTION(DatePrototype::get_utc_month)
+{
+ auto* this_object = typed_this(vm, global_object);
+ if (!this_object)
+ return {};
+ return Value(static_cast<double>(this_object->utc_month()));
+}
+
+JS_DEFINE_NATIVE_FUNCTION(DatePrototype::get_utc_minutes)
+{
+ auto* this_object = typed_this(vm, global_object);
+ if (!this_object)
+ return {};
+ return Value(static_cast<double>(this_object->utc_minutes()));
+}
+
+JS_DEFINE_NATIVE_FUNCTION(DatePrototype::get_utc_seconds)
+{
+ auto* this_object = typed_this(vm, global_object);
+ if (!this_object)
+ return {};
+ return Value(static_cast<double>(this_object->utc_seconds()));
+}
+
+JS_DEFINE_NATIVE_FUNCTION(DatePrototype::to_date_string)
+{
+ auto* this_object = typed_this(vm, global_object);
+ if (!this_object)
+ return {};
+ auto string = this_object->date_string();
+ return js_string(vm, move(string));
+}
+
+JS_DEFINE_NATIVE_FUNCTION(DatePrototype::to_iso_string)
+{
+ auto* this_object = typed_this(vm, global_object);
+ if (!this_object)
+ return {};
+ auto string = this_object->iso_date_string();
+ return js_string(vm, move(string));
+}
+
+JS_DEFINE_NATIVE_FUNCTION(DatePrototype::to_locale_date_string)
+{
+ auto* this_object = typed_this(vm, global_object);
+ if (!this_object)
+ return {};
+ // FIXME: Optional locales, options params.
+ auto string = this_object->locale_date_string();
+ return js_string(vm, move(string));
+}
+
+JS_DEFINE_NATIVE_FUNCTION(DatePrototype::to_locale_string)
+{
+ auto* this_object = typed_this(vm, global_object);
+ if (!this_object)
+ return {};
+ // FIXME: Optional locales, options params.
+ auto string = this_object->locale_string();
+ return js_string(vm, move(string));
+}
+
+JS_DEFINE_NATIVE_FUNCTION(DatePrototype::to_locale_time_string)
+{
+ auto* this_object = typed_this(vm, global_object);
+ if (!this_object)
+ return {};
+ // FIXME: Optional locales, options params.
+ auto string = this_object->locale_time_string();
+ return js_string(vm, move(string));
+}
+
+JS_DEFINE_NATIVE_FUNCTION(DatePrototype::to_time_string)
+{
+ auto* this_object = typed_this(vm, global_object);
+ if (!this_object)
+ return {};
+ auto string = this_object->time_string();
+ return js_string(vm, move(string));
+}
+
+JS_DEFINE_NATIVE_FUNCTION(DatePrototype::to_string)
+{
+ auto* this_object = typed_this(vm, global_object);
+ if (!this_object)
+ return {};
+ auto string = this_object->string();
+ return js_string(vm, move(string));
+}
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/DatePrototype.h b/Userland/Libraries/LibJS/Runtime/DatePrototype.h
new file mode 100644
index 0000000000..5c8c8bebd4
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/DatePrototype.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2020, Linus Groh <mail@linusgroh.de>
+ * 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 DatePrototype final : public Object {
+ JS_OBJECT(DatePrototype, Object);
+
+public:
+ explicit DatePrototype(GlobalObject&);
+ virtual void initialize(GlobalObject&) override;
+ virtual ~DatePrototype() override;
+
+private:
+ JS_DECLARE_NATIVE_FUNCTION(get_date);
+ JS_DECLARE_NATIVE_FUNCTION(get_day);
+ JS_DECLARE_NATIVE_FUNCTION(get_full_year);
+ JS_DECLARE_NATIVE_FUNCTION(get_hours);
+ JS_DECLARE_NATIVE_FUNCTION(get_milliseconds);
+ JS_DECLARE_NATIVE_FUNCTION(get_minutes);
+ JS_DECLARE_NATIVE_FUNCTION(get_month);
+ JS_DECLARE_NATIVE_FUNCTION(get_seconds);
+ JS_DECLARE_NATIVE_FUNCTION(get_time);
+ JS_DECLARE_NATIVE_FUNCTION(get_utc_date);
+ JS_DECLARE_NATIVE_FUNCTION(get_utc_day);
+ JS_DECLARE_NATIVE_FUNCTION(get_utc_full_year);
+ JS_DECLARE_NATIVE_FUNCTION(get_utc_hours);
+ JS_DECLARE_NATIVE_FUNCTION(get_utc_milliseconds);
+ JS_DECLARE_NATIVE_FUNCTION(get_utc_minutes);
+ JS_DECLARE_NATIVE_FUNCTION(get_utc_month);
+ JS_DECLARE_NATIVE_FUNCTION(get_utc_seconds);
+ JS_DECLARE_NATIVE_FUNCTION(to_date_string);
+ JS_DECLARE_NATIVE_FUNCTION(to_iso_string);
+ JS_DECLARE_NATIVE_FUNCTION(to_locale_date_string);
+ JS_DECLARE_NATIVE_FUNCTION(to_locale_string);
+ JS_DECLARE_NATIVE_FUNCTION(to_locale_time_string);
+ JS_DECLARE_NATIVE_FUNCTION(to_time_string);
+ JS_DECLARE_NATIVE_FUNCTION(to_string);
+};
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/Error.cpp b/Userland/Libraries/LibJS/Runtime/Error.cpp
new file mode 100644
index 0000000000..7f30ab872c
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/Error.cpp
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * 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/Error.h>
+#include <LibJS/Runtime/GlobalObject.h>
+
+namespace JS {
+
+Error* Error::create(GlobalObject& global_object, const FlyString& name, const String& message)
+{
+ return global_object.heap().allocate<Error>(global_object, name, message, *global_object.error_prototype());
+}
+
+Error::Error(const FlyString& name, const String& message, Object& prototype)
+ : Object(prototype)
+ , m_name(name)
+ , m_message(message)
+{
+}
+
+Error::~Error()
+{
+}
+
+#define __JS_ENUMERATE(ClassName, snake_name, PrototypeName, ConstructorName, ArrayType) \
+ ClassName* ClassName::create(GlobalObject& global_object, const String& message) \
+ { \
+ return global_object.heap().allocate<ClassName>(global_object, message, *global_object.snake_name##_prototype()); \
+ } \
+ ClassName::ClassName(const String& message, Object& prototype) \
+ : Error(vm().names.ClassName, message, prototype) \
+ { \
+ } \
+ ClassName::~ClassName() { }
+
+JS_ENUMERATE_ERROR_SUBCLASSES
+#undef __JS_ENUMERATE
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/Error.h b/Userland/Libraries/LibJS/Runtime/Error.h
new file mode 100644
index 0000000000..3605825f1d
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/Error.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * 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 <AK/FlyString.h>
+#include <LibJS/Runtime/Object.h>
+
+namespace JS {
+
+class Error : public Object {
+ JS_OBJECT(Error, Object);
+
+public:
+ static Error* create(GlobalObject&, const FlyString& name, const String& message);
+
+ Error(const FlyString& name, const String& message, Object& prototype);
+ virtual ~Error() override;
+
+ const FlyString& name() const { return m_name; }
+ const String& message() const { return m_message; }
+
+ void set_name(const FlyString& name) { m_name = name; }
+
+private:
+ FlyString m_name;
+ String m_message;
+};
+
+#define DECLARE_ERROR_SUBCLASS(ClassName, snake_name, PrototypeName, ConstructorName) \
+ class ClassName final : public Error { \
+ JS_OBJECT(ClassName, Error); \
+ \
+ public: \
+ static ClassName* create(GlobalObject&, const String& message); \
+ \
+ ClassName(const String& message, Object& prototype); \
+ virtual ~ClassName() override; \
+ };
+
+#define __JS_ENUMERATE(ClassName, snake_name, PrototypeName, ConstructorName, ArrayType) \
+ DECLARE_ERROR_SUBCLASS(ClassName, snake_name, PrototypeName, ConstructorName)
+JS_ENUMERATE_ERROR_SUBCLASSES
+#undef __JS_ENUMERATE
+}
diff --git a/Userland/Libraries/LibJS/Runtime/ErrorConstructor.cpp b/Userland/Libraries/LibJS/Runtime/ErrorConstructor.cpp
new file mode 100644
index 0000000000..549c74855c
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/ErrorConstructor.cpp
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2020, Linus Groh <mail@linusgroh.de>
+ * 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/Error.h>
+#include <LibJS/Runtime/ErrorConstructor.h>
+#include <LibJS/Runtime/GlobalObject.h>
+
+namespace JS {
+
+ErrorConstructor::ErrorConstructor(GlobalObject& global_object)
+ : NativeFunction(vm().names.Error, *global_object.function_prototype())
+{
+}
+
+void ErrorConstructor::initialize(GlobalObject& global_object)
+{
+ auto& vm = this->vm();
+ NativeFunction::initialize(global_object);
+ define_property(vm.names.prototype, global_object.error_prototype(), 0);
+ define_property(vm.names.length, Value(1), Attribute::Configurable);
+}
+
+ErrorConstructor::~ErrorConstructor()
+{
+}
+
+Value ErrorConstructor::call()
+{
+ return construct(*this);
+}
+
+Value ErrorConstructor::construct(Function&)
+{
+ auto& vm = this->vm();
+ String message = "";
+ if (!vm.call_frame().arguments.is_empty() && !vm.call_frame().arguments[0].is_undefined()) {
+ message = vm.call_frame().arguments[0].to_string(global_object());
+ if (vm.exception())
+ return {};
+ }
+ return Error::create(global_object(), vm.names.Error, message);
+}
+
+#define __JS_ENUMERATE(ClassName, snake_name, PrototypeName, ConstructorName, ArrayType) \
+ ConstructorName::ConstructorName(GlobalObject& global_object) \
+ : NativeFunction(*global_object.function_prototype()) \
+ { \
+ } \
+ void ConstructorName::initialize(GlobalObject& global_object) \
+ { \
+ auto& vm = this->vm(); \
+ NativeFunction::initialize(global_object); \
+ define_property(vm.names.prototype, global_object.snake_name##_prototype(), 0); \
+ define_property(vm.names.length, Value(1), Attribute::Configurable); \
+ } \
+ ConstructorName::~ConstructorName() { } \
+ Value ConstructorName::call() \
+ { \
+ return construct(*this); \
+ } \
+ Value ConstructorName::construct(Function&) \
+ { \
+ String message = ""; \
+ if (!vm().call_frame().arguments.is_empty() && !vm().call_frame().arguments[0].is_undefined()) { \
+ message = vm().call_frame().arguments[0].to_string(global_object()); \
+ if (vm().exception()) \
+ return {}; \
+ } \
+ return ClassName::create(global_object(), message); \
+ }
+
+JS_ENUMERATE_ERROR_SUBCLASSES
+#undef __JS_ENUMERATE
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/ErrorConstructor.h b/Userland/Libraries/LibJS/Runtime/ErrorConstructor.h
new file mode 100644
index 0000000000..2626623aaa
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/ErrorConstructor.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2020, Linus Groh <mail@linusgroh.de>
+ * 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/Error.h>
+#include <LibJS/Runtime/NativeFunction.h>
+
+namespace JS {
+
+class ErrorConstructor final : public NativeFunction {
+ JS_OBJECT(ErrorConstructor, NativeFunction);
+
+public:
+ explicit ErrorConstructor(GlobalObject&);
+ virtual void initialize(GlobalObject&) override;
+ virtual ~ErrorConstructor() override;
+
+ virtual Value call() override;
+ virtual Value construct(Function& new_target) override;
+
+private:
+ virtual bool has_constructor() const override { return true; }
+};
+
+#define DECLARE_ERROR_SUBCLASS_CONSTRUCTOR(ClassName, snake_name, PrototypeName, ConstructorName) \
+ class ConstructorName final : public NativeFunction { \
+ JS_OBJECT(ConstructorName, NativeFunction); \
+ \
+ public: \
+ explicit ConstructorName(GlobalObject&); \
+ virtual void initialize(GlobalObject&) override; \
+ virtual ~ConstructorName() override; \
+ virtual Value call() override; \
+ virtual Value construct(Function& new_target) override; \
+ \
+ private: \
+ virtual bool has_constructor() const override { return true; } \
+ };
+
+#define __JS_ENUMERATE(ClassName, snake_name, PrototypeName, ConstructorName, ArrayType) \
+ DECLARE_ERROR_SUBCLASS_CONSTRUCTOR(ClassName, snake_name, PrototypeName, ConstructorName)
+JS_ENUMERATE_ERROR_SUBCLASSES
+#undef __JS_ENUMERATE
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/ErrorPrototype.cpp b/Userland/Libraries/LibJS/Runtime/ErrorPrototype.cpp
new file mode 100644
index 0000000000..68affb5745
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/ErrorPrototype.cpp
@@ -0,0 +1,140 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * 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 <AK/Function.h>
+#include <LibJS/Heap/Heap.h>
+#include <LibJS/Runtime/Error.h>
+#include <LibJS/Runtime/ErrorPrototype.h>
+#include <LibJS/Runtime/GlobalObject.h>
+#include <LibJS/Runtime/PrimitiveString.h>
+#include <LibJS/Runtime/Value.h>
+
+namespace JS {
+
+ErrorPrototype::ErrorPrototype(GlobalObject& global_object)
+ : Object(*global_object.object_prototype())
+{
+}
+
+void ErrorPrototype::initialize(GlobalObject& global_object)
+{
+ auto& vm = this->vm();
+ Object::initialize(global_object);
+ u8 attr = Attribute::Writable | Attribute::Configurable;
+ define_native_property(vm.names.name, name_getter, name_setter, attr);
+ define_native_property(vm.names.message, message_getter, {}, attr);
+ define_native_function(vm.names.toString, to_string, 0, attr);
+}
+
+ErrorPrototype::~ErrorPrototype()
+{
+}
+
+JS_DEFINE_NATIVE_GETTER(ErrorPrototype::name_getter)
+{
+ auto* this_object = vm.this_value(global_object).to_object(global_object);
+ if (!this_object)
+ return {};
+ if (!is<Error>(this_object)) {
+ vm.throw_exception<TypeError>(global_object, ErrorType::NotAn, "Error");
+ return {};
+ }
+ return js_string(vm, static_cast<const Error*>(this_object)->name());
+}
+
+JS_DEFINE_NATIVE_SETTER(ErrorPrototype::name_setter)
+{
+ auto* this_object = vm.this_value(global_object).to_object(global_object);
+ if (!this_object)
+ return;
+ if (!is<Error>(this_object)) {
+ vm.throw_exception<TypeError>(global_object, ErrorType::NotAn, "Error");
+ return;
+ }
+ auto name = value.to_string(global_object);
+ if (vm.exception())
+ return;
+ static_cast<Error*>(this_object)->set_name(name);
+}
+
+JS_DEFINE_NATIVE_GETTER(ErrorPrototype::message_getter)
+{
+ auto* this_object = vm.this_value(global_object).to_object(global_object);
+ if (!this_object)
+ return {};
+ if (!is<Error>(this_object)) {
+ vm.throw_exception<TypeError>(global_object, ErrorType::NotAn, "Error");
+ return {};
+ }
+ return js_string(vm, static_cast<const Error*>(this_object)->message());
+}
+
+JS_DEFINE_NATIVE_FUNCTION(ErrorPrototype::to_string)
+{
+ if (!vm.this_value(global_object).is_object()) {
+ vm.throw_exception<TypeError>(global_object, ErrorType::NotAnObject, vm.this_value(global_object).to_string_without_side_effects());
+ return {};
+ }
+ auto& this_object = vm.this_value(global_object).as_object();
+
+ String name = "Error";
+ auto name_property = this_object.get(vm.names.name);
+ if (vm.exception())
+ return {};
+ if (!name_property.is_empty() && !name_property.is_undefined()) {
+ name = name_property.to_string(global_object);
+ if (vm.exception())
+ return {};
+ }
+
+ String message = "";
+ auto message_property = this_object.get(vm.names.message);
+ if (vm.exception())
+ return {};
+ if (!message_property.is_empty() && !message_property.is_undefined()) {
+ message = message_property.to_string(global_object);
+ if (vm.exception())
+ return {};
+ }
+
+ if (name.length() == 0)
+ return js_string(vm, message);
+ if (message.length() == 0)
+ return js_string(vm, name);
+ return js_string(vm, String::formatted("{}: {}", name, message));
+}
+
+#define __JS_ENUMERATE(ClassName, snake_name, PrototypeName, ConstructorName, ArrayType) \
+ PrototypeName::PrototypeName(GlobalObject& global_object) \
+ : Object(*global_object.error_prototype()) \
+ { \
+ } \
+ PrototypeName::~PrototypeName() { }
+
+JS_ENUMERATE_ERROR_SUBCLASSES
+#undef __JS_ENUMERATE
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/ErrorPrototype.h b/Userland/Libraries/LibJS/Runtime/ErrorPrototype.h
new file mode 100644
index 0000000000..3b461942ab
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/ErrorPrototype.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * 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/Error.h>
+
+namespace JS {
+
+class ErrorPrototype final : public Object {
+ JS_OBJECT(ErrorPrototype, Object);
+
+public:
+ explicit ErrorPrototype(GlobalObject&);
+ virtual void initialize(GlobalObject&) override;
+ virtual ~ErrorPrototype() override;
+
+private:
+ JS_DECLARE_NATIVE_FUNCTION(to_string);
+
+ JS_DECLARE_NATIVE_GETTER(name_getter);
+ JS_DECLARE_NATIVE_SETTER(name_setter);
+
+ JS_DECLARE_NATIVE_GETTER(message_getter);
+};
+
+#define DECLARE_ERROR_SUBCLASS_PROTOTYPE(ClassName, snake_name, PrototypeName, ConstructorName) \
+ class PrototypeName final : public Object { \
+ JS_OBJECT(PrototypeName, Object); \
+ \
+ public: \
+ explicit PrototypeName(GlobalObject&); \
+ virtual void initialize(GlobalObject&) override { } \
+ virtual ~PrototypeName() override; \
+ };
+
+#define __JS_ENUMERATE(ClassName, snake_name, PrototypeName, ConstructorName, ArrayType) \
+ DECLARE_ERROR_SUBCLASS_PROTOTYPE(ClassName, snake_name, PrototypeName, ConstructorName)
+JS_ENUMERATE_ERROR_SUBCLASSES
+#undef __JS_ENUMERATE
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/ErrorTypes.cpp b/Userland/Libraries/LibJS/Runtime/ErrorTypes.cpp
new file mode 100644
index 0000000000..f71172b68a
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/ErrorTypes.cpp
@@ -0,0 +1,36 @@
+/*
+ * 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/ErrorTypes.h>
+
+namespace JS {
+
+#define __ENUMERATE_JS_ERROR(name, message) \
+ const ErrorType ErrorType::name = ErrorType(message);
+JS_ENUMERATE_ERROR_TYPES(__ENUMERATE_JS_ERROR)
+#undef __ENUMERATE_JS_ERROR
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/ErrorTypes.h b/Userland/Libraries/LibJS/Runtime/ErrorTypes.h
new file mode 100644
index 0000000000..e406d4377f
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/ErrorTypes.h
@@ -0,0 +1,193 @@
+/*
+ * 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
+
+#define JS_ENUMERATE_ERROR_TYPES(M) \
+ M(ArrayMaxSize, "Maximum array size exceeded") \
+ M(ArrayPrototypeOneArg, "Array.prototype.{}() requires at least one argument") \
+ M(AccessorBadField, "Accessor descriptor's '{}' field must be a function or undefined") \
+ M(AccessorValueOrWritable, "Accessor property descriptor cannot specify a value or writable key") \
+ M(BigIntBadOperator, "Cannot use {} operator with BigInt") \
+ M(BigIntBadOperatorOtherType, "Cannot use {} operator with BigInt and other type") \
+ M(BigIntIntArgument, "BigInt argument must be an integer") \
+ M(BigIntInvalidValue, "Invalid value for BigInt: {}") \
+ M(ClassConstructorWithoutNew, "Class constructor {} must be called with 'new'") \
+ M(ClassIsAbstract, "Abstract class {} cannot be constructed directly") \
+ M(ClassDoesNotExtendAConstructorOrNull, "Class extends value {} is not a constructor or null") \
+ M(ConstructorWithoutNew, "{} constructor must be called with 'new'") \
+ M(Convert, "Cannot convert {} to {}") \
+ M(ConvertUndefinedToObject, "Cannot convert undefined to object") \
+ M(DescChangeNonConfigurable, "Cannot change attributes of non-configurable property '{}'") \
+ M(FunctionArgsNotObject, "Argument array must be an object") \
+ M(InOperatorWithObject, "'in' operator must be used on an object") \
+ M(InstanceOfOperatorBadPrototype, "'prototype' property of {} is not an object") \
+ M(InvalidAssignToConst, "Invalid assignment to const variable") \
+ M(InvalidIndex, "Index must be a positive integer") \
+ M(InvalidLeftHandAssignment, "Invalid left-hand side in assignment") \
+ M(InvalidLength, "Invalid {} length") \
+ M(InvalidRadix, "Radix must be an integer no less than 2, and no greater than 36") \
+ M(IsNotA, "{} is not a {}") \
+ M(IsNotAEvaluatedFrom, "{} is not a {} (evaluated from '{}')") \
+ M(IterableNextBadReturn, "iterator.next() returned a non-object value") \
+ M(IterableNextNotAFunction, "'next' property on returned object from Symbol.iterator method is " \
+ "not a function") \
+ M(JsonBigInt, "Cannot serialize BigInt value to JSON") \
+ M(JsonCircular, "Cannot stringify circular object") \
+ M(JsonMalformed, "Malformed JSON string") \
+ M(NotA, "Not a {} object") \
+ M(NotAConstructor, "{} is not a constructor") \
+ M(NotAFunction, "{} is not a function") \
+ M(NotAFunctionNoParam, "Not a function") \
+ M(NotAn, "Not an {} object") \
+ M(NotAnObject, "{} is not an object") \
+ M(NotASymbol, "{} is not a symbol") \
+ M(NotIterable, "{} is not iterable") \
+ M(NonExtensibleDefine, "Cannot define property {} on non-extensible object") \
+ M(NumberIncompatibleThis, "Number.prototype.{} method called with incompatible this target") \
+ M(ObjectDefinePropertyReturnedFalse, "Object's [[DefineProperty]] method returned false") \
+ M(ObjectSetPrototypeOfReturnedFalse, "Object's [[SetPrototypeOf]] method returned false") \
+ M(ObjectSetPrototypeOfTwoArgs, "Object.setPrototypeOf requires at least two arguments") \
+ M(ObjectPreventExtensionsReturnedFalse, "Object's [[PreventExtensions]] method returned false") \
+ M(ObjectPrototypeNullOrUndefinedOnSuperPropertyAccess, \
+ "Object prototype must not be {} on a super property access") \
+ M(ObjectPrototypeWrongType, "Prototype must be an object or null") \
+ M(ProxyConstructBadReturnType, "Proxy handler's construct trap violates invariant: must return " \
+ "an object") \
+ M(ProxyConstructorBadType, "Expected {} argument of Proxy constructor to be object, got {}") \
+ M(ProxyDefinePropExistingConfigurable, "Proxy handler's defineProperty trap violates " \
+ "invariant: a property cannot be defined as non-configurable if it already exists on the " \
+ "target object as a configurable property") \
+ M(ProxyDefinePropIncompatibleDescriptor, "Proxy handler's defineProperty trap violates " \
+ "invariant: the new descriptor is not compatible with the existing descriptor of the " \
+ "property on the target") \
+ M(ProxyDefinePropNonConfigurableNonExisting, "Proxy handler's defineProperty trap " \
+ "violates invariant: a property cannot be defined as non-configurable if it does not " \
+ "already exist on the target object") \
+ M(ProxyDefinePropNonExtensible, "Proxy handler's defineProperty trap violates invariant: " \
+ "a property cannot be reported as being defined if the property does not exist on " \
+ "the target and the target is non-extensible") \
+ M(ProxyDeleteNonConfigurable, "Proxy handler's deleteProperty trap violates invariant: " \
+ "cannot report a non-configurable own property of the target as deleted") \
+ M(ProxyGetImmutableDataProperty, "Proxy handler's get trap violates invariant: the " \
+ "returned value must match the value on the target if the property exists on the " \
+ "target as a non-writable, non-configurable own data property") \
+ M(ProxyGetNonConfigurableAccessor, "Proxy handler's get trap violates invariant: the " \
+ "returned value must be undefined if the property exists on the target as a " \
+ "non-configurable accessor property with an undefined get attribute") \
+ M(ProxyGetOwnDescriptorExistingConfigurable, "Proxy handler's getOwnPropertyDescriptor " \
+ "trap violates invariant: a property cannot be defined as non-configurable if it " \
+ "already exists on the target object as a configurable property") \
+ M(ProxyGetOwnDescriptorInvalidDescriptor, "Proxy handler's getOwnPropertyDescriptor trap " \
+ "violates invariant: invalid property descriptor for existing property on the target") \
+ M(ProxyGetOwnDescriptorInvalidNonConfig, "Proxy handler's getOwnPropertyDescriptor trap " \
+ "violates invariant: cannot report target's property as non-configurable if the " \
+ "property does not exist, or if it is configurable") \
+ M(ProxyGetOwnDescriptorNonConfigurable, "Proxy handler's getOwnPropertyDescriptor trap " \
+ "violates invariant: cannot return undefined for a property on the target which is " \
+ "a non-configurable property") \
+ M(ProxyGetOwnDescriptorReturn, "Proxy handler's getOwnPropertyDescriptor trap violates " \
+ "invariant: must return an object or undefined") \
+ M(ProxyGetOwnDescriptorUndefReturn, "Proxy handler's getOwnPropertyDescriptor trap " \
+ "violates invariant: cannot report a property as being undefined if it exists as an " \
+ "own property of the target and the target is non-extensible") \
+ M(ProxyGetPrototypeOfNonExtensible, "Proxy handler's getPrototypeOf trap violates " \
+ "invariant: cannot return a different prototype object for a non-extensible target") \
+ M(ProxyGetPrototypeOfReturn, "Proxy handler's getPrototypeOf trap violates invariant: " \
+ "must return an object or null") \
+ M(ProxyHasExistingNonConfigurable, "Proxy handler's has trap violates invariant: a " \
+ "property cannot be reported as non-existent if it exists on the target as a " \
+ "non-configurable property") \
+ M(ProxyHasExistingNonExtensible, "Proxy handler's has trap violates invariant: a property " \
+ "cannot be reported as non-existent if it exists on the target and the target is " \
+ "non-extensible") \
+ M(ProxyInvalidTrap, "Proxy handler's {} trap wasn't undefined, null, or callable") \
+ M(ProxyIsExtensibleReturn, "Proxy handler's isExtensible trap violates invariant: " \
+ "return value must match the target's extensibility") \
+ M(ProxyPreventExtensionsReturn, "Proxy handler's preventExtensions trap violates " \
+ "invariant: cannot return true if the target object is extensible") \
+ M(ProxyRevoked, "An operation was performed on a revoked Proxy object") \
+ M(ProxySetImmutableDataProperty, "Proxy handler's set trap violates invariant: cannot " \
+ "return true for a property on the target which is a non-configurable, non-writable " \
+ "own data property") \
+ M(ProxySetNonConfigurableAccessor, "Proxy handler's set trap violates invariant: cannot " \
+ "return true for a property on the target which is a non-configurable own accessor " \
+ "property with an undefined set attribute") \
+ M(ProxySetPrototypeOfNonExtensible, "Proxy handler's setPrototypeOf trap violates " \
+ "invariant: the argument must match the prototype of the target if the " \
+ "target is non-extensible") \
+ M(ProxyTwoArguments, "Proxy constructor requires at least two arguments") \
+ M(ReduceNoInitial, "Reduce of empty array with no initial value") \
+ M(ReferencePrimitiveAssignment, "Cannot assign property {} to primitive value") \
+ M(ReferenceUnresolvable, "Unresolvable reference") \
+ M(ReflectArgumentMustBeAFunction, "First argument of Reflect.{}() must be a function") \
+ M(ReflectArgumentMustBeAnObject, "First argument of Reflect.{}() must be an object") \
+ M(ReflectBadArgumentsList, "Arguments list must be an object") \
+ M(ReflectBadNewTarget, "Optional third argument of Reflect.construct() must be a constructor") \
+ M(ReflectBadDescriptorArgument, "Descriptor argument is not an object") \
+ M(RegExpCompileError, "RegExp compile error: {}") \
+ M(RegExpObjectBadFlag, "Invalid RegExp flag '{}'") \
+ M(RegExpObjectRepeatedFlag, "Repeated RegExp flag '{}'") \
+ M(StringRawCannotConvert, "Cannot convert property 'raw' to object from {}") \
+ M(StringRepeatCountMustBe, "repeat count must be a {} number") \
+ M(ThisHasNotBeenInitialized, "|this| has not been initialized") \
+ M(ThisIsAlreadyInitialized, "|this| is already initialized") \
+ M(ToObjectNullOrUndef, "ToObject on null or undefined") \
+ M(TypedArrayInvalidBufferLength, "Invalid buffer length for {}: must be a multiple of {}, got {}") \
+ M(TypedArrayInvalidByteOffset, "Invalid byte offset for {}: must be a multiple of {}, got {}") \
+ M(TypedArrayOutOfRangeByteOffset, "Typed array byte offset {} is out of range for buffer with length {}") \
+ M(TypedArrayOutOfRangeByteOffsetOrLength, "Typed array range {}:{} is out of range for buffer with length {}") \
+ M(UnknownIdentifier, "'{}' is not defined") \
+ /* LibWeb bindings */ \
+ M(NotAByteString, "Argument to {}() must be a byte string") \
+ M(BadArgCountOne, "{}() needs one argument") \
+ M(BadArgCountAtLeastOne, "{}() needs at least one argument") \
+ M(BadArgCountMany, "{}() needs {} arguments")
+
+namespace JS {
+
+class ErrorType {
+public:
+#define __ENUMERATE_JS_ERROR(name, message) \
+ static const ErrorType name;
+ JS_ENUMERATE_ERROR_TYPES(__ENUMERATE_JS_ERROR)
+#undef __ENUMERATE_JS_ERROR
+
+ const char* message() const
+ {
+ return m_message;
+ }
+
+private:
+ explicit ErrorType(const char* message)
+ : m_message(message)
+ {
+ }
+
+ const char* m_message;
+};
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/Exception.cpp b/Userland/Libraries/LibJS/Runtime/Exception.cpp
new file mode 100644
index 0000000000..08c6f6bde9
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/Exception.cpp
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * 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 <AK/String.h>
+#include <LibJS/AST.h>
+#include <LibJS/Runtime/Exception.h>
+#include <LibJS/Runtime/VM.h>
+
+namespace JS {
+
+Exception::Exception(Value value)
+ : m_value(value)
+{
+ auto& call_stack = vm().call_stack();
+ for (ssize_t i = call_stack.size() - 1; i >= 0; --i) {
+ String function_name = call_stack[i]->function_name;
+ if (function_name.is_empty())
+ function_name = "<anonymous>";
+ m_trace.append(function_name);
+ }
+
+ auto& node_stack = vm().node_stack();
+ for (ssize_t i = node_stack.size() - 1; i >= 0; --i) {
+ auto* node = node_stack[i];
+ ASSERT(node);
+ m_source_ranges.append(node->source_range());
+ }
+}
+
+Exception::~Exception()
+{
+}
+
+void Exception::visit_edges(Visitor& visitor)
+{
+ Cell::visit_edges(visitor);
+ visitor.visit(m_value);
+}
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/Exception.h b/Userland/Libraries/LibJS/Runtime/Exception.h
new file mode 100644
index 0000000000..2d26447c89
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/Exception.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * 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 <AK/Vector.h>
+#include <LibJS/Runtime/Cell.h>
+#include <LibJS/Runtime/Value.h>
+#include <LibJS/SourceRange.h>
+
+namespace JS {
+
+class Exception : public Cell {
+public:
+ explicit Exception(Value);
+ virtual ~Exception() override;
+
+ Value value() const { return m_value; }
+ const Vector<String>& trace() const { return m_trace; }
+ const Vector<SourceRange>& source_ranges() const { return m_source_ranges; }
+
+private:
+ virtual const char* class_name() const override { return "Exception"; }
+ virtual void visit_edges(Visitor&) override;
+
+ Value m_value;
+ Vector<String> m_trace;
+ Vector<SourceRange> m_source_ranges;
+};
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/Function.cpp b/Userland/Libraries/LibJS/Runtime/Function.cpp
new file mode 100644
index 0000000000..43b7d6e7e1
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/Function.cpp
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * 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/BoundFunction.h>
+#include <LibJS/Runtime/Function.h>
+#include <LibJS/Runtime/GlobalObject.h>
+
+namespace JS {
+
+Function::Function(Object& prototype)
+ : Function(prototype, {}, {})
+{
+}
+
+Function::Function(Object& prototype, Value bound_this, Vector<Value> bound_arguments)
+ : Object(prototype)
+ , m_bound_this(bound_this)
+ , m_bound_arguments(move(bound_arguments))
+{
+}
+
+Function::~Function()
+{
+}
+
+BoundFunction* Function::bind(Value bound_this_value, Vector<Value> arguments)
+{
+ auto& vm = this->vm();
+ Function& target_function = is<BoundFunction>(*this) ? static_cast<BoundFunction&>(*this).target_function() : *this;
+
+ auto bound_this_object = [&vm, bound_this_value, this]() -> Value {
+ if (!m_bound_this.is_empty())
+ return m_bound_this;
+ switch (bound_this_value.type()) {
+ case Value::Type::Undefined:
+ case Value::Type::Null:
+ if (vm.in_strict_mode())
+ return bound_this_value;
+ return &global_object();
+ default:
+ return bound_this_value.to_object(global_object());
+ }
+ }();
+
+ i32 computed_length = 0;
+ auto length_property = get(vm.names.length);
+ if (vm.exception())
+ return nullptr;
+ if (length_property.is_number())
+ computed_length = max(0, length_property.as_i32() - static_cast<i32>(arguments.size()));
+
+ Object* constructor_prototype = nullptr;
+ auto prototype_property = target_function.get(vm.names.prototype);
+ if (vm.exception())
+ return nullptr;
+ if (prototype_property.is_object())
+ constructor_prototype = &prototype_property.as_object();
+
+ auto all_bound_arguments = bound_arguments();
+ all_bound_arguments.append(move(arguments));
+
+ return heap().allocate<BoundFunction>(global_object(), global_object(), target_function, bound_this_object, move(all_bound_arguments), computed_length, constructor_prototype);
+}
+
+void Function::visit_edges(Visitor& visitor)
+{
+ Object::visit_edges(visitor);
+
+ visitor.visit(m_bound_this);
+
+ for (auto argument : m_bound_arguments)
+ visitor.visit(argument);
+}
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/Function.h b/Userland/Libraries/LibJS/Runtime/Function.h
new file mode 100644
index 0000000000..d54f059ec2
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/Function.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * 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 <AK/String.h>
+#include <LibJS/Runtime/Object.h>
+
+namespace JS {
+
+class Function : public Object {
+ JS_OBJECT(Function, Object);
+
+public:
+ enum class ConstructorKind {
+ Base,
+ Derived,
+ };
+
+ virtual ~Function();
+ virtual void initialize(GlobalObject&) override { }
+
+ virtual Value call() = 0;
+ virtual Value construct(Function& new_target) = 0;
+ virtual const FlyString& name() const = 0;
+ virtual LexicalEnvironment* create_environment() = 0;
+
+ virtual void visit_edges(Visitor&) override;
+
+ BoundFunction* bind(Value bound_this_value, Vector<Value> arguments);
+
+ Value bound_this() const { return m_bound_this; }
+
+ const Vector<Value>& bound_arguments() const { return m_bound_arguments; }
+
+ Value home_object() const { return m_home_object; }
+ void set_home_object(Value home_object) { m_home_object = home_object; }
+
+ ConstructorKind constructor_kind() const { return m_constructor_kind; };
+ void set_constructor_kind(ConstructorKind constructor_kind) { m_constructor_kind = constructor_kind; }
+
+ virtual bool is_strict_mode() const { return false; }
+
+protected:
+ explicit Function(Object& prototype);
+ Function(Object& prototype, Value bound_this, Vector<Value> bound_arguments);
+
+private:
+ virtual bool is_function() const override { return true; }
+ Value m_bound_this;
+ Vector<Value> m_bound_arguments;
+ Value m_home_object;
+ ConstructorKind m_constructor_kind = ConstructorKind::Base;
+};
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/FunctionConstructor.cpp b/Userland/Libraries/LibJS/Runtime/FunctionConstructor.cpp
new file mode 100644
index 0000000000..b8664907f2
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/FunctionConstructor.cpp
@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) 2020, Linus Groh <mail@linusgroh.de>
+ * 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 <AK/StringBuilder.h>
+#include <LibJS/AST.h>
+#include <LibJS/Interpreter.h>
+#include <LibJS/Parser.h>
+#include <LibJS/Runtime/Error.h>
+#include <LibJS/Runtime/FunctionConstructor.h>
+#include <LibJS/Runtime/GlobalObject.h>
+#include <LibJS/Runtime/ScriptFunction.h>
+
+namespace JS {
+
+FunctionConstructor::FunctionConstructor(GlobalObject& global_object)
+ : NativeFunction(vm().names.Function, *global_object.function_prototype())
+{
+}
+
+void FunctionConstructor::initialize(GlobalObject& global_object)
+{
+ auto& vm = this->vm();
+ NativeFunction::initialize(global_object);
+ define_property(vm.names.prototype, global_object.function_prototype(), 0);
+ define_property(vm.names.length, Value(1), Attribute::Configurable);
+}
+
+FunctionConstructor::~FunctionConstructor()
+{
+}
+
+Value FunctionConstructor::call()
+{
+ return construct(*this);
+}
+
+Value FunctionConstructor::construct(Function&)
+{
+ auto& vm = this->vm();
+ String parameters_source = "";
+ String body_source = "";
+ if (vm.argument_count() == 1) {
+ body_source = vm.argument(0).to_string(global_object());
+ if (vm.exception())
+ return {};
+ }
+ if (vm.argument_count() > 1) {
+ Vector<String> parameters;
+ for (size_t i = 0; i < vm.argument_count() - 1; ++i) {
+ parameters.append(vm.argument(i).to_string(global_object()));
+ if (vm.exception())
+ return {};
+ }
+ StringBuilder parameters_builder;
+ parameters_builder.join(',', parameters);
+ parameters_source = parameters_builder.build();
+ body_source = vm.argument(vm.argument_count() - 1).to_string(global_object());
+ if (vm.exception())
+ return {};
+ }
+ auto source = String::formatted("function anonymous({}\n) {{\n{}\n}}", parameters_source, body_source);
+ auto parser = Parser(Lexer(source));
+ auto function_expression = parser.parse_function_node<FunctionExpression>();
+ if (parser.has_errors()) {
+ auto error = parser.errors()[0];
+ vm.throw_exception<SyntaxError>(global_object(), error.to_string());
+ return {};
+ }
+
+ OwnPtr<Interpreter> local_interpreter;
+ Interpreter* interpreter = vm.interpreter_if_exists();
+
+ if (!interpreter) {
+ local_interpreter = Interpreter::create_with_existing_global_object(global_object());
+ interpreter = local_interpreter.ptr();
+ }
+
+ VM::InterpreterExecutionScope scope(*interpreter);
+ return function_expression->execute(*interpreter, global_object());
+}
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/FunctionConstructor.h b/Userland/Libraries/LibJS/Runtime/FunctionConstructor.h
new file mode 100644
index 0000000000..70405d43e4
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/FunctionConstructor.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2020, Linus Groh <mail@linusgroh.de>
+ * 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/NativeFunction.h>
+
+namespace JS {
+
+class FunctionConstructor final : public NativeFunction {
+ JS_OBJECT(FunctionConstructor, NativeFunction);
+
+public:
+ explicit FunctionConstructor(GlobalObject&);
+ virtual void initialize(GlobalObject&) override;
+ virtual ~FunctionConstructor() override;
+
+ virtual Value call() override;
+ virtual Value construct(Function& new_target) override;
+
+private:
+ virtual bool has_constructor() const override { return true; }
+};
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/FunctionPrototype.cpp b/Userland/Libraries/LibJS/Runtime/FunctionPrototype.cpp
new file mode 100644
index 0000000000..972c5021ea
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/FunctionPrototype.cpp
@@ -0,0 +1,189 @@
+/*
+ * Copyright (c) 2020, Linus Groh <mail@linusgroh.de>
+ * 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 <AK/Function.h>
+#include <AK/StringBuilder.h>
+#include <LibJS/AST.h>
+#include <LibJS/Interpreter.h>
+#include <LibJS/Runtime/BoundFunction.h>
+#include <LibJS/Runtime/Error.h>
+#include <LibJS/Runtime/Function.h>
+#include <LibJS/Runtime/FunctionPrototype.h>
+#include <LibJS/Runtime/GlobalObject.h>
+#include <LibJS/Runtime/MarkedValueList.h>
+#include <LibJS/Runtime/NativeFunction.h>
+#include <LibJS/Runtime/ScriptFunction.h>
+
+namespace JS {
+
+FunctionPrototype::FunctionPrototype(GlobalObject& global_object)
+ : Object(*global_object.object_prototype())
+{
+}
+
+void FunctionPrototype::initialize(GlobalObject& global_object)
+{
+ auto& vm = this->vm();
+ Object::initialize(global_object);
+ u8 attr = Attribute::Writable | Attribute::Configurable;
+ define_native_function(vm.names.apply, apply, 2, attr);
+ define_native_function(vm.names.bind, bind, 1, attr);
+ define_native_function(vm.names.call, call, 1, attr);
+ define_native_function(vm.names.toString, to_string, 0, attr);
+ define_native_function(vm.well_known_symbol_has_instance(), symbol_has_instance, 1, 0);
+ define_property(vm.names.length, Value(0), Attribute::Configurable);
+ define_property(vm.names.name, js_string(heap(), ""), Attribute::Configurable);
+}
+
+FunctionPrototype::~FunctionPrototype()
+{
+}
+
+JS_DEFINE_NATIVE_FUNCTION(FunctionPrototype::apply)
+{
+ auto* this_object = vm.this_value(global_object).to_object(global_object);
+ if (!this_object)
+ return {};
+ if (!this_object->is_function()) {
+ vm.throw_exception<TypeError>(global_object, ErrorType::NotA, "Function");
+ return {};
+ }
+ auto& function = static_cast<Function&>(*this_object);
+ auto this_arg = vm.argument(0);
+ auto arg_array = vm.argument(1);
+ if (arg_array.is_nullish())
+ return vm.call(function, this_arg);
+ if (!arg_array.is_object()) {
+ vm.throw_exception<TypeError>(global_object, ErrorType::FunctionArgsNotObject);
+ return {};
+ }
+ auto length = length_of_array_like(global_object, arg_array.as_object());
+ if (vm.exception())
+ return {};
+ MarkedValueList arguments(vm.heap());
+ for (size_t i = 0; i < length; ++i) {
+ auto element = arg_array.as_object().get(i);
+ if (vm.exception())
+ return {};
+ arguments.append(element.value_or(js_undefined()));
+ }
+ return vm.call(function, this_arg, move(arguments));
+}
+
+JS_DEFINE_NATIVE_FUNCTION(FunctionPrototype::bind)
+{
+ auto* this_object = vm.this_value(global_object).to_object(global_object);
+ if (!this_object)
+ return {};
+ if (!this_object->is_function()) {
+ vm.throw_exception<TypeError>(global_object, ErrorType::NotA, "Function");
+ return {};
+ }
+ auto& this_function = static_cast<Function&>(*this_object);
+ auto bound_this_arg = vm.argument(0);
+
+ Vector<Value> arguments;
+ if (vm.argument_count() > 1) {
+ arguments = vm.call_frame().arguments;
+ arguments.remove(0);
+ }
+
+ return this_function.bind(bound_this_arg, move(arguments));
+}
+
+JS_DEFINE_NATIVE_FUNCTION(FunctionPrototype::call)
+{
+ auto* this_object = vm.this_value(global_object).to_object(global_object);
+ if (!this_object)
+ return {};
+ if (!this_object->is_function()) {
+ vm.throw_exception<TypeError>(global_object, ErrorType::NotA, "Function");
+ return {};
+ }
+ auto& function = static_cast<Function&>(*this_object);
+ auto this_arg = vm.argument(0);
+ MarkedValueList arguments(vm.heap());
+ if (vm.argument_count() > 1) {
+ for (size_t i = 1; i < vm.argument_count(); ++i)
+ arguments.append(vm.argument(i));
+ }
+ return vm.call(function, this_arg, move(arguments));
+}
+
+JS_DEFINE_NATIVE_FUNCTION(FunctionPrototype::to_string)
+{
+ auto* this_object = vm.this_value(global_object).to_object(global_object);
+ if (!this_object)
+ return {};
+ if (!this_object->is_function()) {
+ vm.throw_exception<TypeError>(global_object, ErrorType::NotA, "Function");
+ return {};
+ }
+ String function_name = static_cast<Function*>(this_object)->name();
+ String function_parameters = "";
+ String function_body;
+
+ if (is<NativeFunction>(this_object) || is<BoundFunction>(this_object)) {
+ function_body = String::formatted(" [{}]", this_object->class_name());
+ } else {
+ StringBuilder parameters_builder;
+ auto first = true;
+ for (auto& parameter : static_cast<ScriptFunction*>(this_object)->parameters()) {
+ if (!first)
+ parameters_builder.append(", ");
+ first = false;
+ parameters_builder.append(parameter.name);
+ if (parameter.default_value) {
+ // FIXME: See note below
+ parameters_builder.append(" = TODO");
+ }
+ }
+ function_parameters = parameters_builder.build();
+ // FIXME: ASTNodes should be able to dump themselves to source strings - something like this:
+ // auto& body = static_cast<ScriptFunction*>(this_object)->body();
+ // function_body = body.to_source();
+ function_body = " ???";
+ }
+
+ auto function_source = String::formatted(
+ "function {}({}) {{\n{}\n}}",
+ function_name.is_null() ? "" : function_name, function_parameters, function_body);
+ return js_string(vm, function_source);
+}
+
+JS_DEFINE_NATIVE_FUNCTION(FunctionPrototype::symbol_has_instance)
+{
+ auto* this_object = vm.this_value(global_object).to_object(global_object);
+ if (!this_object)
+ return {};
+ if (!this_object->is_function()) {
+ vm.throw_exception<TypeError>(global_object, ErrorType::NotA, "Function");
+ return {};
+ }
+ return ordinary_has_instance(global_object, vm.argument(0), this_object);
+}
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/FunctionPrototype.h b/Userland/Libraries/LibJS/Runtime/FunctionPrototype.h
new file mode 100644
index 0000000000..2cb74504ad
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/FunctionPrototype.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2020, Linus Groh <mail@linusgroh.de>
+ * 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 FunctionPrototype final : public Object {
+ JS_OBJECT(FunctionPrototype, Object);
+
+public:
+ explicit FunctionPrototype(GlobalObject&);
+ virtual void initialize(GlobalObject&) override;
+ virtual ~FunctionPrototype() override;
+
+private:
+ JS_DECLARE_NATIVE_FUNCTION(apply);
+ JS_DECLARE_NATIVE_FUNCTION(bind);
+ JS_DECLARE_NATIVE_FUNCTION(call);
+ JS_DECLARE_NATIVE_FUNCTION(to_string);
+ JS_DECLARE_NATIVE_FUNCTION(symbol_has_instance);
+};
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/GlobalObject.cpp b/Userland/Libraries/LibJS/Runtime/GlobalObject.cpp
new file mode 100644
index 0000000000..37135f907c
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/GlobalObject.cpp
@@ -0,0 +1,310 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * Copyright (c) 2020, Linus Groh <mail@linusgroh.de>
+ * 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 <AK/Utf8View.h>
+#include <LibJS/Console.h>
+#include <LibJS/Heap/DeferGC.h>
+#include <LibJS/Runtime/ArrayBufferConstructor.h>
+#include <LibJS/Runtime/ArrayBufferPrototype.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>
+#include <LibJS/Runtime/BooleanConstructor.h>
+#include <LibJS/Runtime/BooleanPrototype.h>
+#include <LibJS/Runtime/ConsoleObject.h>
+#include <LibJS/Runtime/DateConstructor.h>
+#include <LibJS/Runtime/DatePrototype.h>
+#include <LibJS/Runtime/ErrorConstructor.h>
+#include <LibJS/Runtime/ErrorPrototype.h>
+#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>
+#include <LibJS/Runtime/NumberConstructor.h>
+#include <LibJS/Runtime/NumberPrototype.h>
+#include <LibJS/Runtime/Object.h>
+#include <LibJS/Runtime/ObjectConstructor.h>
+#include <LibJS/Runtime/ObjectPrototype.h>
+#include <LibJS/Runtime/ProxyConstructor.h>
+#include <LibJS/Runtime/ReflectObject.h>
+#include <LibJS/Runtime/RegExpConstructor.h>
+#include <LibJS/Runtime/RegExpPrototype.h>
+#include <LibJS/Runtime/Shape.h>
+#include <LibJS/Runtime/StringConstructor.h>
+#include <LibJS/Runtime/StringIteratorPrototype.h>
+#include <LibJS/Runtime/StringPrototype.h>
+#include <LibJS/Runtime/SymbolConstructor.h>
+#include <LibJS/Runtime/SymbolPrototype.h>
+#include <LibJS/Runtime/TypedArray.h>
+#include <LibJS/Runtime/TypedArrayConstructor.h>
+#include <LibJS/Runtime/TypedArrayPrototype.h>
+#include <LibJS/Runtime/Value.h>
+#include <ctype.h>
+
+namespace JS {
+
+GlobalObject::GlobalObject()
+ : ScopeObject(GlobalObjectTag::Tag)
+ , m_console(make<Console>(*this))
+{
+}
+
+void GlobalObject::initialize()
+{
+ auto& vm = this->vm();
+
+ ensure_shape_is_unique();
+
+ // These are done first since other prototypes depend on their presence.
+ m_empty_object_shape = heap().allocate_without_global_object<Shape>(*this);
+ m_object_prototype = heap().allocate_without_global_object<ObjectPrototype>(*this);
+ m_function_prototype = heap().allocate_without_global_object<FunctionPrototype>(*this);
+
+ m_new_object_shape = vm.heap().allocate_without_global_object<Shape>(*this);
+ m_new_object_shape->set_prototype_without_transition(m_object_prototype);
+
+ m_new_script_function_prototype_object_shape = vm.heap().allocate_without_global_object<Shape>(*this);
+ m_new_script_function_prototype_object_shape->set_prototype_without_transition(m_object_prototype);
+ m_new_script_function_prototype_object_shape->add_property_without_transition(vm.names.constructor, Attribute::Writable | Attribute::Configurable);
+
+ static_cast<FunctionPrototype*>(m_function_prototype)->initialize(*this);
+ static_cast<ObjectPrototype*>(m_object_prototype)->initialize(*this);
+
+ set_prototype(m_object_prototype);
+
+#define __JS_ENUMERATE(ClassName, snake_name, PrototypeName, ConstructorName, ArrayType) \
+ if (!m_##snake_name##_prototype) \
+ m_##snake_name##_prototype = heap().allocate<PrototypeName>(*this, *this);
+ 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(vm.names.gc, gc, 0, attr);
+ define_native_function(vm.names.isNaN, is_nan, 1, attr);
+ define_native_function(vm.names.isFinite, is_finite, 1, attr);
+ define_native_function(vm.names.parseFloat, parse_float, 1, attr);
+ define_native_function(vm.names.parseInt, parse_int, 1, attr);
+
+ define_property(vm.names.NaN, js_nan(), 0);
+ define_property(vm.names.Infinity, js_infinity(), 0);
+ define_property(vm.names.undefined, js_undefined(), 0);
+
+ define_property(vm.names.globalThis, this, attr);
+ define_property(vm.names.console, heap().allocate<ConsoleObject>(*this, *this), attr);
+ define_property(vm.names.Math, heap().allocate<MathObject>(*this, *this), attr);
+ define_property(vm.names.JSON, heap().allocate<JSONObject>(*this, *this), attr);
+ define_property(vm.names.Reflect, heap().allocate<ReflectObject>(*this, *this), attr);
+
+ add_constructor(vm.names.Array, m_array_constructor, m_array_prototype);
+ add_constructor(vm.names.ArrayBuffer, m_array_buffer_constructor, m_array_buffer_prototype);
+ add_constructor(vm.names.BigInt, m_bigint_constructor, m_bigint_prototype);
+ add_constructor(vm.names.Boolean, m_boolean_constructor, m_boolean_prototype);
+ add_constructor(vm.names.Date, m_date_constructor, m_date_prototype);
+ add_constructor(vm.names.Error, m_error_constructor, m_error_prototype);
+ add_constructor(vm.names.Function, m_function_constructor, m_function_prototype);
+ add_constructor(vm.names.Number, m_number_constructor, m_number_prototype);
+ add_constructor(vm.names.Object, m_object_constructor, m_object_prototype);
+ add_constructor(vm.names.Proxy, m_proxy_constructor, nullptr);
+ add_constructor(vm.names.RegExp, m_regexp_constructor, m_regexp_prototype);
+ add_constructor(vm.names.String, m_string_constructor, m_string_prototype);
+ add_constructor(vm.names.Symbol, m_symbol_constructor, m_symbol_prototype);
+
+ initialize_constructor(vm.names.TypedArray, m_typed_array_constructor, m_typed_array_prototype);
+
+#define __JS_ENUMERATE(ClassName, snake_name, PrototypeName, ConstructorName, ArrayType) \
+ add_constructor(vm.names.ClassName, m_##snake_name##_constructor, m_##snake_name##_prototype);
+ JS_ENUMERATE_ERROR_SUBCLASSES
+ JS_ENUMERATE_TYPED_ARRAYS
+#undef __JS_ENUMERATE
+}
+
+GlobalObject::~GlobalObject()
+{
+}
+
+void GlobalObject::visit_edges(Visitor& visitor)
+{
+ Base::visit_edges(visitor);
+
+ visitor.visit(m_empty_object_shape);
+ visitor.visit(m_new_object_shape);
+ visitor.visit(m_new_script_function_prototype_object_shape);
+ visitor.visit(m_proxy_constructor);
+
+#define __JS_ENUMERATE(ClassName, snake_name, PrototypeName, ConstructorName, ArrayType) \
+ visitor.visit(m_##snake_name##_constructor);
+ JS_ENUMERATE_ERROR_SUBCLASSES
+#undef __JS_ENUMERATE
+
+#define __JS_ENUMERATE(ClassName, snake_name) \
+ visitor.visit(m_##snake_name##_prototype);
+ JS_ENUMERATE_ITERATOR_PROTOTYPES
+#undef __JS_ENUMERATE
+}
+
+JS_DEFINE_NATIVE_FUNCTION(GlobalObject::gc)
+{
+ dbgln("Forced garbage collection requested!");
+ vm.heap().collect_garbage();
+ return js_undefined();
+}
+
+JS_DEFINE_NATIVE_FUNCTION(GlobalObject::is_nan)
+{
+ auto number = vm.argument(0).to_number(global_object);
+ if (vm.exception())
+ return {};
+ return Value(number.is_nan());
+}
+
+JS_DEFINE_NATIVE_FUNCTION(GlobalObject::is_finite)
+{
+ auto number = vm.argument(0).to_number(global_object);
+ if (vm.exception())
+ return {};
+ return Value(number.is_finite_number());
+}
+
+JS_DEFINE_NATIVE_FUNCTION(GlobalObject::parse_float)
+{
+ if (vm.argument(0).is_number())
+ return vm.argument(0);
+ auto string = vm.argument(0).to_string(global_object);
+ if (vm.exception())
+ return {};
+ for (size_t length = string.length(); length > 0; --length) {
+ // This can't throw, so no exception check is fine.
+ auto number = Value(js_string(vm, string.substring(0, length))).to_number(global_object);
+ if (!number.is_nan())
+ return number;
+ }
+ return js_nan();
+}
+
+JS_DEFINE_NATIVE_FUNCTION(GlobalObject::parse_int)
+{
+ // 18.2.5 parseInt ( string, radix )
+ auto input_string = vm.argument(0).to_string(global_object);
+ if (vm.exception())
+ return {};
+
+ // FIXME: There's a bunch of unnecessary string copying here.
+ double sign = 1;
+ auto s = input_string.trim_whitespace(TrimMode::Left);
+ if (!s.is_empty() && s[0] == '-')
+ sign = -1;
+ if (!s.is_empty() && (s[0] == '+' || s[0] == '-'))
+ s = s.substring(1, s.length() - 1);
+
+ auto radix = vm.argument(1).to_i32(global_object);
+ if (vm.exception())
+ return {};
+
+ bool strip_prefix = true;
+ if (radix != 0) {
+ if (radix < 2 || radix > 36)
+ return js_nan();
+ if (radix != 16)
+ strip_prefix = false;
+ } else {
+ radix = 10;
+ }
+
+ if (strip_prefix) {
+ if (s.length() >= 2 && s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) {
+ s = s.substring(2, s.length() - 2);
+ radix = 16;
+ }
+ }
+
+ auto parse_digit = [&](u32 codepoint, i32 radix) -> Optional<i32> {
+ i32 digit = -1;
+
+ if (isdigit(codepoint))
+ digit = codepoint - '0';
+ else if (islower(codepoint))
+ digit = 10 + (codepoint - 'a');
+ else if (isupper(codepoint))
+ digit = 10 + (codepoint - 'A');
+
+ if (digit == -1 || digit >= radix)
+ return {};
+ return digit;
+ };
+
+ bool had_digits = false;
+ double number = 0;
+ for (auto codepoint : Utf8View(s)) {
+ auto digit = parse_digit(codepoint, radix);
+ if (!digit.has_value())
+ break;
+ had_digits = true;
+ number *= radix;
+ number += digit.value();
+ }
+
+ if (!had_digits)
+ return js_nan();
+
+ return Value(sign * number);
+}
+
+Optional<Variable> GlobalObject::get_from_scope(const FlyString& name) const
+{
+ auto value = get(name);
+ if (value.is_empty())
+ return {};
+ return Variable { value, DeclarationKind::Var };
+}
+
+void GlobalObject::put_to_scope(const FlyString& name, Variable variable)
+{
+ put(name, variable.value);
+}
+
+bool GlobalObject::has_this_binding() const
+{
+ return true;
+}
+
+Value GlobalObject::get_this_binding(GlobalObject&) const
+{
+ return Value(this);
+}
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/GlobalObject.h b/Userland/Libraries/LibJS/Runtime/GlobalObject.h
new file mode 100644
index 0000000000..d3b7041e43
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/GlobalObject.h
@@ -0,0 +1,133 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * 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/Heap/Heap.h>
+#include <LibJS/Runtime/ScopeObject.h>
+#include <LibJS/Runtime/VM.h>
+
+namespace JS {
+
+class GlobalObject : public ScopeObject {
+ JS_OBJECT(GlobalObject, ScopeObject);
+
+public:
+ explicit GlobalObject();
+ virtual void initialize();
+
+ virtual ~GlobalObject() override;
+
+ virtual Optional<Variable> get_from_scope(const FlyString&) const override;
+ virtual void put_to_scope(const FlyString&, Variable) override;
+ virtual bool has_this_binding() const override;
+ virtual Value get_this_binding(GlobalObject&) const override;
+
+ Console& console() { return *m_console; }
+
+ Shape* empty_object_shape() { return m_empty_object_shape; }
+
+ Shape* new_object_shape() { return m_new_object_shape; }
+ Shape* new_script_function_prototype_object_shape() { return m_new_script_function_prototype_object_shape; }
+
+ // Not included in JS_ENUMERATE_NATIVE_OBJECTS due to missing distinct prototype
+ ProxyConstructor* proxy_constructor() { return m_proxy_constructor; }
+
+#define __JS_ENUMERATE(ClassName, snake_name, PrototypeName, ConstructorName, ArrayType) \
+ ConstructorName* snake_name##_constructor() { return m_##snake_name##_constructor; } \
+ Object* snake_name##_prototype() { return m_##snake_name##_prototype; }
+ 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_edges(Visitor&) override;
+
+ template<typename ConstructorType>
+ void initialize_constructor(const FlyString& property_name, ConstructorType*&, Object* prototype);
+ template<typename ConstructorType>
+ void add_constructor(const FlyString& property_name, ConstructorType*&, Object* prototype);
+
+private:
+ JS_DECLARE_NATIVE_FUNCTION(gc);
+ JS_DECLARE_NATIVE_FUNCTION(is_nan);
+ JS_DECLARE_NATIVE_FUNCTION(is_finite);
+ JS_DECLARE_NATIVE_FUNCTION(parse_float);
+ JS_DECLARE_NATIVE_FUNCTION(parse_int);
+
+ NonnullOwnPtr<Console> m_console;
+
+ Shape* m_empty_object_shape { nullptr };
+ Shape* m_new_object_shape { nullptr };
+ Shape* m_new_script_function_prototype_object_shape { nullptr };
+
+ // Not included in JS_ENUMERATE_NATIVE_OBJECTS due to missing distinct prototype
+ ProxyConstructor* m_proxy_constructor { nullptr };
+
+#define __JS_ENUMERATE(ClassName, snake_name, PrototypeName, ConstructorName, ArrayType) \
+ ConstructorName* m_##snake_name##_constructor { nullptr }; \
+ 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>
+inline void GlobalObject::initialize_constructor(const FlyString& property_name, ConstructorType*& constructor, Object* prototype)
+{
+ auto& vm = this->vm();
+ constructor = heap().allocate<ConstructorType>(*this, *this);
+ constructor->define_property(vm.names.name, js_string(heap(), property_name), Attribute::Configurable);
+ if (vm.exception())
+ return;
+ if (prototype) {
+ prototype->define_property(vm.names.constructor, constructor, Attribute::Writable | Attribute::Configurable);
+ if (vm.exception())
+ return;
+ }
+}
+
+template<typename ConstructorType>
+inline void GlobalObject::add_constructor(const FlyString& property_name, ConstructorType*& constructor, Object* prototype)
+{
+ initialize_constructor(property_name, constructor, prototype);
+ define_property(property_name, constructor, Attribute::Writable | Attribute::Configurable);
+}
+
+inline GlobalObject* Shape::global_object() const
+{
+ return static_cast<GlobalObject*>(m_global_object);
+}
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/IndexedProperties.cpp b/Userland/Libraries/LibJS/Runtime/IndexedProperties.cpp
new file mode 100644
index 0000000000..7dc1b4b9a3
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/IndexedProperties.cpp
@@ -0,0 +1,396 @@
+/*
+ * 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 <AK/QuickSort.h>
+#include <LibJS/Runtime/Accessor.h>
+#include <LibJS/Runtime/IndexedProperties.h>
+
+namespace JS {
+
+SimpleIndexedPropertyStorage::SimpleIndexedPropertyStorage(Vector<Value>&& initial_values)
+ : m_array_size(initial_values.size())
+ , m_packed_elements(move(initial_values))
+{
+}
+
+bool SimpleIndexedPropertyStorage::has_index(u32 index) const
+{
+ return index < m_array_size && !m_packed_elements[index].is_empty();
+}
+
+Optional<ValueAndAttributes> SimpleIndexedPropertyStorage::get(u32 index) const
+{
+ if (index >= m_array_size)
+ return {};
+ return ValueAndAttributes { m_packed_elements[index], default_attributes };
+}
+
+void SimpleIndexedPropertyStorage::put(u32 index, Value value, PropertyAttributes attributes)
+{
+ ASSERT(attributes == default_attributes);
+ ASSERT(index < SPARSE_ARRAY_THRESHOLD);
+
+ if (index >= m_array_size) {
+ m_array_size = index + 1;
+ if (index >= m_packed_elements.size())
+ m_packed_elements.resize(index + MIN_PACKED_RESIZE_AMOUNT >= SPARSE_ARRAY_THRESHOLD ? SPARSE_ARRAY_THRESHOLD : index + MIN_PACKED_RESIZE_AMOUNT);
+ }
+ m_packed_elements[index] = value;
+}
+
+void SimpleIndexedPropertyStorage::remove(u32 index)
+{
+ if (index < m_array_size)
+ m_packed_elements[index] = {};
+}
+
+void SimpleIndexedPropertyStorage::insert(u32 index, Value value, PropertyAttributes attributes)
+{
+ ASSERT(attributes == default_attributes);
+ ASSERT(index < SPARSE_ARRAY_THRESHOLD);
+ m_array_size++;
+ ASSERT(m_array_size <= SPARSE_ARRAY_THRESHOLD);
+ m_packed_elements.insert(index, value);
+}
+
+ValueAndAttributes SimpleIndexedPropertyStorage::take_first()
+{
+ m_array_size--;
+ return { m_packed_elements.take_first(), default_attributes };
+}
+
+ValueAndAttributes SimpleIndexedPropertyStorage::take_last()
+{
+ m_array_size--;
+ auto last_element = m_packed_elements[m_array_size];
+ m_packed_elements[m_array_size] = {};
+ return { last_element, default_attributes };
+}
+
+void SimpleIndexedPropertyStorage::set_array_like_size(size_t new_size)
+{
+ ASSERT(new_size <= SPARSE_ARRAY_THRESHOLD);
+ m_array_size = new_size;
+ m_packed_elements.resize(new_size);
+}
+
+GenericIndexedPropertyStorage::GenericIndexedPropertyStorage(SimpleIndexedPropertyStorage&& storage)
+{
+ m_array_size = storage.array_like_size();
+ for (auto& element : move(storage.m_packed_elements))
+ m_packed_elements.append({ element, default_attributes });
+}
+
+bool GenericIndexedPropertyStorage::has_index(u32 index) const
+{
+ if (index < SPARSE_ARRAY_THRESHOLD)
+ return index < m_packed_elements.size() && !m_packed_elements[index].value.is_empty();
+ return m_sparse_elements.contains(index);
+}
+
+Optional<ValueAndAttributes> GenericIndexedPropertyStorage::get(u32 index) const
+{
+ if (index >= m_array_size)
+ return {};
+ if (index < SPARSE_ARRAY_THRESHOLD) {
+ if (index >= m_packed_elements.size())
+ return {};
+ return m_packed_elements[index];
+ }
+ return m_sparse_elements.get(index);
+}
+
+void GenericIndexedPropertyStorage::put(u32 index, Value value, PropertyAttributes attributes)
+{
+ if (index >= m_array_size)
+ m_array_size = index + 1;
+ if (index < SPARSE_ARRAY_THRESHOLD) {
+ if (index >= m_packed_elements.size())
+ m_packed_elements.resize(index + MIN_PACKED_RESIZE_AMOUNT >= SPARSE_ARRAY_THRESHOLD ? SPARSE_ARRAY_THRESHOLD : index + MIN_PACKED_RESIZE_AMOUNT);
+ m_packed_elements[index] = { value, attributes };
+ } else {
+ m_sparse_elements.set(index, { value, attributes });
+ }
+}
+
+void GenericIndexedPropertyStorage::remove(u32 index)
+{
+ if (index >= m_array_size)
+ return;
+ if (index + 1 == m_array_size) {
+ take_last();
+ return;
+ }
+ if (index < SPARSE_ARRAY_THRESHOLD) {
+ if (index < m_packed_elements.size())
+ m_packed_elements[index] = {};
+ } else {
+ m_sparse_elements.remove(index);
+ }
+}
+
+void GenericIndexedPropertyStorage::insert(u32 index, Value value, PropertyAttributes attributes)
+{
+ if (index >= m_array_size) {
+ put(index, value, attributes);
+ return;
+ }
+
+ m_array_size++;
+
+ if (!m_sparse_elements.is_empty()) {
+ HashMap<u32, ValueAndAttributes> new_sparse_elements;
+ for (auto& entry : m_sparse_elements)
+ new_sparse_elements.set(entry.key >= index ? entry.key + 1 : entry.key, entry.value);
+ m_sparse_elements = move(new_sparse_elements);
+ }
+
+ if (index < SPARSE_ARRAY_THRESHOLD) {
+ m_packed_elements.insert(index, { value, attributes });
+ } else {
+ m_sparse_elements.set(index, { value, attributes });
+ }
+}
+
+ValueAndAttributes GenericIndexedPropertyStorage::take_first()
+{
+ ASSERT(m_array_size > 0);
+ m_array_size--;
+
+ if (!m_sparse_elements.is_empty()) {
+ HashMap<u32, ValueAndAttributes> new_sparse_elements;
+ for (auto& entry : m_sparse_elements)
+ new_sparse_elements.set(entry.key - 1, entry.value);
+ m_sparse_elements = move(new_sparse_elements);
+ }
+
+ return m_packed_elements.take_first();
+}
+
+ValueAndAttributes GenericIndexedPropertyStorage::take_last()
+{
+ ASSERT(m_array_size > 0);
+ m_array_size--;
+
+ if (m_array_size <= SPARSE_ARRAY_THRESHOLD) {
+ auto last_element = m_packed_elements[m_array_size];
+ m_packed_elements[m_array_size] = {};
+ return last_element;
+ } else {
+ auto result = m_sparse_elements.get(m_array_size);
+ m_sparse_elements.remove(m_array_size);
+ ASSERT(result.has_value());
+ return result.value();
+ }
+}
+
+void GenericIndexedPropertyStorage::set_array_like_size(size_t new_size)
+{
+ m_array_size = new_size;
+ if (new_size < SPARSE_ARRAY_THRESHOLD) {
+ m_packed_elements.resize(new_size);
+ m_sparse_elements.clear();
+ } else {
+ m_packed_elements.resize(SPARSE_ARRAY_THRESHOLD);
+
+ HashMap<u32, ValueAndAttributes> new_sparse_elements;
+ for (auto& entry : m_sparse_elements) {
+ if (entry.key < new_size)
+ new_sparse_elements.set(entry.key, entry.value);
+ }
+ m_sparse_elements = move(new_sparse_elements);
+ }
+}
+
+IndexedPropertyIterator::IndexedPropertyIterator(const IndexedProperties& indexed_properties, u32 staring_index, bool skip_empty)
+ : m_indexed_properties(indexed_properties)
+ , m_index(staring_index)
+ , m_skip_empty(skip_empty)
+{
+ if (m_skip_empty)
+ skip_empty_indices();
+}
+
+IndexedPropertyIterator& IndexedPropertyIterator::operator++()
+{
+ m_index++;
+
+ if (m_skip_empty)
+ skip_empty_indices();
+
+ return *this;
+}
+
+IndexedPropertyIterator& IndexedPropertyIterator::operator*()
+{
+ return *this;
+}
+
+bool IndexedPropertyIterator::operator!=(const IndexedPropertyIterator& other) const
+{
+ return m_index != other.m_index;
+}
+
+ValueAndAttributes IndexedPropertyIterator::value_and_attributes(Object* this_object, bool evaluate_accessors)
+{
+ if (m_index < m_indexed_properties.array_like_size())
+ return m_indexed_properties.get(this_object, m_index, evaluate_accessors).value_or({});
+ return {};
+}
+
+void IndexedPropertyIterator::skip_empty_indices()
+{
+ auto indices = m_indexed_properties.indices();
+ for (auto i : indices) {
+ if (i < m_index)
+ continue;
+ m_index = i;
+ return;
+ }
+ m_index = m_indexed_properties.array_like_size();
+}
+
+Optional<ValueAndAttributes> IndexedProperties::get(Object* this_object, u32 index, bool evaluate_accessors) const
+{
+ auto result = m_storage->get(index);
+ if (!evaluate_accessors)
+ return result;
+ if (!result.has_value())
+ return {};
+ auto& value = result.value();
+ if (value.value.is_accessor()) {
+ ASSERT(this_object);
+ auto& accessor = value.value.as_accessor();
+ return ValueAndAttributes { accessor.call_getter(this_object), value.attributes };
+ }
+ return result;
+}
+
+void IndexedProperties::put(Object* this_object, u32 index, Value value, PropertyAttributes attributes, bool evaluate_accessors)
+{
+ if (m_storage->is_simple_storage() && (index >= SPARSE_ARRAY_THRESHOLD || attributes != default_attributes))
+ switch_to_generic_storage();
+ if (m_storage->is_simple_storage() || !evaluate_accessors) {
+ m_storage->put(index, value, attributes);
+ return;
+ }
+
+ auto value_here = m_storage->get(index);
+ if (value_here.has_value() && value_here.value().value.is_accessor()) {
+ ASSERT(this_object);
+ value_here.value().value.as_accessor().call_setter(this_object, value);
+ } else {
+ m_storage->put(index, value, attributes);
+ }
+}
+
+bool IndexedProperties::remove(u32 index)
+{
+ auto result = m_storage->get(index);
+ if (!result.has_value())
+ return true;
+ if (!result.value().attributes.is_configurable())
+ return false;
+ m_storage->remove(index);
+ return true;
+}
+
+void IndexedProperties::insert(u32 index, Value value, PropertyAttributes attributes)
+{
+ if (m_storage->is_simple_storage() && (index >= SPARSE_ARRAY_THRESHOLD || attributes != default_attributes || array_like_size() == SPARSE_ARRAY_THRESHOLD))
+ switch_to_generic_storage();
+ m_storage->insert(index, move(value), attributes);
+}
+
+ValueAndAttributes IndexedProperties::take_first(Object* this_object)
+{
+ auto first = m_storage->take_first();
+ if (first.value.is_accessor())
+ return { first.value.as_accessor().call_getter(this_object), first.attributes };
+ return first;
+}
+
+ValueAndAttributes IndexedProperties::take_last(Object* this_object)
+{
+ auto last = m_storage->take_last();
+ if (last.value.is_accessor())
+ return { last.value.as_accessor().call_getter(this_object), last.attributes };
+ return last;
+}
+
+void IndexedProperties::append_all(Object* this_object, const IndexedProperties& properties, bool evaluate_accessors)
+{
+ if (m_storage->is_simple_storage() && !properties.m_storage->is_simple_storage())
+ switch_to_generic_storage();
+
+ for (auto it = properties.begin(false); it != properties.end(); ++it) {
+ const auto& element = it.value_and_attributes(this_object, evaluate_accessors);
+ if (this_object && this_object->vm().exception())
+ return;
+ m_storage->put(m_storage->array_like_size(), element.value, element.attributes);
+ }
+}
+
+void IndexedProperties::set_array_like_size(size_t new_size)
+{
+ if (m_storage->is_simple_storage() && new_size > SPARSE_ARRAY_THRESHOLD)
+ switch_to_generic_storage();
+ m_storage->set_array_like_size(new_size);
+}
+
+Vector<u32> IndexedProperties::indices() const
+{
+ Vector<u32> indices;
+ if (m_storage->is_simple_storage()) {
+ const auto& storage = static_cast<const SimpleIndexedPropertyStorage&>(*m_storage);
+ const auto& elements = storage.elements();
+ indices.ensure_capacity(storage.array_like_size());
+ for (size_t i = 0; i < elements.size(); ++i) {
+ if (!elements.at(i).is_empty())
+ indices.unchecked_append(i);
+ }
+ } else {
+ const auto& storage = static_cast<const GenericIndexedPropertyStorage&>(*m_storage);
+ const auto packed_elements = storage.packed_elements();
+ indices.ensure_capacity(storage.array_like_size());
+ for (size_t i = 0; i < packed_elements.size(); ++i) {
+ if (!packed_elements.at(i).value.is_empty())
+ indices.unchecked_append(i);
+ }
+ auto sparse_elements_keys = storage.sparse_elements().keys();
+ quick_sort(sparse_elements_keys);
+ indices.append(move(sparse_elements_keys));
+ }
+ return indices;
+}
+
+void IndexedProperties::switch_to_generic_storage()
+{
+ auto& storage = static_cast<SimpleIndexedPropertyStorage&>(*m_storage);
+ m_storage = make<GenericIndexedPropertyStorage>(move(storage));
+}
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/IndexedProperties.h b/Userland/Libraries/LibJS/Runtime/IndexedProperties.h
new file mode 100644
index 0000000000..984c3cef4c
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/IndexedProperties.h
@@ -0,0 +1,190 @@
+/*
+ * 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 <AK/NonnullOwnPtr.h>
+#include <LibJS/Runtime/Shape.h>
+#include <LibJS/Runtime/Value.h>
+
+namespace JS {
+
+const u32 SPARSE_ARRAY_THRESHOLD = 200;
+const u32 MIN_PACKED_RESIZE_AMOUNT = 20;
+
+struct ValueAndAttributes {
+ Value value;
+ PropertyAttributes attributes { default_attributes };
+};
+
+class IndexedProperties;
+class IndexedPropertyIterator;
+class GenericIndexedPropertyStorage;
+
+class IndexedPropertyStorage {
+public:
+ virtual ~IndexedPropertyStorage() {};
+
+ virtual bool has_index(u32 index) const = 0;
+ virtual Optional<ValueAndAttributes> get(u32 index) const = 0;
+ virtual void put(u32 index, Value value, PropertyAttributes attributes = default_attributes) = 0;
+ virtual void remove(u32 index) = 0;
+
+ virtual void insert(u32 index, Value value, PropertyAttributes attributes = default_attributes) = 0;
+ virtual ValueAndAttributes take_first() = 0;
+ virtual ValueAndAttributes take_last() = 0;
+
+ virtual size_t size() const = 0;
+ virtual size_t array_like_size() const = 0;
+ virtual void set_array_like_size(size_t new_size) = 0;
+
+ virtual bool is_simple_storage() const { return false; }
+};
+
+class SimpleIndexedPropertyStorage final : public IndexedPropertyStorage {
+public:
+ SimpleIndexedPropertyStorage() = default;
+ explicit SimpleIndexedPropertyStorage(Vector<Value>&& initial_values);
+
+ virtual bool has_index(u32 index) const override;
+ virtual Optional<ValueAndAttributes> get(u32 index) const override;
+ virtual void put(u32 index, Value value, PropertyAttributes attributes = default_attributes) override;
+ virtual void remove(u32 index) override;
+
+ virtual void insert(u32 index, Value value, PropertyAttributes attributes = default_attributes) override;
+ virtual ValueAndAttributes take_first() override;
+ virtual ValueAndAttributes take_last() override;
+
+ virtual size_t size() const override { return m_packed_elements.size(); }
+ virtual size_t array_like_size() const override { return m_array_size; }
+ virtual void set_array_like_size(size_t new_size) override;
+
+ virtual bool is_simple_storage() const override { return true; }
+ const Vector<Value>& elements() const { return m_packed_elements; }
+
+private:
+ friend GenericIndexedPropertyStorage;
+
+ size_t m_array_size { 0 };
+ Vector<Value> m_packed_elements;
+};
+
+class GenericIndexedPropertyStorage final : public IndexedPropertyStorage {
+public:
+ explicit GenericIndexedPropertyStorage(SimpleIndexedPropertyStorage&&);
+
+ virtual bool has_index(u32 index) const override;
+ virtual Optional<ValueAndAttributes> get(u32 index) const override;
+ virtual void put(u32 index, Value value, PropertyAttributes attributes = default_attributes) override;
+ virtual void remove(u32 index) override;
+
+ virtual void insert(u32 index, Value value, PropertyAttributes attributes = default_attributes) override;
+ virtual ValueAndAttributes take_first() override;
+ virtual ValueAndAttributes take_last() override;
+
+ virtual size_t size() const override { return m_packed_elements.size() + m_sparse_elements.size(); }
+ virtual size_t array_like_size() const override { return m_array_size; }
+ virtual void set_array_like_size(size_t new_size) override;
+
+ const Vector<ValueAndAttributes>& packed_elements() const { return m_packed_elements; }
+ const HashMap<u32, ValueAndAttributes>& sparse_elements() const { return m_sparse_elements; }
+
+private:
+ size_t m_array_size { 0 };
+ Vector<ValueAndAttributes> m_packed_elements;
+ HashMap<u32, ValueAndAttributes> m_sparse_elements;
+};
+
+class IndexedPropertyIterator {
+public:
+ IndexedPropertyIterator(const IndexedProperties&, u32 starting_index, bool skip_empty);
+
+ IndexedPropertyIterator& operator++();
+ IndexedPropertyIterator& operator*();
+ bool operator!=(const IndexedPropertyIterator&) const;
+
+ u32 index() const { return m_index; };
+ ValueAndAttributes value_and_attributes(Object* this_object, bool evaluate_accessors = true);
+
+private:
+ void skip_empty_indices();
+
+ const IndexedProperties& m_indexed_properties;
+ u32 m_index;
+ bool m_skip_empty;
+};
+
+class IndexedProperties {
+public:
+ IndexedProperties() = default;
+
+ IndexedProperties(Vector<Value>&& values)
+ : m_storage(make<SimpleIndexedPropertyStorage>(move(values)))
+ {
+ }
+
+ bool has_index(u32 index) const { return m_storage->has_index(index); }
+ Optional<ValueAndAttributes> get(Object* this_object, u32 index, bool evaluate_accessors = true) const;
+ void put(Object* this_object, u32 index, Value value, PropertyAttributes attributes = default_attributes, bool evaluate_accessors = true);
+ bool remove(u32 index);
+
+ void insert(u32 index, Value value, PropertyAttributes attributes = default_attributes);
+ ValueAndAttributes take_first(Object* this_object);
+ ValueAndAttributes take_last(Object* this_object);
+
+ void append(Value value, PropertyAttributes attributes = default_attributes) { put(nullptr, array_like_size(), value, attributes, false); }
+ void append_all(Object* this_object, const IndexedProperties& properties, bool evaluate_accessors = true);
+
+ IndexedPropertyIterator begin(bool skip_empty = true) const { return IndexedPropertyIterator(*this, 0, skip_empty); };
+ IndexedPropertyIterator end() const { return IndexedPropertyIterator(*this, array_like_size(), false); };
+
+ bool is_empty() const { return array_like_size() == 0; }
+ size_t array_like_size() const { return m_storage->array_like_size(); }
+ void set_array_like_size(size_t);
+
+ Vector<u32> indices() const;
+
+ template<typename Callback>
+ void for_each_value(Callback callback)
+ {
+ if (m_storage->is_simple_storage()) {
+ for (auto& value : static_cast<SimpleIndexedPropertyStorage&>(*m_storage).elements())
+ callback(value);
+ } else {
+ for (auto& element : static_cast<const GenericIndexedPropertyStorage&>(*m_storage).packed_elements())
+ callback(element.value);
+ for (auto& element : static_cast<const GenericIndexedPropertyStorage&>(*m_storage).sparse_elements())
+ callback(element.value.value);
+ }
+ }
+
+private:
+ void switch_to_generic_storage();
+
+ NonnullOwnPtr<IndexedPropertyStorage> m_storage { make<SimpleIndexedPropertyStorage>() };
+};
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/IteratorOperations.cpp b/Userland/Libraries/LibJS/Runtime/IteratorOperations.cpp
new file mode 100644
index 0000000000..ee6a8ed057
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/IteratorOperations.cpp
@@ -0,0 +1,135 @@
+/*
+ * 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/Error.h>
+#include <LibJS/Runtime/GlobalObject.h>
+#include <LibJS/Runtime/IteratorOperations.h>
+
+namespace JS {
+
+Object* get_iterator(GlobalObject& global_object, Value value, String hint, Value method)
+{
+ auto& vm = global_object.vm();
+ ASSERT(hint == "sync" || hint == "async");
+ if (method.is_empty()) {
+ if (hint == "async")
+ TODO();
+ auto object = value.to_object(global_object);
+ if (!object)
+ return {};
+ method = object->get(global_object.vm().well_known_symbol_iterator());
+ if (vm.exception())
+ return {};
+ }
+ if (!method.is_function()) {
+ vm.throw_exception<TypeError>(global_object, ErrorType::NotIterable, value.to_string_without_side_effects());
+ return nullptr;
+ }
+ auto iterator = vm.call(method.as_function(), value);
+ if (vm.exception())
+ return {};
+ if (!iterator.is_object()) {
+ vm.throw_exception<TypeError>(global_object, ErrorType::NotIterable, value.to_string_without_side_effects());
+ return nullptr;
+ }
+ return &iterator.as_object();
+}
+
+Object* iterator_next(Object& iterator, Value value)
+{
+ auto& vm = iterator.vm();
+ auto& global_object = iterator.global_object();
+ auto next_method = iterator.get(vm.names.next);
+ if (vm.exception())
+ return {};
+
+ if (!next_method.is_function()) {
+ vm.throw_exception<TypeError>(global_object, ErrorType::IterableNextNotAFunction);
+ return nullptr;
+ }
+
+ Value result;
+ if (value.is_empty())
+ result = vm.call(next_method.as_function(), &iterator);
+ else
+ result = vm.call(next_method.as_function(), &iterator, value);
+
+ if (vm.exception())
+ return {};
+ if (!result.is_object()) {
+ vm.throw_exception<TypeError>(global_object, ErrorType::IterableNextBadReturn);
+ return nullptr;
+ }
+
+ return &result.as_object();
+}
+
+void iterator_close([[maybe_unused]] Object& iterator)
+{
+ TODO();
+}
+
+Value create_iterator_result_object(GlobalObject& global_object, Value value, bool done)
+{
+ auto& vm = global_object.vm();
+ auto* object = Object::create_empty(global_object);
+ object->define_property(vm.names.value, value);
+ object->define_property(vm.names.done, Value(done));
+ return object;
+}
+
+void get_iterator_values(GlobalObject& global_object, Value value, AK::Function<IterationDecision(Value)> callback)
+{
+ auto& vm = global_object.vm();
+
+ 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(vm.names.done);
+ if (vm.exception())
+ return;
+
+ if (!done_property.is_empty() && done_property.to_boolean())
+ return;
+
+ auto next_value = next_object->get(vm.names.value);
+ if (vm.exception())
+ return;
+
+ auto result = callback(next_value);
+ if (result == IterationDecision::Break)
+ return;
+ ASSERT(result == IterationDecision::Continue);
+ }
+}
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/IteratorOperations.h b/Userland/Libraries/LibJS/Runtime/IteratorOperations.h
new file mode 100644
index 0000000000..db113eec97
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/IteratorOperations.h
@@ -0,0 +1,46 @@
+/*
+ * 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 <AK/Function.h>
+#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(GlobalObject&, Value value, String hint = "sync", Value method = {});
+bool is_iterator_complete(Object& iterator_result);
+Value create_iterator_result_object(GlobalObject&, Value value, bool done);
+
+Object* iterator_next(Object& iterator, Value value = {});
+void iterator_close(Object& iterator);
+
+void get_iterator_values(GlobalObject&, Value value, AK::Function<IterationDecision(Value)> callback);
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/IteratorPrototype.cpp b/Userland/Libraries/LibJS/Runtime/IteratorPrototype.cpp
new file mode 100644
index 0000000000..32eef36315
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/IteratorPrototype.cpp
@@ -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.
+ */
+
+#include <AK/Function.h>
+#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(GlobalObject& global_object)
+{
+ Object::initialize(global_object);
+ define_native_function(global_object.vm().well_known_symbol_iterator(), symbol_iterator, 0, Attribute::Writable | Attribute::Enumerable);
+}
+
+IteratorPrototype::~IteratorPrototype()
+{
+}
+
+JS_DEFINE_NATIVE_FUNCTION(IteratorPrototype::symbol_iterator)
+{
+ auto* this_object = vm.this_value(global_object).to_object(global_object);
+ if (!this_object)
+ return {};
+ return this_object;
+}
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/IteratorPrototype.h b/Userland/Libraries/LibJS/Runtime/IteratorPrototype.h
new file mode 100644
index 0000000000..72d37b2540
--- /dev/null
+++ b/Userland/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(GlobalObject&) override;
+ virtual ~IteratorPrototype() override;
+
+private:
+ JS_DECLARE_NATIVE_FUNCTION(symbol_iterator);
+};
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/JSONObject.cpp b/Userland/Libraries/LibJS/Runtime/JSONObject.cpp
new file mode 100644
index 0000000000..dddd82bfa0
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/JSONObject.cpp
@@ -0,0 +1,507 @@
+/*
+ * 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 <AK/Function.h>
+#include <AK/JsonArray.h>
+#include <AK/JsonObject.h>
+#include <AK/JsonParser.h>
+#include <AK/StringBuilder.h>
+#include <LibJS/Runtime/Array.h>
+#include <LibJS/Runtime/BigIntObject.h>
+#include <LibJS/Runtime/BooleanObject.h>
+#include <LibJS/Runtime/Error.h>
+#include <LibJS/Runtime/GlobalObject.h>
+#include <LibJS/Runtime/JSONObject.h>
+#include <LibJS/Runtime/NumberObject.h>
+#include <LibJS/Runtime/Object.h>
+#include <LibJS/Runtime/StringObject.h>
+
+namespace JS {
+
+JSONObject::JSONObject(GlobalObject& global_object)
+ : Object(*global_object.object_prototype())
+{
+}
+
+void JSONObject::initialize(GlobalObject& global_object)
+{
+ auto& vm = this->vm();
+ Object::initialize(global_object);
+ u8 attr = Attribute::Writable | Attribute::Configurable;
+ define_native_function(vm.names.stringify, stringify, 3, attr);
+ define_native_function(vm.names.parse, parse, 2, attr);
+ define_property(vm.well_known_symbol_to_string_tag(), js_string(global_object.heap(), "JSON"), Attribute::Configurable);
+}
+
+JSONObject::~JSONObject()
+{
+}
+
+String JSONObject::stringify_impl(GlobalObject& global_object, Value value, Value replacer, Value space)
+{
+ auto& vm = global_object.vm();
+ StringifyState state;
+
+ if (replacer.is_object()) {
+ if (replacer.as_object().is_function()) {
+ state.replacer_function = &replacer.as_function();
+ } else if (replacer.is_array()) {
+ auto& replacer_object = replacer.as_object();
+ auto replacer_length = length_of_array_like(global_object, replacer_object);
+ if (vm.exception())
+ return {};
+ Vector<String> list;
+ for (size_t i = 0; i < replacer_length; ++i) {
+ auto replacer_value = replacer_object.get(i);
+ if (vm.exception())
+ return {};
+ String item;
+ if (replacer_value.is_string() || replacer_value.is_number()) {
+ item = replacer_value.to_string(global_object);
+ if (vm.exception())
+ return {};
+ } else if (replacer_value.is_object()) {
+ auto& value_object = replacer_value.as_object();
+ if (is<StringObject>(value_object) || is<NumberObject>(value_object)) {
+ item = value_object.value_of().to_string(global_object);
+ if (vm.exception())
+ return {};
+ }
+ }
+ if (!item.is_null() && !list.contains_slow(item)) {
+ list.append(item);
+ }
+ }
+ state.property_list = list;
+ }
+ }
+
+ if (space.is_object()) {
+ auto& space_obj = space.as_object();
+ if (is<StringObject>(space_obj) || is<NumberObject>(space_obj))
+ space = space_obj.value_of();
+ }
+
+ if (space.is_number()) {
+ StringBuilder gap_builder;
+ auto gap_size = min(10, space.as_i32());
+ for (auto i = 0; i < gap_size; ++i)
+ gap_builder.append(' ');
+ state.gap = gap_builder.to_string();
+ } else if (space.is_string()) {
+ auto string = space.as_string().string();
+ if (string.length() <= 10) {
+ state.gap = string;
+ } else {
+ state.gap = string.substring(0, 10);
+ }
+ } else {
+ state.gap = String::empty();
+ }
+
+ auto* wrapper = Object::create_empty(global_object);
+ wrapper->define_property(String::empty(), value);
+ if (vm.exception())
+ return {};
+ auto result = serialize_json_property(global_object, state, String::empty(), wrapper);
+ if (vm.exception())
+ return {};
+ if (result.is_null())
+ return {};
+
+ return result;
+}
+
+JS_DEFINE_NATIVE_FUNCTION(JSONObject::stringify)
+{
+ if (!vm.argument_count())
+ return js_undefined();
+
+ auto value = vm.argument(0);
+ auto replacer = vm.argument(1);
+ auto space = vm.argument(2);
+
+ auto string = stringify_impl(global_object, value, replacer, space);
+ if (string.is_null())
+ return js_undefined();
+
+ return js_string(vm, string);
+}
+
+String JSONObject::serialize_json_property(GlobalObject& global_object, StringifyState& state, const PropertyName& key, Object* holder)
+{
+ auto& vm = global_object.vm();
+ auto value = holder->get(key);
+ if (vm.exception())
+ return {};
+ if (value.is_object()) {
+ auto to_json = value.as_object().get(vm.names.toJSON);
+ if (vm.exception())
+ return {};
+ if (to_json.is_function()) {
+ value = vm.call(to_json.as_function(), value, js_string(vm, key.to_string()));
+ if (vm.exception())
+ return {};
+ }
+ }
+
+ if (state.replacer_function) {
+ value = vm.call(*state.replacer_function, holder, js_string(vm, key.to_string()), value);
+ if (vm.exception())
+ return {};
+ }
+
+ if (value.is_object()) {
+ auto& value_object = value.as_object();
+ if (is<NumberObject>(value_object) || is<BooleanObject>(value_object) || is<StringObject>(value_object) || is<BigIntObject>(value_object))
+ value = value_object.value_of();
+ }
+
+ if (value.is_null())
+ return "null";
+ if (value.is_boolean())
+ return value.as_bool() ? "true" : "false";
+ if (value.is_string())
+ return quote_json_string(value.as_string().string());
+ if (value.is_number()) {
+ if (value.is_finite_number())
+ return value.to_string(global_object);
+ return "null";
+ }
+ if (value.is_object() && !value.is_function()) {
+ if (value.is_array())
+ return serialize_json_array(global_object, state, static_cast<Array&>(value.as_object()));
+ return serialize_json_object(global_object, state, value.as_object());
+ }
+ if (value.is_bigint())
+ vm.throw_exception<TypeError>(global_object, ErrorType::JsonBigInt);
+ return {};
+}
+
+String JSONObject::serialize_json_object(GlobalObject& global_object, StringifyState& state, Object& object)
+{
+ auto& vm = global_object.vm();
+ if (state.seen_objects.contains(&object)) {
+ vm.throw_exception<TypeError>(global_object, ErrorType::JsonCircular);
+ return {};
+ }
+
+ state.seen_objects.set(&object);
+ String previous_indent = state.indent;
+ state.indent = String::formatted("{}{}", state.indent, state.gap);
+ Vector<String> property_strings;
+
+ auto process_property = [&](const PropertyName& key) {
+ auto serialized_property_string = serialize_json_property(global_object, state, key, &object);
+ if (vm.exception())
+ return;
+ if (!serialized_property_string.is_null()) {
+ property_strings.append(String::formatted(
+ "{}:{}{}",
+ quote_json_string(key.to_string()),
+ state.gap.is_empty() ? "" : " ",
+ serialized_property_string));
+ }
+ };
+
+ if (state.property_list.has_value()) {
+ auto property_list = state.property_list.value();
+ for (auto& property : property_list) {
+ process_property(property);
+ if (vm.exception())
+ return {};
+ }
+ } else {
+ for (auto& entry : object.indexed_properties()) {
+ auto value_and_attributes = entry.value_and_attributes(&object);
+ if (!value_and_attributes.attributes.is_enumerable())
+ continue;
+ process_property(entry.index());
+ if (vm.exception())
+ return {};
+ }
+ for (auto& [key, metadata] : object.shape().property_table_ordered()) {
+ if (!metadata.attributes.is_enumerable())
+ continue;
+ process_property(key);
+ if (vm.exception())
+ return {};
+ }
+ }
+ StringBuilder builder;
+ if (property_strings.is_empty()) {
+ builder.append("{}");
+ } else {
+ bool first = true;
+ builder.append('{');
+ if (state.gap.is_empty()) {
+ for (auto& property_string : property_strings) {
+ if (!first)
+ builder.append(',');
+ first = false;
+ builder.append(property_string);
+ }
+ } else {
+ builder.append('\n');
+ builder.append(state.indent);
+ auto separator = String::formatted(",\n{}", state.indent);
+ for (auto& property_string : property_strings) {
+ if (!first)
+ builder.append(separator);
+ first = false;
+ builder.append(property_string);
+ }
+ builder.append('\n');
+ builder.append(previous_indent);
+ }
+ builder.append('}');
+ }
+
+ state.seen_objects.remove(&object);
+ state.indent = previous_indent;
+ return builder.to_string();
+}
+
+String JSONObject::serialize_json_array(GlobalObject& global_object, StringifyState& state, Object& object)
+{
+ auto& vm = global_object.vm();
+ if (state.seen_objects.contains(&object)) {
+ vm.throw_exception<TypeError>(global_object, ErrorType::JsonCircular);
+ return {};
+ }
+
+ state.seen_objects.set(&object);
+ String previous_indent = state.indent;
+ state.indent = String::formatted("{}{}", state.indent, state.gap);
+ Vector<String> property_strings;
+
+ auto length = length_of_array_like(global_object, object);
+ if (vm.exception())
+ return {};
+ for (size_t i = 0; i < length; ++i) {
+ if (vm.exception())
+ return {};
+ auto serialized_property_string = serialize_json_property(global_object, state, i, &object);
+ if (vm.exception())
+ return {};
+ if (serialized_property_string.is_null()) {
+ property_strings.append("null");
+ } else {
+ property_strings.append(serialized_property_string);
+ }
+ }
+
+ StringBuilder builder;
+ if (property_strings.is_empty()) {
+ builder.append("[]");
+ } else {
+ if (state.gap.is_empty()) {
+ builder.append('[');
+ bool first = true;
+ for (auto& property_string : property_strings) {
+ if (!first)
+ builder.append(',');
+ first = false;
+ builder.append(property_string);
+ }
+ builder.append(']');
+ } else {
+ builder.append("[\n");
+ builder.append(state.indent);
+ auto separator = String::formatted(",\n{}", state.indent);
+ bool first = true;
+ for (auto& property_string : property_strings) {
+ if (!first)
+ builder.append(separator);
+ first = false;
+ builder.append(property_string);
+ }
+ builder.append('\n');
+ builder.append(previous_indent);
+ builder.append(']');
+ }
+ }
+
+ state.seen_objects.remove(&object);
+ state.indent = previous_indent;
+ return builder.to_string();
+}
+
+String JSONObject::quote_json_string(String string)
+{
+ // FIXME: Handle UTF16
+ StringBuilder builder;
+ builder.append('"');
+ for (auto& ch : string) {
+ switch (ch) {
+ case '\b':
+ builder.append("\\b");
+ break;
+ case '\t':
+ builder.append("\\t");
+ break;
+ case '\n':
+ builder.append("\\n");
+ break;
+ case '\f':
+ builder.append("\\f");
+ break;
+ case '\r':
+ builder.append("\\r");
+ break;
+ case '"':
+ builder.append("\\\"");
+ break;
+ case '\\':
+ builder.append("\\\\");
+ break;
+ default:
+ if (ch < 0x20) {
+ builder.append("\\u%#08x", ch);
+ } else {
+ builder.append(ch);
+ }
+ }
+ }
+ builder.append('"');
+ return builder.to_string();
+}
+
+JS_DEFINE_NATIVE_FUNCTION(JSONObject::parse)
+{
+ if (!vm.argument_count())
+ return js_undefined();
+ auto string = vm.argument(0).to_string(global_object);
+ if (vm.exception())
+ return {};
+ auto reviver = vm.argument(1);
+
+ auto json = JsonValue::from_string(string);
+ if (!json.has_value()) {
+ vm.throw_exception<SyntaxError>(global_object, ErrorType::JsonMalformed);
+ return {};
+ }
+ Value result = parse_json_value(global_object, json.value());
+ if (reviver.is_function()) {
+ auto* holder_object = Object::create_empty(global_object);
+ holder_object->define_property(String::empty(), result);
+ if (vm.exception())
+ return {};
+ return internalize_json_property(global_object, holder_object, String::empty(), reviver.as_function());
+ }
+ return result;
+}
+
+Value JSONObject::parse_json_value(GlobalObject& global_object, const JsonValue& value)
+{
+ if (value.is_object())
+ return Value(parse_json_object(global_object, value.as_object()));
+ if (value.is_array())
+ return Value(parse_json_array(global_object, value.as_array()));
+ if (value.is_null())
+ return js_null();
+#if !defined(KERNEL)
+ if (value.is_double())
+ return Value(value.as_double());
+#endif
+ if (value.is_number())
+ return Value(value.to_i32(0));
+ if (value.is_string())
+ return js_string(global_object.heap(), value.to_string());
+ if (value.is_bool())
+ return Value(static_cast<bool>(value.as_bool()));
+ ASSERT_NOT_REACHED();
+}
+
+Object* JSONObject::parse_json_object(GlobalObject& global_object, const JsonObject& json_object)
+{
+ auto* object = Object::create_empty(global_object);
+ json_object.for_each_member([&](auto& key, auto& value) {
+ object->define_property(key, parse_json_value(global_object, value));
+ });
+ return object;
+}
+
+Array* JSONObject::parse_json_array(GlobalObject& global_object, const JsonArray& json_array)
+{
+ auto* array = Array::create(global_object);
+ size_t index = 0;
+ json_array.for_each([&](auto& value) {
+ array->define_property(index++, parse_json_value(global_object, value));
+ });
+ return array;
+}
+
+Value JSONObject::internalize_json_property(GlobalObject& global_object, Object* holder, const PropertyName& name, Function& reviver)
+{
+ auto& vm = global_object.vm();
+ auto value = holder->get(name);
+ if (vm.exception())
+ return {};
+ if (value.is_object()) {
+ auto& value_object = value.as_object();
+
+ auto process_property = [&](const PropertyName& key) {
+ auto element = internalize_json_property(global_object, &value_object, key, reviver);
+ if (vm.exception())
+ return;
+ if (element.is_undefined()) {
+ value_object.delete_property(key);
+ } else {
+ value_object.define_property(key, element, default_attributes, false);
+ }
+ };
+
+ if (value_object.is_array()) {
+ auto length = length_of_array_like(global_object, value_object);
+ for (size_t i = 0; i < length; ++i) {
+ process_property(i);
+ if (vm.exception())
+ return {};
+ }
+ } else {
+ for (auto& entry : value_object.indexed_properties()) {
+ auto value_and_attributes = entry.value_and_attributes(&value_object);
+ if (!value_and_attributes.attributes.is_enumerable())
+ continue;
+ process_property(entry.index());
+ if (vm.exception())
+ return {};
+ }
+ for (auto& [key, metadata] : value_object.shape().property_table_ordered()) {
+ if (!metadata.attributes.is_enumerable())
+ continue;
+ process_property(key);
+ if (vm.exception())
+ return {};
+ }
+ }
+ }
+
+ return vm.call(reviver, Value(holder), js_string(vm, name.to_string()), value);
+}
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/JSONObject.h b/Userland/Libraries/LibJS/Runtime/JSONObject.h
new file mode 100644
index 0000000000..23fdff2473
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/JSONObject.h
@@ -0,0 +1,70 @@
+/*
+ * 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 JSONObject final : public Object {
+ JS_OBJECT(JSONObject, Object);
+
+public:
+ explicit JSONObject(GlobalObject&);
+ virtual void initialize(GlobalObject&) override;
+ virtual ~JSONObject() override;
+
+ // The base implementation of stringify is exposed because it is used by
+ // test-js to communicate between the JS tests and the C++ test runner.
+ static String stringify_impl(GlobalObject&, Value value, Value replacer, Value space);
+
+private:
+ struct StringifyState {
+ Function* replacer_function { nullptr };
+ HashTable<Object*> seen_objects;
+ String indent { String::empty() };
+ String gap;
+ Optional<Vector<String>> property_list;
+ };
+
+ // Stringify helpers
+ static String serialize_json_property(GlobalObject&, StringifyState&, const PropertyName& key, Object* holder);
+ static String serialize_json_object(GlobalObject&, StringifyState&, Object&);
+ static String serialize_json_array(GlobalObject&, StringifyState&, Object&);
+ static String quote_json_string(String);
+
+ // Parse helpers
+ static Object* parse_json_object(GlobalObject&, const JsonObject&);
+ static Array* parse_json_array(GlobalObject&, const JsonArray&);
+ static Value parse_json_value(GlobalObject&, const JsonValue&);
+ static Value internalize_json_property(GlobalObject&, Object* holder, const PropertyName& name, Function& reviver);
+
+ JS_DECLARE_NATIVE_FUNCTION(stringify);
+ JS_DECLARE_NATIVE_FUNCTION(parse);
+};
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/LexicalEnvironment.cpp b/Userland/Libraries/LibJS/Runtime/LexicalEnvironment.cpp
new file mode 100644
index 0000000000..2275b6402b
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/LexicalEnvironment.cpp
@@ -0,0 +1,134 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * 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/Error.h>
+#include <LibJS/Runtime/Function.h>
+#include <LibJS/Runtime/GlobalObject.h>
+#include <LibJS/Runtime/LexicalEnvironment.h>
+#include <LibJS/Runtime/Value.h>
+
+namespace JS {
+
+LexicalEnvironment::LexicalEnvironment()
+ : ScopeObject(nullptr)
+{
+}
+
+LexicalEnvironment::LexicalEnvironment(EnvironmentRecordType environment_record_type)
+ : ScopeObject(nullptr)
+ , m_environment_record_type(environment_record_type)
+{
+}
+
+LexicalEnvironment::LexicalEnvironment(HashMap<FlyString, Variable> variables, ScopeObject* parent_scope)
+ : ScopeObject(parent_scope)
+ , m_variables(move(variables))
+{
+}
+
+LexicalEnvironment::LexicalEnvironment(HashMap<FlyString, Variable> variables, ScopeObject* parent_scope, EnvironmentRecordType environment_record_type)
+ : ScopeObject(parent_scope)
+ , m_environment_record_type(environment_record_type)
+ , m_variables(move(variables))
+{
+}
+
+LexicalEnvironment::~LexicalEnvironment()
+{
+}
+
+void LexicalEnvironment::visit_edges(Visitor& visitor)
+{
+ Cell::visit_edges(visitor);
+ visitor.visit(m_this_value);
+ visitor.visit(m_home_object);
+ visitor.visit(m_new_target);
+ visitor.visit(m_current_function);
+ for (auto& it : m_variables)
+ visitor.visit(it.value.value);
+}
+
+Optional<Variable> LexicalEnvironment::get_from_scope(const FlyString& name) const
+{
+ return m_variables.get(name);
+}
+
+void LexicalEnvironment::put_to_scope(const FlyString& name, Variable variable)
+{
+ m_variables.set(name, variable);
+}
+
+bool LexicalEnvironment::has_super_binding() const
+{
+ return m_environment_record_type == EnvironmentRecordType::Function && this_binding_status() != ThisBindingStatus::Lexical && m_home_object.is_object();
+}
+
+Value LexicalEnvironment::get_super_base()
+{
+ ASSERT(has_super_binding());
+ if (m_home_object.is_object())
+ return m_home_object.as_object().prototype();
+ return {};
+}
+
+bool LexicalEnvironment::has_this_binding() const
+{
+ // More like "is_capable_of_having_a_this_binding".
+ switch (m_environment_record_type) {
+ case EnvironmentRecordType::Declarative:
+ case EnvironmentRecordType::Object:
+ return false;
+ case EnvironmentRecordType::Function:
+ return this_binding_status() != ThisBindingStatus::Lexical;
+ case EnvironmentRecordType::Module:
+ return true;
+ }
+ ASSERT_NOT_REACHED();
+}
+
+Value LexicalEnvironment::get_this_binding(GlobalObject& global_object) const
+{
+ ASSERT(has_this_binding());
+ if (this_binding_status() == ThisBindingStatus::Uninitialized) {
+ vm().throw_exception<ReferenceError>(global_object, ErrorType::ThisHasNotBeenInitialized);
+ return {};
+ }
+ return m_this_value;
+}
+
+void LexicalEnvironment::bind_this_value(GlobalObject& global_object, Value this_value)
+{
+ ASSERT(has_this_binding());
+ if (m_this_binding_status == ThisBindingStatus::Initialized) {
+ vm().throw_exception<ReferenceError>(global_object, ErrorType::ThisIsAlreadyInitialized);
+ return;
+ }
+ m_this_value = this_value;
+ m_this_binding_status = ThisBindingStatus::Initialized;
+}
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/LexicalEnvironment.h b/Userland/Libraries/LibJS/Runtime/LexicalEnvironment.h
new file mode 100644
index 0000000000..1e4b4723d1
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/LexicalEnvironment.h
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * 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 <AK/FlyString.h>
+#include <AK/HashMap.h>
+#include <LibJS/Runtime/ScopeObject.h>
+#include <LibJS/Runtime/Value.h>
+
+namespace JS {
+
+class LexicalEnvironment final : public ScopeObject {
+ JS_OBJECT(LexicalEnvironment, ScopeObject);
+
+public:
+ enum class ThisBindingStatus {
+ Lexical,
+ Initialized,
+ Uninitialized,
+ };
+
+ enum class EnvironmentRecordType {
+ Declarative,
+ Function,
+ Object,
+ Module,
+ };
+
+ LexicalEnvironment();
+ LexicalEnvironment(EnvironmentRecordType);
+ LexicalEnvironment(HashMap<FlyString, Variable> variables, ScopeObject* parent_scope);
+ LexicalEnvironment(HashMap<FlyString, Variable> variables, ScopeObject* parent_scope, EnvironmentRecordType);
+ virtual ~LexicalEnvironment() override;
+
+ // ^ScopeObject
+ virtual Optional<Variable> get_from_scope(const FlyString&) const override;
+ virtual void put_to_scope(const FlyString&, Variable) override;
+ virtual bool has_this_binding() const override;
+ virtual Value get_this_binding(GlobalObject&) const override;
+
+ void clear();
+
+ const HashMap<FlyString, Variable>& variables() const { return m_variables; }
+
+ void set_home_object(Value object) { m_home_object = object; }
+ bool has_super_binding() const;
+ Value get_super_base();
+
+ ThisBindingStatus this_binding_status() const { return m_this_binding_status; }
+ void bind_this_value(GlobalObject&, Value this_value);
+
+ // Not a standard operation.
+ void replace_this_binding(Value this_value) { m_this_value = this_value; }
+
+ Value new_target() const { return m_new_target; };
+ void set_new_target(Value new_target) { m_new_target = new_target; }
+
+ Function* current_function() const { return m_current_function; }
+ void set_current_function(Function& function) { m_current_function = &function; }
+
+ EnvironmentRecordType type() const { return m_environment_record_type; }
+
+private:
+ virtual void visit_edges(Visitor&) override;
+
+ EnvironmentRecordType m_environment_record_type : 8 { EnvironmentRecordType::Declarative };
+ ThisBindingStatus m_this_binding_status : 8 { ThisBindingStatus::Uninitialized };
+ HashMap<FlyString, Variable> m_variables;
+ Value m_home_object;
+ Value m_this_value;
+ Value m_new_target;
+ // Corresponds to [[FunctionObject]]
+ Function* m_current_function { nullptr };
+};
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/MarkedValueList.cpp b/Userland/Libraries/LibJS/Runtime/MarkedValueList.cpp
new file mode 100644
index 0000000000..f268a21c6b
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/MarkedValueList.cpp
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * 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/Heap/Heap.h>
+#include <LibJS/Runtime/MarkedValueList.h>
+
+namespace JS {
+
+MarkedValueList::MarkedValueList(Heap& heap)
+ : m_heap(heap)
+{
+ m_heap.did_create_marked_value_list({}, *this);
+}
+
+MarkedValueList::MarkedValueList(MarkedValueList&& other)
+ : AK::Vector<Value, 32>(move(static_cast<Vector<Value, 32>&>(other)))
+ , m_heap(other.m_heap)
+{
+ m_heap.did_create_marked_value_list({}, *this);
+}
+
+MarkedValueList::~MarkedValueList()
+{
+ m_heap.did_destroy_marked_value_list({}, *this);
+}
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/MarkedValueList.h b/Userland/Libraries/LibJS/Runtime/MarkedValueList.h
new file mode 100644
index 0000000000..e311972181
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/MarkedValueList.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * 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 <AK/Noncopyable.h>
+#include <AK/Vector.h>
+#include <LibJS/Forward.h>
+#include <LibJS/Runtime/Value.h>
+
+namespace JS {
+
+class MarkedValueList : public AK::Vector<Value, 32> {
+ AK_MAKE_NONCOPYABLE(MarkedValueList);
+
+public:
+ explicit MarkedValueList(Heap&);
+ MarkedValueList(MarkedValueList&&);
+ ~MarkedValueList();
+
+ MarkedValueList& operator=(MarkedValueList&&) = delete;
+
+ Vector<Value, 32>& values() { return *this; }
+
+ MarkedValueList copy() const
+ {
+ MarkedValueList copy { m_heap };
+ copy.append(*this);
+ return copy;
+ }
+
+private:
+ Heap& m_heap;
+};
+}
diff --git a/Userland/Libraries/LibJS/Runtime/MathObject.cpp b/Userland/Libraries/LibJS/Runtime/MathObject.cpp
new file mode 100644
index 0000000000..bdd3ec6e17
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/MathObject.cpp
@@ -0,0 +1,504 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * Copyright (c) 2020, Linus Groh <mail@linusgroh.de>
+ * 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 <AK/FlyString.h>
+#include <AK/Function.h>
+#include <LibJS/Runtime/GlobalObject.h>
+#include <LibJS/Runtime/MathObject.h>
+#include <math.h>
+
+namespace JS {
+
+MathObject::MathObject(GlobalObject& global_object)
+ : Object(*global_object.object_prototype())
+{
+}
+
+void MathObject::initialize(GlobalObject& global_object)
+{
+ auto& vm = this->vm();
+ Object::initialize(global_object);
+ u8 attr = Attribute::Writable | Attribute::Configurable;
+ define_native_function(vm.names.abs, abs, 1, attr);
+ define_native_function(vm.names.random, random, 0, attr);
+ define_native_function(vm.names.sqrt, sqrt, 1, attr);
+ define_native_function(vm.names.floor, floor, 1, attr);
+ define_native_function(vm.names.ceil, ceil, 1, attr);
+ define_native_function(vm.names.round, round, 1, attr);
+ define_native_function(vm.names.max, max, 2, attr);
+ define_native_function(vm.names.min, min, 2, attr);
+ define_native_function(vm.names.trunc, trunc, 1, attr);
+ define_native_function(vm.names.sin, sin, 1, attr);
+ define_native_function(vm.names.cos, cos, 1, attr);
+ define_native_function(vm.names.tan, tan, 1, attr);
+ define_native_function(vm.names.pow, pow, 2, attr);
+ define_native_function(vm.names.exp, exp, 1, attr);
+ define_native_function(vm.names.expm1, expm1, 1, attr);
+ define_native_function(vm.names.sign, sign, 1, attr);
+ define_native_function(vm.names.clz32, clz32, 1, attr);
+ define_native_function(vm.names.acos, acos, 1, attr);
+ define_native_function(vm.names.acosh, acosh, 1, attr);
+ define_native_function(vm.names.asin, asin, 1, attr);
+ define_native_function(vm.names.asinh, asinh, 1, attr);
+ define_native_function(vm.names.atan, atan, 1, attr);
+ define_native_function(vm.names.atanh, atanh, 1, attr);
+ define_native_function(vm.names.log1p, log1p, 1, attr);
+ define_native_function(vm.names.cbrt, cbrt, 1, attr);
+ define_native_function(vm.names.atan2, atan2, 2, attr);
+ define_native_function(vm.names.fround, fround, 1, attr);
+ define_native_function(vm.names.hypot, hypot, 2, attr);
+ define_native_function(vm.names.log, log, 1, attr);
+ define_native_function(vm.names.log2, log2, 1, attr);
+ define_native_function(vm.names.log10, log10, 1, attr);
+ define_native_function(vm.names.sinh, sinh, 1, attr);
+ define_native_function(vm.names.cosh, cosh, 1, attr);
+ define_native_function(vm.names.tanh, tanh, 1, attr);
+
+ define_property(vm.names.E, Value(M_E), 0);
+ define_property(vm.names.LN2, Value(M_LN2), 0);
+ define_property(vm.names.LN10, Value(M_LN10), 0);
+ define_property(vm.names.LOG2E, Value(::log2(M_E)), 0);
+ define_property(vm.names.LOG10E, Value(::log10(M_E)), 0);
+ define_property(vm.names.PI, Value(M_PI), 0);
+ define_property(vm.names.SQRT1_2, Value(M_SQRT1_2), 0);
+ define_property(vm.names.SQRT2, Value(M_SQRT2), 0);
+
+ define_property(vm.well_known_symbol_to_string_tag(), js_string(vm.heap(), "Math"), Attribute::Configurable);
+}
+
+MathObject::~MathObject()
+{
+}
+
+JS_DEFINE_NATIVE_FUNCTION(MathObject::abs)
+{
+ auto number = vm.argument(0).to_number(global_object);
+ if (vm.exception())
+ return {};
+ if (number.is_nan())
+ return js_nan();
+ return Value(number.as_double() >= 0 ? number.as_double() : -number.as_double());
+}
+
+JS_DEFINE_NATIVE_FUNCTION(MathObject::random)
+{
+#ifdef __serenity__
+ double r = (double)arc4random() / (double)UINT32_MAX;
+#else
+ double r = (double)rand() / (double)RAND_MAX;
+#endif
+ return Value(r);
+}
+
+JS_DEFINE_NATIVE_FUNCTION(MathObject::sqrt)
+{
+ auto number = vm.argument(0).to_number(global_object);
+ if (vm.exception())
+ return {};
+ if (number.is_nan())
+ return js_nan();
+ return Value(::sqrt(number.as_double()));
+}
+
+JS_DEFINE_NATIVE_FUNCTION(MathObject::floor)
+{
+ auto number = vm.argument(0).to_number(global_object);
+ if (vm.exception())
+ return {};
+ if (number.is_nan())
+ return js_nan();
+ return Value(::floor(number.as_double()));
+}
+
+JS_DEFINE_NATIVE_FUNCTION(MathObject::ceil)
+{
+ auto number = vm.argument(0).to_number(global_object);
+ if (vm.exception())
+ return {};
+ if (number.is_nan())
+ return js_nan();
+ auto number_double = number.as_double();
+ if (number_double < 0 && number_double > -1)
+ return Value(-0.f);
+ return Value(::ceil(number.as_double()));
+}
+
+JS_DEFINE_NATIVE_FUNCTION(MathObject::round)
+{
+ auto number = vm.argument(0).to_number(global_object);
+ if (vm.exception())
+ return {};
+ if (number.is_nan())
+ return js_nan();
+ return Value(::round(number.as_double()));
+}
+
+JS_DEFINE_NATIVE_FUNCTION(MathObject::max)
+{
+ if (!vm.argument_count())
+ return js_negative_infinity();
+
+ auto max = vm.argument(0).to_number(global_object);
+ if (vm.exception())
+ return {};
+ for (size_t i = 1; i < vm.argument_count(); ++i) {
+ auto cur = vm.argument(i).to_number(global_object);
+ if (vm.exception())
+ return {};
+ max = Value(cur.as_double() > max.as_double() ? cur : max);
+ }
+ return max;
+}
+
+JS_DEFINE_NATIVE_FUNCTION(MathObject::min)
+{
+ if (!vm.argument_count())
+ return js_infinity();
+
+ auto min = vm.argument(0).to_number(global_object);
+ if (vm.exception())
+ return {};
+ for (size_t i = 1; i < vm.argument_count(); ++i) {
+ auto cur = vm.argument(i).to_number(global_object);
+ if (vm.exception())
+ return {};
+ min = Value(cur.as_double() < min.as_double() ? cur : min);
+ }
+ return min;
+}
+
+JS_DEFINE_NATIVE_FUNCTION(MathObject::trunc)
+{
+ auto number = vm.argument(0).to_number(global_object);
+ if (vm.exception())
+ return {};
+ if (number.is_nan())
+ return js_nan();
+ if (number.as_double() < 0)
+ return MathObject::ceil(vm, global_object);
+ return MathObject::floor(vm, global_object);
+}
+
+JS_DEFINE_NATIVE_FUNCTION(MathObject::sin)
+{
+ auto number = vm.argument(0).to_number(global_object);
+ if (vm.exception())
+ return {};
+ if (number.is_nan())
+ return js_nan();
+ return Value(::sin(number.as_double()));
+}
+
+JS_DEFINE_NATIVE_FUNCTION(MathObject::cos)
+{
+ auto number = vm.argument(0).to_number(global_object);
+ if (vm.exception())
+ return {};
+ if (number.is_nan())
+ return js_nan();
+ return Value(::cos(number.as_double()));
+}
+
+JS_DEFINE_NATIVE_FUNCTION(MathObject::tan)
+{
+ auto number = vm.argument(0).to_number(global_object);
+ if (vm.exception())
+ return {};
+ if (number.is_nan())
+ return js_nan();
+ return Value(::tan(number.as_double()));
+}
+
+JS_DEFINE_NATIVE_FUNCTION(MathObject::pow)
+{
+ return JS::exp(global_object, vm.argument(0), vm.argument(1));
+}
+
+JS_DEFINE_NATIVE_FUNCTION(MathObject::exp)
+{
+ auto number = vm.argument(0).to_number(global_object);
+ if (vm.exception())
+ return {};
+ if (number.is_nan())
+ return js_nan();
+ return Value(::exp(number.as_double()));
+}
+
+JS_DEFINE_NATIVE_FUNCTION(MathObject::expm1)
+{
+ auto number = vm.argument(0).to_number(global_object);
+ if (vm.exception())
+ return {};
+ if (number.is_nan())
+ return js_nan();
+ return Value(::expm1(number.as_double()));
+}
+
+JS_DEFINE_NATIVE_FUNCTION(MathObject::sign)
+{
+ auto number = vm.argument(0).to_number(global_object);
+ if (vm.exception())
+ return {};
+ if (number.is_positive_zero())
+ return Value(0);
+ if (number.is_negative_zero())
+ return Value(-0.0);
+ if (number.as_double() > 0)
+ return Value(1);
+ if (number.as_double() < 0)
+ return Value(-1);
+ return js_nan();
+}
+
+JS_DEFINE_NATIVE_FUNCTION(MathObject::clz32)
+{
+ auto number = vm.argument(0).to_number(global_object);
+ if (vm.exception())
+ return {};
+ if (!number.is_finite_number() || (unsigned)number.as_double() == 0)
+ return Value(32);
+ return Value(__builtin_clz((unsigned)number.as_double()));
+}
+
+JS_DEFINE_NATIVE_FUNCTION(MathObject::acos)
+{
+ auto number = vm.argument(0).to_number(global_object);
+ if (vm.exception())
+ return {};
+ if (number.is_nan() || number.as_double() > 1 || number.as_double() < -1)
+ return js_nan();
+ if (number.as_double() == 1)
+ return Value(0);
+ return Value(::acos(number.as_double()));
+}
+
+JS_DEFINE_NATIVE_FUNCTION(MathObject::acosh)
+{
+ auto number = vm.argument(0).to_number(global_object);
+ if (vm.exception())
+ return {};
+ if (number.as_double() < 1)
+ return js_nan();
+ return Value(::acosh(number.as_double()));
+}
+
+JS_DEFINE_NATIVE_FUNCTION(MathObject::asin)
+{
+ auto number = vm.argument(0).to_number(global_object);
+ if (vm.exception())
+ return {};
+ if (number.is_nan() || number.is_positive_zero() || number.is_negative_zero())
+ return number;
+ return Value(::asin(number.as_double()));
+}
+
+JS_DEFINE_NATIVE_FUNCTION(MathObject::asinh)
+{
+ auto number = vm.argument(0).to_number(global_object);
+ if (vm.exception())
+ return {};
+ return Value(::asinh(number.as_double()));
+}
+
+JS_DEFINE_NATIVE_FUNCTION(MathObject::atan)
+{
+ auto number = vm.argument(0).to_number(global_object);
+ if (vm.exception())
+ return {};
+ if (number.is_nan() || number.is_positive_zero() || number.is_negative_zero())
+ return number;
+ if (number.is_positive_infinity())
+ return Value(M_PI_2);
+ if (number.is_negative_infinity())
+ return Value(-M_PI_2);
+ return Value(::atan(number.as_double()));
+}
+
+JS_DEFINE_NATIVE_FUNCTION(MathObject::atanh)
+{
+ auto number = vm.argument(0).to_number(global_object);
+ if (vm.exception())
+ return {};
+ if (number.as_double() > 1 || number.as_double() < -1)
+ return js_nan();
+ return Value(::atanh(number.as_double()));
+}
+
+JS_DEFINE_NATIVE_FUNCTION(MathObject::log1p)
+{
+ auto number = vm.argument(0).to_number(global_object);
+ if (vm.exception())
+ return {};
+ if (number.as_double() < -1)
+ return js_nan();
+ return Value(::log1p(number.as_double()));
+}
+
+JS_DEFINE_NATIVE_FUNCTION(MathObject::cbrt)
+{
+ auto number = vm.argument(0).to_number(global_object);
+ if (vm.exception())
+ return {};
+ return Value(::cbrt(number.as_double()));
+}
+
+JS_DEFINE_NATIVE_FUNCTION(MathObject::atan2)
+{
+ auto y = vm.argument(0).to_number(global_object), x = vm.argument(1).to_number(global_object);
+ auto pi_4 = M_PI_2 / 2;
+ auto three_pi_4 = pi_4 + M_PI_2;
+ if (vm.exception())
+ return {};
+ if (x.is_positive_zero()) {
+ if (y.is_positive_zero() || y.is_negative_zero())
+ return y;
+ else
+ return (y.as_double() > 0) ? Value(M_PI_2) : Value(-M_PI_2);
+ }
+ if (x.is_negative_zero()) {
+ if (y.is_positive_zero())
+ return Value(M_PI);
+ else if (y.is_negative_zero())
+ return Value(-M_PI);
+ else
+ return (y.as_double() > 0) ? Value(M_PI_2) : Value(-M_PI_2);
+ }
+ if (x.is_positive_infinity()) {
+ if (y.is_infinity())
+ return (y.is_positive_infinity()) ? Value(pi_4) : Value(-pi_4);
+ else
+ return (y.as_double() > 0) ? Value(+0.0) : Value(-0.0);
+ }
+ if (x.is_negative_infinity()) {
+ if (y.is_infinity())
+ return (y.is_positive_infinity()) ? Value(three_pi_4) : Value(-three_pi_4);
+ else
+ return (y.as_double() > 0) ? Value(M_PI) : Value(-M_PI);
+ }
+ if (y.is_infinity())
+ return (y.is_positive_infinity()) ? Value(M_PI_2) : Value(-M_PI_2);
+ if (y.is_positive_zero())
+ return (x.as_double() > 0) ? Value(+0.0) : Value(M_PI);
+ if (y.is_negative_zero())
+ return (x.as_double() > 0) ? Value(-0.0) : Value(-M_PI);
+
+ return Value(::atan2(y.as_double(), x.as_double()));
+}
+
+JS_DEFINE_NATIVE_FUNCTION(MathObject::fround)
+{
+ auto number = vm.argument(0).to_number(global_object);
+ if (vm.exception())
+ return {};
+ if (number.is_nan())
+ return js_nan();
+ return Value((float)number.as_double());
+}
+
+JS_DEFINE_NATIVE_FUNCTION(MathObject::hypot)
+{
+ if (!vm.argument_count())
+ return Value(0);
+
+ auto hypot = vm.argument(0).to_number(global_object);
+ if (vm.exception())
+ return {};
+ hypot = Value(hypot.as_double() * hypot.as_double());
+ for (size_t i = 1; i < vm.argument_count(); ++i) {
+ auto cur = vm.argument(i).to_number(global_object);
+ if (vm.exception())
+ return {};
+ hypot = Value(hypot.as_double() + cur.as_double() * cur.as_double());
+ }
+ return Value(::sqrt(hypot.as_double()));
+}
+
+JS_DEFINE_NATIVE_FUNCTION(MathObject::log)
+{
+ auto number = vm.argument(0).to_number(global_object);
+ if (vm.exception())
+ return {};
+ if (number.as_double() < 0)
+ return js_nan();
+ return Value(::log(number.as_double()));
+}
+
+JS_DEFINE_NATIVE_FUNCTION(MathObject::log2)
+{
+ auto number = vm.argument(0).to_number(global_object);
+ if (vm.exception())
+ return {};
+ if (number.as_double() < 0)
+ return js_nan();
+ return Value(::log2(number.as_double()));
+}
+
+JS_DEFINE_NATIVE_FUNCTION(MathObject::log10)
+{
+ auto number = vm.argument(0).to_number(global_object);
+ if (vm.exception())
+ return {};
+ if (number.as_double() < 0)
+ return js_nan();
+ return Value(::log10(number.as_double()));
+}
+
+JS_DEFINE_NATIVE_FUNCTION(MathObject::sinh)
+{
+ auto number = vm.argument(0).to_number(global_object);
+ if (vm.exception())
+ return {};
+ if (number.is_nan())
+ return js_nan();
+ return Value(::sinh(number.as_double()));
+}
+
+JS_DEFINE_NATIVE_FUNCTION(MathObject::cosh)
+{
+ auto number = vm.argument(0).to_number(global_object);
+ if (vm.exception())
+ return {};
+ if (number.is_nan())
+ return js_nan();
+ return Value(::cosh(number.as_double()));
+}
+
+JS_DEFINE_NATIVE_FUNCTION(MathObject::tanh)
+{
+ auto number = vm.argument(0).to_number(global_object);
+ if (vm.exception())
+ return {};
+ if (number.is_nan())
+ return js_nan();
+ if (number.is_positive_infinity())
+ return Value(1);
+ if (number.is_negative_infinity())
+ return Value(-1);
+ return Value(::tanh(number.as_double()));
+}
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/MathObject.h b/Userland/Libraries/LibJS/Runtime/MathObject.h
new file mode 100644
index 0000000000..06f09c2e4a
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/MathObject.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * 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 MathObject final : public Object {
+ JS_OBJECT(MathObject, Object);
+
+public:
+ explicit MathObject(GlobalObject&);
+ virtual void initialize(GlobalObject&) override;
+ virtual ~MathObject() override;
+
+private:
+ JS_DECLARE_NATIVE_FUNCTION(abs);
+ JS_DECLARE_NATIVE_FUNCTION(random);
+ JS_DECLARE_NATIVE_FUNCTION(sqrt);
+ JS_DECLARE_NATIVE_FUNCTION(floor);
+ JS_DECLARE_NATIVE_FUNCTION(ceil);
+ JS_DECLARE_NATIVE_FUNCTION(round);
+ JS_DECLARE_NATIVE_FUNCTION(max);
+ JS_DECLARE_NATIVE_FUNCTION(min);
+ JS_DECLARE_NATIVE_FUNCTION(trunc);
+ JS_DECLARE_NATIVE_FUNCTION(sin);
+ JS_DECLARE_NATIVE_FUNCTION(cos);
+ JS_DECLARE_NATIVE_FUNCTION(tan);
+ JS_DECLARE_NATIVE_FUNCTION(pow);
+ JS_DECLARE_NATIVE_FUNCTION(exp);
+ JS_DECLARE_NATIVE_FUNCTION(expm1);
+ JS_DECLARE_NATIVE_FUNCTION(sign);
+ JS_DECLARE_NATIVE_FUNCTION(clz32);
+ JS_DECLARE_NATIVE_FUNCTION(acos);
+ JS_DECLARE_NATIVE_FUNCTION(acosh);
+ JS_DECLARE_NATIVE_FUNCTION(asin);
+ JS_DECLARE_NATIVE_FUNCTION(asinh);
+ JS_DECLARE_NATIVE_FUNCTION(atan);
+ JS_DECLARE_NATIVE_FUNCTION(atanh);
+ JS_DECLARE_NATIVE_FUNCTION(log1p);
+ JS_DECLARE_NATIVE_FUNCTION(cbrt);
+ JS_DECLARE_NATIVE_FUNCTION(atan2);
+ JS_DECLARE_NATIVE_FUNCTION(fround);
+ JS_DECLARE_NATIVE_FUNCTION(hypot);
+ JS_DECLARE_NATIVE_FUNCTION(log);
+ JS_DECLARE_NATIVE_FUNCTION(log2);
+ JS_DECLARE_NATIVE_FUNCTION(log10);
+ JS_DECLARE_NATIVE_FUNCTION(sinh);
+ JS_DECLARE_NATIVE_FUNCTION(cosh);
+ JS_DECLARE_NATIVE_FUNCTION(tanh);
+};
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/NativeFunction.cpp b/Userland/Libraries/LibJS/Runtime/NativeFunction.cpp
new file mode 100644
index 0000000000..8453724414
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/NativeFunction.cpp
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * 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/LexicalEnvironment.h>
+#include <LibJS/Runtime/NativeFunction.h>
+#include <LibJS/Runtime/Value.h>
+
+namespace JS {
+
+NativeFunction* NativeFunction::create(GlobalObject& global_object, const FlyString& name, AK::Function<Value(VM&, GlobalObject&)> function)
+{
+ return global_object.heap().allocate<NativeFunction>(global_object, name, move(function), *global_object.function_prototype());
+}
+
+NativeFunction::NativeFunction(Object& prototype)
+ : Function(prototype)
+{
+}
+
+NativeFunction::NativeFunction(const FlyString& name, AK::Function<Value(VM&, GlobalObject&)> native_function, Object& prototype)
+ : Function(prototype)
+ , m_name(name)
+ , m_native_function(move(native_function))
+{
+}
+
+NativeFunction::NativeFunction(const FlyString& name, Object& prototype)
+ : Function(prototype)
+ , m_name(name)
+{
+}
+
+NativeFunction::~NativeFunction()
+{
+}
+
+Value NativeFunction::call()
+{
+ return m_native_function(vm(), global_object());
+}
+
+Value NativeFunction::construct(Function&)
+{
+ return {};
+}
+
+LexicalEnvironment* NativeFunction::create_environment()
+{
+ return heap().allocate<LexicalEnvironment>(global_object(), LexicalEnvironment::EnvironmentRecordType::Function);
+}
+
+bool NativeFunction::is_strict_mode() const
+{
+ return vm().in_strict_mode();
+}
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/NativeFunction.h b/Userland/Libraries/LibJS/Runtime/NativeFunction.h
new file mode 100644
index 0000000000..a37336329f
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/NativeFunction.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * 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 <AK/Function.h>
+#include <LibJS/Runtime/Function.h>
+
+namespace JS {
+
+class NativeFunction : public Function {
+ JS_OBJECT(NativeFunction, Function);
+
+public:
+ static NativeFunction* create(GlobalObject&, const FlyString& name, AK::Function<Value(VM&, GlobalObject&)>);
+
+ explicit NativeFunction(const FlyString& name, AK::Function<Value(VM&, GlobalObject&)>, Object& prototype);
+ virtual void initialize(GlobalObject&) override { }
+ virtual ~NativeFunction() override;
+
+ virtual Value call() override;
+ virtual Value construct(Function& new_target) override;
+
+ virtual const FlyString& name() const override { return m_name; };
+ virtual bool has_constructor() const { return false; }
+
+ virtual bool is_strict_mode() const override;
+
+protected:
+ NativeFunction(const FlyString& name, Object& prototype);
+ explicit NativeFunction(Object& prototype);
+
+private:
+ virtual LexicalEnvironment* create_environment() override final;
+
+ FlyString m_name;
+ AK::Function<Value(VM&, GlobalObject&)> m_native_function;
+};
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/NativeProperty.cpp b/Userland/Libraries/LibJS/Runtime/NativeProperty.cpp
new file mode 100644
index 0000000000..d52e7995fa
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/NativeProperty.cpp
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * 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/NativeProperty.h>
+#include <LibJS/Runtime/Value.h>
+
+namespace JS {
+
+NativeProperty::NativeProperty(AK::Function<Value(VM&, GlobalObject&)> getter, AK::Function<void(VM&, GlobalObject&, Value)> setter)
+ : m_getter(move(getter))
+ , m_setter(move(setter))
+{
+}
+
+NativeProperty::~NativeProperty()
+{
+}
+
+Value NativeProperty::get(VM& vm, GlobalObject& global_object) const
+{
+ if (!m_getter)
+ return js_undefined();
+ return m_getter(vm, global_object);
+}
+
+void NativeProperty::set(VM& vm, GlobalObject& global_object, Value value)
+{
+ if (!m_setter)
+ return;
+ m_setter(vm, global_object, move(value));
+}
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/NativeProperty.h b/Userland/Libraries/LibJS/Runtime/NativeProperty.h
new file mode 100644
index 0000000000..e0bb4140b7
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/NativeProperty.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * 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 <AK/Function.h>
+#include <LibJS/Runtime/Object.h>
+
+namespace JS {
+
+class NativeProperty final : public Cell {
+public:
+ NativeProperty(AK::Function<Value(VM&, GlobalObject&)> getter, AK::Function<void(VM&, GlobalObject&, Value)> setter);
+ virtual ~NativeProperty() override;
+
+ Value get(VM&, GlobalObject&) const;
+ void set(VM&, GlobalObject&, Value);
+
+private:
+ virtual const char* class_name() const override { return "NativeProperty"; }
+
+ AK::Function<Value(VM&, GlobalObject&)> m_getter;
+ AK::Function<void(VM&, GlobalObject&, Value)> m_setter;
+};
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/NumberConstructor.cpp b/Userland/Libraries/LibJS/Runtime/NumberConstructor.cpp
new file mode 100644
index 0000000000..c95441166e
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/NumberConstructor.cpp
@@ -0,0 +1,115 @@
+/*
+ * Copyright (c) 2020, Linus Groh <mail@linusgroh.de>
+ * 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/Error.h>
+#include <LibJS/Runtime/GlobalObject.h>
+#include <LibJS/Runtime/NumberConstructor.h>
+#include <LibJS/Runtime/NumberObject.h>
+#include <math.h>
+
+#ifdef __clang__
+# define EPSILON_VALUE pow(2, -52)
+# define MAX_SAFE_INTEGER_VALUE pow(2, 53) - 1
+# define MIN_SAFE_INTEGER_VALUE -(pow(2, 53) - 1)
+#else
+constexpr const double EPSILON_VALUE { __builtin_pow(2, -52) };
+constexpr const double MAX_SAFE_INTEGER_VALUE { __builtin_pow(2, 53) - 1 };
+constexpr const double MIN_SAFE_INTEGER_VALUE { -(__builtin_pow(2, 53) - 1) };
+#endif
+
+namespace JS {
+
+NumberConstructor::NumberConstructor(GlobalObject& global_object)
+ : NativeFunction(vm().names.Number, *global_object.function_prototype())
+{
+}
+
+void NumberConstructor::initialize(GlobalObject& global_object)
+{
+ auto& vm = this->vm();
+ NativeFunction::initialize(global_object);
+ u8 attr = Attribute::Writable | Attribute::Configurable;
+ define_native_function(vm.names.isFinite, is_finite, 1, attr);
+ define_native_function(vm.names.isInteger, is_integer, 1, attr);
+ define_native_function(vm.names.isNaN, is_nan, 1, attr);
+ define_native_function(vm.names.isSafeInteger, is_safe_integer, 1, attr);
+ define_property(vm.names.parseFloat, global_object.get(vm.names.parseFloat));
+ define_property(vm.names.prototype, global_object.number_prototype(), 0);
+ define_property(vm.names.length, Value(1), Attribute::Configurable);
+ define_property(vm.names.EPSILON, Value(EPSILON_VALUE), 0);
+ define_property(vm.names.MAX_SAFE_INTEGER, Value(MAX_SAFE_INTEGER_VALUE), 0);
+ define_property(vm.names.MIN_SAFE_INTEGER, Value(MIN_SAFE_INTEGER_VALUE), 0);
+ define_property(vm.names.NEGATIVE_INFINITY, js_negative_infinity(), 0);
+ define_property(vm.names.POSITIVE_INFINITY, js_infinity(), 0);
+ define_property(vm.names.NaN, js_nan(), 0);
+}
+
+NumberConstructor::~NumberConstructor()
+{
+}
+
+Value NumberConstructor::call()
+{
+ if (!vm().argument_count())
+ return Value(0);
+ return vm().argument(0).to_number(global_object());
+}
+
+Value NumberConstructor::construct(Function&)
+{
+ double number = 0;
+ if (vm().argument_count()) {
+ number = vm().argument(0).to_double(global_object());
+ if (vm().exception())
+ return {};
+ }
+ return NumberObject::create(global_object(), number);
+}
+
+JS_DEFINE_NATIVE_FUNCTION(NumberConstructor::is_finite)
+{
+ return Value(vm.argument(0).is_finite_number());
+}
+
+JS_DEFINE_NATIVE_FUNCTION(NumberConstructor::is_integer)
+{
+ return Value(vm.argument(0).is_integer());
+}
+
+JS_DEFINE_NATIVE_FUNCTION(NumberConstructor::is_nan)
+{
+ return Value(vm.argument(0).is_nan());
+}
+
+JS_DEFINE_NATIVE_FUNCTION(NumberConstructor::is_safe_integer)
+{
+ if (!vm.argument(0).is_number())
+ return Value(false);
+ auto value = vm.argument(0).as_double();
+ return Value((int64_t)value == value && value >= MIN_SAFE_INTEGER_VALUE && value <= MAX_SAFE_INTEGER_VALUE);
+}
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/NumberConstructor.h b/Userland/Libraries/LibJS/Runtime/NumberConstructor.h
new file mode 100644
index 0000000000..0ed58b70cb
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/NumberConstructor.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2020, Linus Groh <mail@linusgroh.de>
+ * 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/NativeFunction.h>
+
+namespace JS {
+
+class NumberConstructor final : public NativeFunction {
+ JS_OBJECT(NumberConstructor, NativeFunction);
+
+public:
+ explicit NumberConstructor(GlobalObject&);
+ virtual void initialize(GlobalObject&) override;
+ virtual ~NumberConstructor() override;
+
+ virtual Value call() override;
+ virtual Value construct(Function& new_target) override;
+
+private:
+ virtual bool has_constructor() const override { return true; }
+
+ JS_DECLARE_NATIVE_FUNCTION(is_finite);
+ JS_DECLARE_NATIVE_FUNCTION(is_integer);
+ JS_DECLARE_NATIVE_FUNCTION(is_nan);
+ JS_DECLARE_NATIVE_FUNCTION(is_safe_integer);
+};
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/NumberObject.cpp b/Userland/Libraries/LibJS/Runtime/NumberObject.cpp
new file mode 100644
index 0000000000..1376be1571
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/NumberObject.cpp
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * 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/Heap/Heap.h>
+#include <LibJS/Runtime/GlobalObject.h>
+#include <LibJS/Runtime/NumberObject.h>
+#include <LibJS/Runtime/NumberPrototype.h>
+#include <LibJS/Runtime/Value.h>
+
+namespace JS {
+
+NumberObject* NumberObject::create(GlobalObject& global_object, double value)
+{
+ return global_object.heap().allocate<NumberObject>(global_object, value, *global_object.number_prototype());
+}
+
+NumberObject::NumberObject(double value, Object& prototype)
+ : Object(prototype)
+ , m_value(value)
+{
+}
+
+NumberObject::~NumberObject()
+{
+}
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/NumberObject.h b/Userland/Libraries/LibJS/Runtime/NumberObject.h
new file mode 100644
index 0000000000..7ae5e71451
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/NumberObject.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * 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 NumberObject : public Object {
+ JS_OBJECT(NumberObject, Object);
+
+public:
+ static NumberObject* create(GlobalObject&, double);
+
+ NumberObject(double, Object& prototype);
+ virtual ~NumberObject() override;
+
+ virtual Value value_of() const override { return Value(m_value); }
+
+ double number() const { return m_value; }
+
+private:
+ double m_value { 0 };
+};
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/NumberPrototype.cpp b/Userland/Libraries/LibJS/Runtime/NumberPrototype.cpp
new file mode 100644
index 0000000000..94add9a146
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/NumberPrototype.cpp
@@ -0,0 +1,146 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * 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 <AK/Function.h>
+#include <LibJS/Runtime/Error.h>
+#include <LibJS/Runtime/GlobalObject.h>
+#include <LibJS/Runtime/NumberObject.h>
+#include <LibJS/Runtime/NumberPrototype.h>
+
+namespace JS {
+
+static const u8 max_precision_for_radix[37] = {
+ // clang-format off
+ 0, 0, 52, 32, 26, 22, 20, 18, 17, 16,
+ 15, 15, 14, 14, 13, 13, 13, 12, 12, 12,
+ 12, 11, 11, 11, 11, 11, 11, 10, 10, 10,
+ 10, 10, 10, 10, 10, 10, 10,
+ // clang-format on
+};
+
+static char digits[] = "0123456789abcdefghijklmnopqrstuvwxyz";
+
+NumberPrototype::NumberPrototype(GlobalObject& global_object)
+ : NumberObject(0, *global_object.object_prototype())
+{
+}
+
+void NumberPrototype::initialize(GlobalObject& object)
+{
+ auto& vm = this->vm();
+ Object::initialize(object);
+ define_native_function(vm.names.toString, to_string, 1, Attribute::Configurable | Attribute::Writable);
+}
+
+NumberPrototype::~NumberPrototype()
+{
+}
+
+JS_DEFINE_NATIVE_FUNCTION(NumberPrototype::to_string)
+{
+ Value number_value;
+
+ auto this_value = vm.this_value(global_object);
+ if (this_value.is_number()) {
+ number_value = this_value;
+ } else if (this_value.is_object() && is<NumberObject>(this_value.as_object())) {
+ number_value = static_cast<NumberObject&>(this_value.as_object()).value_of();
+ } else {
+ vm.throw_exception<TypeError>(global_object, ErrorType::NumberIncompatibleThis, "toString");
+ return {};
+ }
+
+ int radix;
+ auto argument = vm.argument(0);
+ if (argument.is_undefined()) {
+ radix = 10;
+ } else {
+ radix = argument.to_i32(global_object);
+ }
+
+ if (vm.exception() || radix < 2 || radix > 36) {
+ vm.throw_exception<RangeError>(global_object, ErrorType::InvalidRadix);
+ return {};
+ }
+
+ if (number_value.is_positive_infinity())
+ return js_string(vm, "Infinity");
+ if (number_value.is_negative_infinity())
+ return js_string(vm, "-Infinity");
+ if (number_value.is_nan())
+ return js_string(vm, "NaN");
+ if (number_value.is_positive_zero() || number_value.is_negative_zero())
+ return js_string(vm, "0");
+
+ double number = number_value.as_double();
+ bool negative = number < 0;
+ if (negative)
+ number *= -1;
+
+ int int_part = floor(number);
+ double decimal_part = number - int_part;
+
+ Vector<char> backwards_characters;
+
+ if (int_part == 0) {
+ backwards_characters.append('0');
+ } else {
+ while (int_part > 0) {
+ backwards_characters.append(digits[int_part % radix]);
+ int_part /= radix;
+ }
+ }
+
+ Vector<char> characters;
+ if (negative)
+ characters.append('-');
+
+ // Reverse characters;
+ for (ssize_t i = backwards_characters.size() - 1; i >= 0; --i) {
+ characters.append(backwards_characters[i]);
+ }
+
+ // decimal part
+ if (decimal_part != 0.0) {
+ characters.append('.');
+
+ int precision = max_precision_for_radix[radix];
+
+ for (int i = 0; i < precision; ++i) {
+ decimal_part *= radix;
+ int integral = floor(decimal_part);
+ characters.append(digits[integral]);
+ decimal_part -= integral;
+ }
+
+ while (characters.last() == '0')
+ characters.take_last();
+ }
+
+ return js_string(vm, String(characters.data(), characters.size()));
+}
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/NumberPrototype.h b/Userland/Libraries/LibJS/Runtime/NumberPrototype.h
new file mode 100644
index 0000000000..a2b94143f1
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/NumberPrototype.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * 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/NumberObject.h>
+
+namespace JS {
+
+class NumberPrototype final : public NumberObject {
+ JS_OBJECT(NumberPrototype, NumberObject);
+
+public:
+ explicit NumberPrototype(GlobalObject&);
+ virtual void initialize(GlobalObject&) override;
+ virtual ~NumberPrototype() override;
+
+ JS_DECLARE_NATIVE_FUNCTION(to_string);
+};
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/Object.cpp b/Userland/Libraries/LibJS/Runtime/Object.cpp
new file mode 100644
index 0000000000..87a9d75dd5
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/Object.cpp
@@ -0,0 +1,927 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * 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 <AK/String.h>
+#include <AK/TemporaryChange.h>
+#include <LibJS/Heap/Heap.h>
+#include <LibJS/Runtime/Accessor.h>
+#include <LibJS/Runtime/Array.h>
+#include <LibJS/Runtime/Error.h>
+#include <LibJS/Runtime/GlobalObject.h>
+#include <LibJS/Runtime/NativeFunction.h>
+#include <LibJS/Runtime/NativeProperty.h>
+#include <LibJS/Runtime/Object.h>
+#include <LibJS/Runtime/Shape.h>
+#include <LibJS/Runtime/StringObject.h>
+#include <LibJS/Runtime/Value.h>
+
+//#define OBJECT_DEBUG
+
+namespace JS {
+
+PropertyDescriptor PropertyDescriptor::from_dictionary(VM& vm, const Object& object)
+{
+ PropertyAttributes attributes;
+ if (object.has_property(vm.names.configurable)) {
+ attributes.set_has_configurable();
+ if (object.get(vm.names.configurable).value_or(Value(false)).to_boolean())
+ attributes.set_configurable();
+ if (vm.exception())
+ return {};
+ }
+ if (object.has_property(vm.names.enumerable)) {
+ attributes.set_has_enumerable();
+ if (object.get(vm.names.enumerable).value_or(Value(false)).to_boolean())
+ attributes.set_enumerable();
+ if (vm.exception())
+ return {};
+ }
+ if (object.has_property(vm.names.writable)) {
+ attributes.set_has_writable();
+ if (object.get(vm.names.writable).value_or(Value(false)).to_boolean())
+ attributes.set_writable();
+ if (vm.exception())
+ return {};
+ }
+ PropertyDescriptor descriptor { attributes, object.get(vm.names.value), nullptr, nullptr };
+ if (vm.exception())
+ return {};
+ auto getter = object.get(vm.names.get);
+ if (vm.exception())
+ return {};
+ if (getter.is_function())
+ descriptor.getter = &getter.as_function();
+ auto setter = object.get(vm.names.set);
+ if (vm.exception())
+ return {};
+ if (setter.is_function())
+ descriptor.setter = &setter.as_function();
+ return descriptor;
+}
+
+Object* Object::create_empty(GlobalObject& global_object)
+{
+ return global_object.heap().allocate<Object>(global_object, *global_object.new_object_shape());
+}
+
+Object::Object(GlobalObjectTag)
+{
+ // This is the global object
+ m_shape = heap().allocate_without_global_object<Shape>(*this);
+}
+
+Object::Object(ConstructWithoutPrototypeTag, GlobalObject& global_object)
+{
+ m_shape = heap().allocate_without_global_object<Shape>(global_object);
+}
+
+Object::Object(Object& prototype)
+{
+ m_shape = prototype.global_object().empty_object_shape();
+ set_prototype(&prototype);
+}
+
+Object::Object(Shape& shape)
+ : m_shape(&shape)
+{
+ m_storage.resize(shape.property_count());
+}
+
+void Object::initialize(GlobalObject&)
+{
+}
+
+Object::~Object()
+{
+}
+
+Object* Object::prototype()
+{
+ return shape().prototype();
+}
+
+const Object* Object::prototype() const
+{
+ return shape().prototype();
+}
+
+bool Object::set_prototype(Object* new_prototype)
+{
+ if (prototype() == new_prototype)
+ return true;
+ if (!m_is_extensible)
+ return false;
+ if (shape().is_unique()) {
+ shape().set_prototype_without_transition(new_prototype);
+ return true;
+ }
+ m_shape = m_shape->create_prototype_transition(new_prototype);
+ return true;
+}
+
+bool Object::has_prototype(const Object* prototype) const
+{
+ for (auto* object = this->prototype(); object; object = object->prototype()) {
+ if (vm().exception())
+ return false;
+ if (object == prototype)
+ return true;
+ }
+ return false;
+}
+
+bool Object::prevent_extensions()
+{
+ m_is_extensible = false;
+ return true;
+}
+
+Value Object::get_own_property(const PropertyName& property_name, Value receiver) const
+{
+ ASSERT(property_name.is_valid());
+ ASSERT(!receiver.is_empty());
+
+ Value value_here;
+
+ if (property_name.is_number()) {
+ auto existing_property = m_indexed_properties.get(nullptr, property_name.as_number(), false);
+ if (!existing_property.has_value())
+ return {};
+ value_here = existing_property.value().value.value_or(js_undefined());
+ } else {
+ auto metadata = shape().lookup(property_name.to_string_or_symbol());
+ if (!metadata.has_value())
+ return {};
+ value_here = m_storage[metadata.value().offset].value_or(js_undefined());
+ }
+
+ ASSERT(!value_here.is_empty());
+ if (value_here.is_accessor())
+ return value_here.as_accessor().call_getter(receiver);
+ if (value_here.is_native_property())
+ return call_native_property_getter(value_here.as_native_property(), receiver);
+ return value_here;
+}
+
+Value Object::get_own_properties(const Object& this_object, PropertyKind kind, bool only_enumerable_properties, GetOwnPropertyReturnType return_type) const
+{
+ auto* properties_array = Array::create(global_object());
+
+ // FIXME: Support generic iterables
+ if (is<StringObject>(this_object)) {
+ auto str = static_cast<const StringObject&>(this_object).primitive_string().string();
+
+ for (size_t i = 0; i < str.length(); ++i) {
+ if (kind == PropertyKind::Key) {
+ properties_array->define_property(i, js_string(vm(), String::number(i)));
+ } else if (kind == PropertyKind::Value) {
+ properties_array->define_property(i, js_string(vm(), String::formatted("{:c}", str[i])));
+ } else {
+ auto* entry_array = Array::create(global_object());
+ entry_array->define_property(0, js_string(vm(), String::number(i)));
+ entry_array->define_property(1, js_string(vm(), String::formatted("{:c}", str[i])));
+ properties_array->define_property(i, entry_array);
+ }
+ if (vm().exception())
+ return {};
+ }
+
+ return properties_array;
+ }
+
+ size_t property_index = 0;
+ for (auto& entry : m_indexed_properties) {
+ auto value_and_attributes = entry.value_and_attributes(const_cast<Object*>(&this_object));
+ if (only_enumerable_properties && !value_and_attributes.attributes.is_enumerable())
+ continue;
+
+ if (kind == PropertyKind::Key) {
+ properties_array->define_property(property_index, js_string(vm(), String::number(entry.index())));
+ } else if (kind == PropertyKind::Value) {
+ properties_array->define_property(property_index, value_and_attributes.value);
+ } else {
+ auto* entry_array = Array::create(global_object());
+ entry_array->define_property(0, js_string(vm(), String::number(entry.index())));
+ entry_array->define_property(1, value_and_attributes.value);
+ properties_array->define_property(property_index, entry_array);
+ }
+ if (vm().exception())
+ return {};
+
+ ++property_index;
+ }
+
+ for (auto& it : this_object.shape().property_table_ordered()) {
+ if (only_enumerable_properties && !it.value.attributes.is_enumerable())
+ continue;
+
+ if (return_type == GetOwnPropertyReturnType::StringOnly && it.key.is_symbol())
+ continue;
+ if (return_type == GetOwnPropertyReturnType::SymbolOnly && it.key.is_string())
+ continue;
+
+ if (kind == PropertyKind::Key) {
+ properties_array->define_property(property_index, it.key.to_value(vm()));
+ } else if (kind == PropertyKind::Value) {
+ properties_array->define_property(property_index, this_object.get(it.key));
+ } else {
+ auto* entry_array = Array::create(global_object());
+ entry_array->define_property(0, it.key.to_value(vm()));
+ entry_array->define_property(1, this_object.get(it.key));
+ properties_array->define_property(property_index, entry_array);
+ }
+ if (vm().exception())
+ return {};
+
+ ++property_index;
+ }
+
+ return properties_array;
+}
+
+Optional<PropertyDescriptor> Object::get_own_property_descriptor(const PropertyName& property_name) const
+{
+ ASSERT(property_name.is_valid());
+
+ Value value;
+ PropertyAttributes attributes;
+
+ if (property_name.is_number()) {
+ auto existing_value = m_indexed_properties.get(nullptr, property_name.as_number(), false);
+ if (!existing_value.has_value())
+ return {};
+ value = existing_value.value().value;
+ attributes = existing_value.value().attributes;
+ attributes = default_attributes;
+ } else {
+ auto metadata = shape().lookup(property_name.to_string_or_symbol());
+ if (!metadata.has_value())
+ return {};
+ value = m_storage[metadata.value().offset];
+ if (vm().exception())
+ return {};
+ attributes = metadata.value().attributes;
+ }
+
+ PropertyDescriptor descriptor { attributes, {}, nullptr, nullptr };
+ if (value.is_native_property()) {
+ auto result = call_native_property_getter(value.as_native_property(), const_cast<Object*>(this));
+ descriptor.value = result.value_or(js_undefined());
+ } else if (value.is_accessor()) {
+ auto& pair = value.as_accessor();
+ if (pair.getter())
+ descriptor.getter = pair.getter();
+ if (pair.setter())
+ descriptor.setter = pair.setter();
+ } else {
+ descriptor.value = value.value_or(js_undefined());
+ }
+
+ return descriptor;
+}
+
+Value Object::get_own_property_descriptor_object(const PropertyName& property_name) const
+{
+ ASSERT(property_name.is_valid());
+
+ auto& vm = this->vm();
+ auto descriptor_opt = get_own_property_descriptor(property_name);
+ if (!descriptor_opt.has_value())
+ return js_undefined();
+ auto descriptor = descriptor_opt.value();
+
+ auto* descriptor_object = Object::create_empty(global_object());
+ descriptor_object->define_property(vm.names.enumerable, Value(descriptor.attributes.is_enumerable()));
+ if (vm.exception())
+ return {};
+ descriptor_object->define_property(vm.names.configurable, Value(descriptor.attributes.is_configurable()));
+ if (vm.exception())
+ return {};
+ if (descriptor.is_data_descriptor()) {
+ descriptor_object->define_property(vm.names.value, descriptor.value.value_or(js_undefined()));
+ if (vm.exception())
+ return {};
+ descriptor_object->define_property(vm.names.writable, Value(descriptor.attributes.is_writable()));
+ if (vm.exception())
+ return {};
+ } else if (descriptor.is_accessor_descriptor()) {
+ if (descriptor.getter) {
+ descriptor_object->define_property(vm.names.get, Value(descriptor.getter));
+ if (vm.exception())
+ return {};
+ }
+ if (descriptor.setter) {
+ descriptor_object->define_property(vm.names.set, Value(descriptor.setter));
+ if (vm.exception())
+ return {};
+ }
+ }
+ return descriptor_object;
+}
+
+void Object::set_shape(Shape& new_shape)
+{
+ m_storage.resize(new_shape.property_count());
+ m_shape = &new_shape;
+}
+
+bool Object::define_property(const StringOrSymbol& property_name, const Object& descriptor, bool throw_exceptions)
+{
+ auto& vm = this->vm();
+ bool is_accessor_property = descriptor.has_property(vm.names.get) || descriptor.has_property(vm.names.set);
+ PropertyAttributes attributes;
+ if (descriptor.has_property(vm.names.configurable)) {
+ attributes.set_has_configurable();
+ if (descriptor.get(vm.names.configurable).value_or(Value(false)).to_boolean())
+ attributes.set_configurable();
+ if (vm.exception())
+ return false;
+ }
+ if (descriptor.has_property(vm.names.enumerable)) {
+ attributes.set_has_enumerable();
+ if (descriptor.get(vm.names.enumerable).value_or(Value(false)).to_boolean())
+ attributes.set_enumerable();
+ if (vm.exception())
+ return false;
+ }
+
+ if (is_accessor_property) {
+ if (descriptor.has_property(vm.names.value) || descriptor.has_property(vm.names.writable)) {
+ if (throw_exceptions)
+ vm.throw_exception<TypeError>(global_object(), ErrorType::AccessorValueOrWritable);
+ return false;
+ }
+
+ auto getter = descriptor.get(vm.names.get).value_or(js_undefined());
+ if (vm.exception())
+ return {};
+ auto setter = descriptor.get(vm.names.set).value_or(js_undefined());
+ if (vm.exception())
+ return {};
+
+ Function* getter_function { nullptr };
+ Function* setter_function { nullptr };
+
+ if (getter.is_function()) {
+ getter_function = &getter.as_function();
+ } else if (!getter.is_undefined()) {
+ vm.throw_exception<TypeError>(global_object(), ErrorType::AccessorBadField, "get");
+ return false;
+ }
+
+ if (setter.is_function()) {
+ setter_function = &setter.as_function();
+ } else if (!setter.is_undefined()) {
+ vm.throw_exception<TypeError>(global_object(), ErrorType::AccessorBadField, "set");
+ return false;
+ }
+
+#ifdef OBJECT_DEBUG
+ dbgln("Defining new property {} with accessor descriptor {{ attributes={}, getter={}, setter={} }}", property_name.to_display_string(), attributes, getter, setter);
+#endif
+
+ return define_property(property_name, Accessor::create(vm, getter_function, setter_function), attributes, throw_exceptions);
+ }
+
+ auto value = descriptor.get(vm.names.value);
+ if (vm.exception())
+ return {};
+ if (descriptor.has_property(vm.names.writable)) {
+ attributes.set_has_writable();
+ if (descriptor.get(vm.names.writable).value_or(Value(false)).to_boolean())
+ attributes.set_writable();
+ if (vm.exception())
+ return false;
+ }
+ if (vm.exception())
+ return {};
+
+#ifdef OBJECT_DEBUG
+ dbgln("Defining new property {} with data descriptor {{ attributes={}, value={} }}", property_name.to_display_string(), attributes, value);
+#endif
+
+ return define_property(property_name, value, attributes, throw_exceptions);
+}
+
+bool Object::define_property_without_transition(const PropertyName& property_name, Value value, PropertyAttributes attributes, bool throw_exceptions)
+{
+ TemporaryChange change(m_transitions_enabled, false);
+ return define_property(property_name, value, attributes, throw_exceptions);
+}
+
+bool Object::define_property(const PropertyName& property_name, Value value, PropertyAttributes attributes, bool throw_exceptions)
+{
+ ASSERT(property_name.is_valid());
+
+ if (property_name.is_number())
+ return put_own_property_by_index(*this, property_name.as_number(), value, attributes, PutOwnPropertyMode::DefineProperty, throw_exceptions);
+
+ if (property_name.is_string()) {
+ i32 property_index = property_name.as_string().to_int().value_or(-1);
+ if (property_index >= 0)
+ return put_own_property_by_index(*this, property_index, value, attributes, PutOwnPropertyMode::DefineProperty, throw_exceptions);
+ }
+ return put_own_property(*this, property_name.to_string_or_symbol(), value, attributes, PutOwnPropertyMode::DefineProperty, throw_exceptions);
+}
+
+bool Object::define_accessor(const PropertyName& property_name, Function& getter_or_setter, bool is_getter, PropertyAttributes attributes, bool throw_exceptions)
+{
+ ASSERT(property_name.is_valid());
+
+ Accessor* accessor { nullptr };
+ auto property_metadata = shape().lookup(property_name.to_string_or_symbol());
+ if (property_metadata.has_value()) {
+ auto existing_property = get_direct(property_metadata.value().offset);
+ if (existing_property.is_accessor())
+ accessor = &existing_property.as_accessor();
+ }
+ if (!accessor) {
+ accessor = Accessor::create(vm(), nullptr, nullptr);
+ bool definition_success = define_property(property_name, accessor, attributes, throw_exceptions);
+ if (vm().exception())
+ return {};
+ if (!definition_success)
+ return false;
+ }
+ if (is_getter)
+ accessor->set_getter(&getter_or_setter);
+ else
+ accessor->set_setter(&getter_or_setter);
+
+ return true;
+}
+
+bool Object::put_own_property(Object& this_object, const StringOrSymbol& property_name, Value value, PropertyAttributes attributes, PutOwnPropertyMode mode, bool throw_exceptions)
+{
+ ASSERT(!(mode == PutOwnPropertyMode::Put && value.is_accessor()));
+
+ if (value.is_accessor()) {
+ auto& accessor = value.as_accessor();
+ if (accessor.getter())
+ attributes.set_has_getter();
+ if (accessor.setter())
+ attributes.set_has_setter();
+ }
+
+ // NOTE: We disable transitions during initialize(), this makes building common runtime objects significantly faster.
+ // Transitions are primarily interesting when scripts add properties to objects.
+ if (!m_transitions_enabled && !m_shape->is_unique()) {
+ m_shape->add_property_without_transition(property_name, attributes);
+ m_storage.resize(m_shape->property_count());
+ m_storage[m_shape->property_count() - 1] = value;
+ return true;
+ }
+
+ auto metadata = shape().lookup(property_name);
+ bool new_property = !metadata.has_value();
+
+ if (!is_extensible() && new_property) {
+#ifdef OBJECT_DEBUG
+ dbgln("Disallow define_property of non-extensible object");
+#endif
+ if (throw_exceptions && vm().in_strict_mode())
+ vm().throw_exception<TypeError>(global_object(), ErrorType::NonExtensibleDefine, property_name.to_display_string());
+ return false;
+ }
+
+ if (new_property) {
+ if (!m_shape->is_unique() && shape().property_count() > 100) {
+ // If you add more than 100 properties to an object, let's stop doing
+ // transitions to avoid filling up the heap with shapes.
+ ensure_shape_is_unique();
+ }
+
+ if (m_shape->is_unique()) {
+ m_shape->add_property_to_unique_shape(property_name, attributes);
+ m_storage.resize(m_shape->property_count());
+ } else if (m_transitions_enabled) {
+ set_shape(*m_shape->create_put_transition(property_name, attributes));
+ } else {
+ m_shape->add_property_without_transition(property_name, attributes);
+ m_storage.resize(m_shape->property_count());
+ }
+ metadata = shape().lookup(property_name);
+ ASSERT(metadata.has_value());
+ }
+
+ if (!new_property && mode == PutOwnPropertyMode::DefineProperty && !metadata.value().attributes.is_configurable() && attributes != metadata.value().attributes) {
+#ifdef OBJECT_DEBUG
+ dbgln("Disallow reconfig of non-configurable property");
+#endif
+ if (throw_exceptions)
+ vm().throw_exception<TypeError>(global_object(), ErrorType::DescChangeNonConfigurable, property_name.to_display_string());
+ return false;
+ }
+
+ if (mode == PutOwnPropertyMode::DefineProperty && attributes != metadata.value().attributes) {
+ if (m_shape->is_unique()) {
+ m_shape->reconfigure_property_in_unique_shape(property_name, attributes);
+ } else {
+ set_shape(*m_shape->create_configure_transition(property_name, attributes));
+ }
+ metadata = shape().lookup(property_name);
+
+#ifdef OBJECT_DEBUG
+ dbgln("Reconfigured property {}, new shape says offset is {} and my storage capacity is {}", property_name.to_display_string(), metadata.value().offset, m_storage.size());
+#endif
+ }
+
+ auto value_here = m_storage[metadata.value().offset];
+ if (!new_property && mode == PutOwnPropertyMode::Put && !value_here.is_accessor() && !metadata.value().attributes.is_writable()) {
+#ifdef OBJECT_DEBUG
+ dbgln("Disallow write to non-writable property");
+#endif
+ return false;
+ }
+
+ if (value.is_empty())
+ return true;
+
+ if (value_here.is_native_property()) {
+ call_native_property_setter(value_here.as_native_property(), &this_object, value);
+ } else {
+ m_storage[metadata.value().offset] = value;
+ }
+ return true;
+}
+
+bool Object::put_own_property_by_index(Object& this_object, u32 property_index, Value value, PropertyAttributes attributes, PutOwnPropertyMode mode, bool throw_exceptions)
+{
+ ASSERT(!(mode == PutOwnPropertyMode::Put && value.is_accessor()));
+
+ auto existing_property = m_indexed_properties.get(nullptr, property_index, false);
+ auto new_property = !existing_property.has_value();
+
+ if (!is_extensible() && new_property) {
+#ifdef OBJECT_DEBUG
+ dbgln("Disallow define_property of non-extensible object");
+#endif
+ if (throw_exceptions && vm().in_strict_mode())
+ vm().throw_exception<TypeError>(global_object(), ErrorType::NonExtensibleDefine, property_index);
+ return false;
+ }
+
+ if (value.is_accessor()) {
+ auto& accessor = value.as_accessor();
+ if (accessor.getter())
+ attributes.set_has_getter();
+ if (accessor.setter())
+ attributes.set_has_setter();
+ }
+
+ PropertyAttributes existing_attributes = new_property ? 0 : existing_property.value().attributes;
+
+ if (!new_property && mode == PutOwnPropertyMode::DefineProperty && !existing_attributes.is_configurable() && attributes != existing_attributes) {
+#ifdef OBJECT_DEBUG
+ dbgln("Disallow reconfig of non-configurable property");
+#endif
+ if (throw_exceptions)
+ vm().throw_exception<TypeError>(global_object(), ErrorType::DescChangeNonConfigurable, property_index);
+ return false;
+ }
+
+ auto value_here = new_property ? Value() : existing_property.value().value;
+ if (!new_property && mode == PutOwnPropertyMode::Put && !value_here.is_accessor() && !existing_attributes.is_writable()) {
+#ifdef OBJECT_DEBUG
+ dbgln("Disallow write to non-writable property");
+#endif
+ return false;
+ }
+
+ if (value.is_empty())
+ return true;
+
+ if (value_here.is_native_property()) {
+ call_native_property_setter(value_here.as_native_property(), &this_object, value);
+ } else {
+ m_indexed_properties.put(&this_object, property_index, value, attributes, mode == PutOwnPropertyMode::Put);
+ }
+ return true;
+}
+
+Value Object::delete_property(const PropertyName& property_name)
+{
+ ASSERT(property_name.is_valid());
+
+ if (property_name.is_number())
+ return Value(m_indexed_properties.remove(property_name.as_number()));
+
+ if (property_name.is_string()) {
+ i32 property_index = property_name.as_string().to_int().value_or(-1);
+ if (property_index >= 0)
+ return Value(m_indexed_properties.remove(property_index));
+ }
+
+ auto metadata = shape().lookup(property_name.to_string_or_symbol());
+ if (!metadata.has_value())
+ return Value(true);
+ if (!metadata.value().attributes.is_configurable())
+ return Value(false);
+
+ size_t deleted_offset = metadata.value().offset;
+
+ ensure_shape_is_unique();
+
+ shape().remove_property_from_unique_shape(property_name.to_string_or_symbol(), deleted_offset);
+ m_storage.remove(deleted_offset);
+ return Value(true);
+}
+
+void Object::ensure_shape_is_unique()
+{
+ if (shape().is_unique())
+ return;
+
+ m_shape = m_shape->create_unique_clone();
+}
+
+Value Object::get_by_index(u32 property_index) const
+{
+ const Object* object = this;
+ while (object) {
+ if (is<StringObject>(*this)) {
+ auto& string = static_cast<const StringObject*>(this)->primitive_string().string();
+ if (property_index < string.length())
+ return js_string(heap(), string.substring(property_index, 1));
+ return js_undefined();
+ }
+ if (static_cast<size_t>(property_index) < object->m_indexed_properties.array_like_size()) {
+ auto result = object->m_indexed_properties.get(const_cast<Object*>(this), property_index);
+ if (vm().exception())
+ return {};
+ if (result.has_value() && !result.value().value.is_empty())
+ return result.value().value;
+ return {};
+ }
+ object = object->prototype();
+ if (vm().exception())
+ return {};
+ }
+ return {};
+}
+
+Value Object::get(const PropertyName& property_name, Value receiver) const
+{
+ ASSERT(property_name.is_valid());
+
+ if (property_name.is_number())
+ return get_by_index(property_name.as_number());
+
+ if (property_name.is_string()) {
+ auto property_string = property_name.to_string();
+ i32 property_index = property_string.to_int().value_or(-1);
+ if (property_index >= 0)
+ return get_by_index(property_index);
+ }
+
+ const Object* object = this;
+ while (object) {
+ if (receiver.is_empty())
+ receiver = Value(const_cast<Object*>(this));
+ auto value = object->get_own_property(property_name, receiver);
+ if (vm().exception())
+ return {};
+ if (!value.is_empty())
+ return value;
+ object = object->prototype();
+ if (vm().exception())
+ return {};
+ }
+ return {};
+}
+
+bool Object::put_by_index(u32 property_index, Value value)
+{
+ ASSERT(!value.is_empty());
+
+ // If there's a setter in the prototype chain, we go to the setter.
+ // Otherwise, it goes in the own property storage.
+ Object* object = this;
+ while (object) {
+ auto existing_value = object->m_indexed_properties.get(nullptr, property_index, false);
+ if (existing_value.has_value()) {
+ auto value_here = existing_value.value();
+ if (value_here.value.is_accessor()) {
+ value_here.value.as_accessor().call_setter(object, value);
+ return true;
+ }
+ if (value_here.value.is_native_property()) {
+ // FIXME: Why doesn't put_by_index() receive the receiver value from put()?!
+ auto receiver = this;
+ call_native_property_setter(value_here.value.as_native_property(), receiver, value);
+ return true;
+ }
+ }
+ object = object->prototype();
+ if (vm().exception())
+ return {};
+ }
+ return put_own_property_by_index(*this, property_index, value, default_attributes, PutOwnPropertyMode::Put);
+}
+
+bool Object::put(const PropertyName& property_name, Value value, Value receiver)
+{
+ ASSERT(property_name.is_valid());
+
+ if (property_name.is_number())
+ return put_by_index(property_name.as_number(), value);
+
+ ASSERT(!value.is_empty());
+
+ if (property_name.is_string()) {
+ auto& property_string = property_name.as_string();
+ i32 property_index = property_string.to_int().value_or(-1);
+ if (property_index >= 0)
+ return put_by_index(property_index, value);
+ }
+
+ auto string_or_symbol = property_name.to_string_or_symbol();
+
+ if (receiver.is_empty())
+ receiver = Value(this);
+
+ // If there's a setter in the prototype chain, we go to the setter.
+ // Otherwise, it goes in the own property storage.
+ Object* object = this;
+ while (object) {
+ auto metadata = object->shape().lookup(string_or_symbol);
+ if (metadata.has_value()) {
+ auto value_here = object->m_storage[metadata.value().offset];
+ if (value_here.is_accessor()) {
+ value_here.as_accessor().call_setter(receiver, value);
+ return true;
+ }
+ if (value_here.is_native_property()) {
+ call_native_property_setter(value_here.as_native_property(), receiver, value);
+ return true;
+ }
+ }
+ object = object->prototype();
+ if (vm().exception())
+ return false;
+ }
+ return put_own_property(*this, string_or_symbol, value, default_attributes, PutOwnPropertyMode::Put);
+}
+
+bool Object::define_native_function(const StringOrSymbol& property_name, AK::Function<Value(VM&, GlobalObject&)> native_function, i32 length, PropertyAttributes attribute)
+{
+ auto& vm = this->vm();
+ String function_name;
+ if (property_name.is_string()) {
+ function_name = property_name.as_string();
+ } else {
+ function_name = String::formatted("[{}]", property_name.as_symbol()->description());
+ }
+ auto* function = NativeFunction::create(global_object(), function_name, move(native_function));
+ function->define_property_without_transition(vm.names.length, Value(length), Attribute::Configurable);
+ if (vm.exception())
+ return {};
+ function->define_property_without_transition(vm.names.name, js_string(vm.heap(), function_name), Attribute::Configurable);
+ if (vm.exception())
+ return {};
+ return define_property(property_name, function, attribute);
+}
+
+bool Object::define_native_property(const StringOrSymbol& property_name, AK::Function<Value(VM&, GlobalObject&)> getter, AK::Function<void(VM&, GlobalObject&, Value)> setter, PropertyAttributes attribute)
+{
+ return define_property(property_name, heap().allocate_without_global_object<NativeProperty>(move(getter), move(setter)), attribute);
+}
+
+void Object::visit_edges(Cell::Visitor& visitor)
+{
+ Cell::visit_edges(visitor);
+ visitor.visit(m_shape);
+
+ for (auto& value : m_storage)
+ visitor.visit(value);
+
+ m_indexed_properties.for_each_value([&visitor](auto& value) {
+ visitor.visit(value);
+ });
+}
+
+bool Object::has_property(const PropertyName& property_name) const
+{
+ const Object* object = this;
+ while (object) {
+ if (object->has_own_property(property_name))
+ return true;
+ object = object->prototype();
+ if (vm().exception())
+ return false;
+ }
+ return false;
+}
+
+bool Object::has_own_property(const PropertyName& property_name) const
+{
+ ASSERT(property_name.is_valid());
+
+ auto has_indexed_property = [&](u32 index) -> bool {
+ if (is<StringObject>(*this))
+ return index < static_cast<const StringObject*>(this)->primitive_string().string().length();
+ return m_indexed_properties.has_index(index);
+ };
+
+ if (property_name.is_number())
+ return has_indexed_property(property_name.as_number());
+
+ if (property_name.is_string()) {
+ i32 property_index = property_name.as_string().to_int().value_or(-1);
+ if (property_index >= 0)
+ return has_indexed_property(property_index);
+ }
+
+ return shape().lookup(property_name.to_string_or_symbol()).has_value();
+}
+
+Value Object::ordinary_to_primitive(Value::PreferredType preferred_type) const
+{
+ ASSERT(preferred_type == Value::PreferredType::String || preferred_type == Value::PreferredType::Number);
+
+ auto& vm = this->vm();
+
+ Vector<FlyString, 2> method_names;
+ if (preferred_type == Value::PreferredType::String)
+ method_names = { vm.names.toString, vm.names.valueOf };
+ else
+ method_names = { vm.names.valueOf, vm.names.toString };
+
+ for (auto& method_name : method_names) {
+ auto method = get(method_name);
+ if (vm.exception())
+ return {};
+ if (method.is_function()) {
+ auto result = vm.call(method.as_function(), const_cast<Object*>(this));
+ if (!result.is_object())
+ return result;
+ }
+ }
+ vm.throw_exception<TypeError>(global_object(), ErrorType::Convert, "object", preferred_type == Value::PreferredType::String ? "string" : "number");
+ return {};
+}
+
+Value Object::invoke(const StringOrSymbol& property_name, Optional<MarkedValueList> arguments)
+{
+ auto& vm = this->vm();
+ auto property = get(property_name).value_or(js_undefined());
+ if (vm.exception())
+ return {};
+ if (!property.is_function()) {
+ vm.throw_exception<TypeError>(global_object(), ErrorType::NotAFunction, property.to_string_without_side_effects());
+ return {};
+ }
+ return vm.call(property.as_function(), this, move(arguments));
+}
+
+Value Object::call_native_property_getter(NativeProperty& property, Value this_value) const
+{
+ auto& vm = this->vm();
+ CallFrame call_frame;
+ call_frame.is_strict_mode = vm.in_strict_mode();
+ call_frame.this_value = this_value;
+ vm.push_call_frame(call_frame, global_object());
+ if (vm.exception())
+ return {};
+ auto result = property.get(vm, global_object());
+ vm.pop_call_frame();
+ return result;
+}
+
+void Object::call_native_property_setter(NativeProperty& property, Value this_value, Value setter_value) const
+{
+ auto& vm = this->vm();
+ CallFrame call_frame;
+ call_frame.is_strict_mode = vm.in_strict_mode();
+ call_frame.this_value = this_value;
+ vm.push_call_frame(call_frame, global_object());
+ if (vm.exception())
+ return;
+ property.set(vm, global_object(), setter_value);
+ vm.pop_call_frame();
+}
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/Object.h b/Userland/Libraries/LibJS/Runtime/Object.h
new file mode 100644
index 0000000000..e869aee748
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/Object.h
@@ -0,0 +1,168 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * 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 <AK/HashMap.h>
+#include <AK/String.h>
+#include <LibJS/Forward.h>
+#include <LibJS/Runtime/Cell.h>
+#include <LibJS/Runtime/IndexedProperties.h>
+#include <LibJS/Runtime/MarkedValueList.h>
+#include <LibJS/Runtime/PrimitiveString.h>
+#include <LibJS/Runtime/PropertyName.h>
+#include <LibJS/Runtime/Shape.h>
+#include <LibJS/Runtime/Value.h>
+
+namespace JS {
+
+#define JS_OBJECT(class_, base_class) \
+public: \
+ using Base = base_class; \
+ virtual const char* class_name() const override { return #class_; }
+
+struct PropertyDescriptor {
+ PropertyAttributes attributes;
+ Value value;
+ Function* getter { nullptr };
+ Function* setter { nullptr };
+
+ static PropertyDescriptor from_dictionary(VM&, const Object&);
+
+ bool is_accessor_descriptor() const { return getter || setter; }
+ bool is_data_descriptor() const { return !(value.is_empty() && !attributes.has_writable()); }
+ bool is_generic_descriptor() const { return !is_accessor_descriptor() && !is_data_descriptor(); }
+};
+
+class Object : public Cell {
+public:
+ static Object* create_empty(GlobalObject&);
+
+ explicit Object(Object& prototype);
+ explicit Object(Shape&);
+ virtual void initialize(GlobalObject&) override;
+ virtual ~Object();
+
+ enum class PropertyKind {
+ Key,
+ Value,
+ KeyAndValue,
+ };
+
+ enum class GetOwnPropertyReturnType {
+ StringOnly,
+ SymbolOnly,
+ };
+
+ enum class PutOwnPropertyMode {
+ Put,
+ DefineProperty,
+ };
+
+ Shape& shape() { return *m_shape; }
+ const Shape& shape() const { return *m_shape; }
+
+ GlobalObject& global_object() const { return *shape().global_object(); }
+
+ virtual Value get(const PropertyName&, Value receiver = {}) const;
+
+ virtual bool has_property(const PropertyName&) const;
+ bool has_own_property(const PropertyName&) const;
+
+ virtual bool put(const PropertyName&, Value, Value receiver = {});
+
+ Value get_own_property(const PropertyName&, Value receiver) const;
+ Value get_own_properties(const Object& this_object, PropertyKind, bool only_enumerable_properties = false, GetOwnPropertyReturnType = GetOwnPropertyReturnType::StringOnly) const;
+ virtual Optional<PropertyDescriptor> get_own_property_descriptor(const PropertyName&) const;
+ Value get_own_property_descriptor_object(const PropertyName&) const;
+
+ virtual bool define_property(const StringOrSymbol& property_name, const Object& descriptor, bool throw_exceptions = true);
+ bool define_property(const PropertyName&, Value value, PropertyAttributes attributes = default_attributes, bool throw_exceptions = true);
+ bool define_property_without_transition(const PropertyName&, Value value, PropertyAttributes attributes = default_attributes, bool throw_exceptions = true);
+ bool define_accessor(const PropertyName&, Function& getter_or_setter, bool is_getter, PropertyAttributes attributes = default_attributes, bool throw_exceptions = true);
+
+ bool define_native_function(const StringOrSymbol& property_name, AK::Function<Value(VM&, GlobalObject&)>, i32 length = 0, PropertyAttributes attributes = default_attributes);
+ bool define_native_property(const StringOrSymbol& property_name, AK::Function<Value(VM&, GlobalObject&)> getter, AK::Function<void(VM&, GlobalObject&, Value)> setter, PropertyAttributes attributes = default_attributes);
+
+ virtual Value delete_property(const PropertyName&);
+
+ virtual bool is_array() const { return false; }
+ virtual bool is_function() const { return false; }
+ virtual bool is_typed_array() const { return false; }
+
+ virtual const char* class_name() const override { return "Object"; }
+ virtual void visit_edges(Cell::Visitor&) override;
+
+ virtual Object* prototype();
+ virtual const Object* prototype() const;
+ virtual bool set_prototype(Object* prototype);
+ bool has_prototype(const Object* prototype) const;
+
+ virtual bool is_extensible() const { return m_is_extensible; }
+ virtual bool prevent_extensions();
+
+ virtual Value value_of() const { return Value(const_cast<Object*>(this)); }
+ virtual Value ordinary_to_primitive(Value::PreferredType preferred_type) const;
+
+ Value get_direct(size_t index) const { return m_storage[index]; }
+
+ const IndexedProperties& indexed_properties() const { return m_indexed_properties; }
+ IndexedProperties& indexed_properties() { return m_indexed_properties; }
+ void set_indexed_property_elements(Vector<Value>&& values) { m_indexed_properties = IndexedProperties(move(values)); }
+
+ Value invoke(const StringOrSymbol& property_name, Optional<MarkedValueList> arguments = {});
+
+ void ensure_shape_is_unique();
+
+ void enable_transitions() { m_transitions_enabled = true; }
+ void disable_transitions() { m_transitions_enabled = false; }
+
+protected:
+ enum class GlobalObjectTag { Tag };
+ enum class ConstructWithoutPrototypeTag { Tag };
+ explicit Object(GlobalObjectTag);
+ Object(ConstructWithoutPrototypeTag, GlobalObject&);
+
+ virtual Value get_by_index(u32 property_index) const;
+ virtual bool put_by_index(u32 property_index, Value);
+
+private:
+ bool put_own_property(Object& this_object, const StringOrSymbol& property_name, Value, PropertyAttributes attributes, PutOwnPropertyMode = PutOwnPropertyMode::Put, bool throw_exceptions = true);
+ bool put_own_property_by_index(Object& this_object, u32 property_index, Value, PropertyAttributes attributes, PutOwnPropertyMode = PutOwnPropertyMode::Put, bool throw_exceptions = true);
+
+ Value call_native_property_getter(NativeProperty& property, Value this_value) const;
+ void call_native_property_setter(NativeProperty& property, Value this_value, Value) const;
+
+ void set_shape(Shape&);
+
+ bool m_is_extensible { true };
+ bool m_transitions_enabled { true };
+ Shape* m_shape { nullptr };
+ Vector<Value> m_storage;
+ IndexedProperties m_indexed_properties;
+};
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/ObjectConstructor.cpp b/Userland/Libraries/LibJS/Runtime/ObjectConstructor.cpp
new file mode 100644
index 0000000000..a5601d9753
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/ObjectConstructor.cpp
@@ -0,0 +1,242 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * 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 <AK/Function.h>
+#include <LibJS/Heap/Heap.h>
+#include <LibJS/Runtime/Array.h>
+#include <LibJS/Runtime/Error.h>
+#include <LibJS/Runtime/GlobalObject.h>
+#include <LibJS/Runtime/ObjectConstructor.h>
+#include <LibJS/Runtime/ProxyObject.h>
+#include <LibJS/Runtime/Shape.h>
+
+namespace JS {
+
+ObjectConstructor::ObjectConstructor(GlobalObject& global_object)
+ : NativeFunction(vm().names.Object, *global_object.function_prototype())
+{
+}
+
+void ObjectConstructor::initialize(GlobalObject& global_object)
+{
+ auto& vm = this->vm();
+ NativeFunction::initialize(global_object);
+ define_property(vm.names.prototype, global_object.object_prototype(), 0);
+ define_property(vm.names.length, Value(1), Attribute::Configurable);
+
+ u8 attr = Attribute::Writable | Attribute::Configurable;
+ define_native_function(vm.names.defineProperty, define_property_, 3, attr);
+ define_native_function(vm.names.is, is, 2, attr);
+ define_native_function(vm.names.getOwnPropertyDescriptor, get_own_property_descriptor, 2, attr);
+ define_native_function(vm.names.getOwnPropertyNames, get_own_property_names, 1, attr);
+ define_native_function(vm.names.getPrototypeOf, get_prototype_of, 1, attr);
+ define_native_function(vm.names.setPrototypeOf, set_prototype_of, 2, attr);
+ define_native_function(vm.names.isExtensible, is_extensible, 1, attr);
+ define_native_function(vm.names.preventExtensions, prevent_extensions, 1, attr);
+ define_native_function(vm.names.keys, keys, 1, attr);
+ define_native_function(vm.names.values, values, 1, attr);
+ define_native_function(vm.names.entries, entries, 1, attr);
+}
+
+ObjectConstructor::~ObjectConstructor()
+{
+}
+
+Value ObjectConstructor::call()
+{
+ auto value = vm().argument(0);
+ if (value.is_nullish())
+ return Object::create_empty(global_object());
+ return value.to_object(global_object());
+}
+
+Value ObjectConstructor::construct(Function&)
+{
+ return call();
+}
+
+JS_DEFINE_NATIVE_FUNCTION(ObjectConstructor::get_own_property_names)
+{
+ if (!vm.argument_count())
+ return {};
+ auto* object = vm.argument(0).to_object(global_object);
+ if (vm.exception())
+ return {};
+ auto* result = Array::create(global_object);
+ for (auto& entry : object->indexed_properties())
+ result->indexed_properties().append(js_string(vm, String::number(entry.index())));
+ for (auto& it : object->shape().property_table_ordered()) {
+ if (!it.key.is_string())
+ continue;
+ result->indexed_properties().append(js_string(vm, it.key.as_string()));
+ }
+
+ return result;
+}
+
+JS_DEFINE_NATIVE_FUNCTION(ObjectConstructor::get_prototype_of)
+{
+ if (!vm.argument_count())
+ return {};
+ auto* object = vm.argument(0).to_object(global_object);
+ if (vm.exception())
+ return {};
+ return object->prototype();
+}
+
+JS_DEFINE_NATIVE_FUNCTION(ObjectConstructor::set_prototype_of)
+{
+ if (vm.argument_count() < 2) {
+ vm.throw_exception<TypeError>(global_object, ErrorType::ObjectSetPrototypeOfTwoArgs);
+ return {};
+ }
+ auto* object = vm.argument(0).to_object(global_object);
+ if (vm.exception())
+ return {};
+ auto prototype_value = vm.argument(1);
+ Object* prototype;
+ if (prototype_value.is_null()) {
+ prototype = nullptr;
+ } else if (prototype_value.is_object()) {
+ prototype = &prototype_value.as_object();
+ } else {
+ vm.throw_exception<TypeError>(global_object, ErrorType::ObjectPrototypeWrongType);
+ return {};
+ }
+ if (!object->set_prototype(prototype)) {
+ if (!vm.exception())
+ vm.throw_exception<TypeError>(global_object, ErrorType::ObjectSetPrototypeOfReturnedFalse);
+ return {};
+ }
+ return object;
+}
+
+JS_DEFINE_NATIVE_FUNCTION(ObjectConstructor::is_extensible)
+{
+ auto argument = vm.argument(0);
+ if (!argument.is_object())
+ return Value(false);
+ return Value(argument.as_object().is_extensible());
+}
+
+JS_DEFINE_NATIVE_FUNCTION(ObjectConstructor::prevent_extensions)
+{
+ auto argument = vm.argument(0);
+ if (!argument.is_object())
+ return argument;
+ if (!argument.as_object().prevent_extensions()) {
+ if (!vm.exception())
+ vm.throw_exception<TypeError>(global_object, ErrorType::ObjectPreventExtensionsReturnedFalse);
+ return {};
+ }
+ return argument;
+}
+
+JS_DEFINE_NATIVE_FUNCTION(ObjectConstructor::get_own_property_descriptor)
+{
+ auto* object = vm.argument(0).to_object(global_object);
+ if (vm.exception())
+ return {};
+ auto property_key = PropertyName::from_value(global_object, vm.argument(1));
+ if (vm.exception())
+ return {};
+ return object->get_own_property_descriptor_object(property_key);
+}
+
+JS_DEFINE_NATIVE_FUNCTION(ObjectConstructor::define_property_)
+{
+ if (!vm.argument(0).is_object()) {
+ vm.throw_exception<TypeError>(global_object, ErrorType::NotAnObject, "Object argument");
+ return {};
+ }
+ if (!vm.argument(2).is_object()) {
+ vm.throw_exception<TypeError>(global_object, ErrorType::NotAnObject, "Descriptor argument");
+ return {};
+ }
+ auto& object = vm.argument(0).as_object();
+ auto property_key = StringOrSymbol::from_value(global_object, vm.argument(1));
+ if (vm.exception())
+ return {};
+ auto& descriptor = vm.argument(2).as_object();
+ if (!object.define_property(property_key, descriptor)) {
+ if (!vm.exception()) {
+ if (AK::is<ProxyObject>(object)) {
+ vm.throw_exception<TypeError>(global_object, ErrorType::ObjectDefinePropertyReturnedFalse);
+ } else {
+ vm.throw_exception<TypeError>(global_object, ErrorType::NonExtensibleDefine, property_key.to_display_string());
+ }
+ }
+ return {};
+ }
+ return &object;
+}
+
+JS_DEFINE_NATIVE_FUNCTION(ObjectConstructor::is)
+{
+ return Value(same_value(vm.argument(0), vm.argument(1)));
+}
+
+JS_DEFINE_NATIVE_FUNCTION(ObjectConstructor::keys)
+{
+ if (!vm.argument_count()) {
+ vm.throw_exception<TypeError>(global_object, ErrorType::ConvertUndefinedToObject);
+ return {};
+ }
+
+ auto* obj_arg = vm.argument(0).to_object(global_object);
+ if (vm.exception())
+ return {};
+
+ return obj_arg->get_own_properties(*obj_arg, PropertyKind::Key, true);
+}
+
+JS_DEFINE_NATIVE_FUNCTION(ObjectConstructor::values)
+{
+ if (!vm.argument_count()) {
+ vm.throw_exception<TypeError>(global_object, ErrorType::ConvertUndefinedToObject);
+ return {};
+ }
+ auto* obj_arg = vm.argument(0).to_object(global_object);
+ if (vm.exception())
+ return {};
+
+ return obj_arg->get_own_properties(*obj_arg, PropertyKind::Value, true);
+}
+
+JS_DEFINE_NATIVE_FUNCTION(ObjectConstructor::entries)
+{
+ if (!vm.argument_count()) {
+ vm.throw_exception<TypeError>(global_object, ErrorType::ConvertUndefinedToObject);
+ return {};
+ }
+ auto* obj_arg = vm.argument(0).to_object(global_object);
+ if (vm.exception())
+ return {};
+
+ return obj_arg->get_own_properties(*obj_arg, PropertyKind::KeyAndValue, true);
+}
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/ObjectConstructor.h b/Userland/Libraries/LibJS/Runtime/ObjectConstructor.h
new file mode 100644
index 0000000000..717539651c
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/ObjectConstructor.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * 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/NativeFunction.h>
+
+namespace JS {
+
+class ObjectConstructor final : public NativeFunction {
+ JS_OBJECT(ObjectConstructor, NativeFunction);
+
+public:
+ explicit ObjectConstructor(GlobalObject&);
+ virtual void initialize(GlobalObject&) override;
+ virtual ~ObjectConstructor() override;
+
+ virtual Value call() override;
+ virtual Value construct(Function& new_target) override;
+
+private:
+ virtual bool has_constructor() const override { return true; }
+
+ JS_DECLARE_NATIVE_FUNCTION(define_property_);
+ JS_DECLARE_NATIVE_FUNCTION(is);
+ JS_DECLARE_NATIVE_FUNCTION(get_own_property_descriptor);
+ JS_DECLARE_NATIVE_FUNCTION(get_own_property_names);
+ JS_DECLARE_NATIVE_FUNCTION(get_prototype_of);
+ JS_DECLARE_NATIVE_FUNCTION(set_prototype_of);
+ JS_DECLARE_NATIVE_FUNCTION(is_extensible);
+ JS_DECLARE_NATIVE_FUNCTION(prevent_extensions);
+ JS_DECLARE_NATIVE_FUNCTION(keys);
+ JS_DECLARE_NATIVE_FUNCTION(values);
+ JS_DECLARE_NATIVE_FUNCTION(entries);
+};
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/ObjectPrototype.cpp b/Userland/Libraries/LibJS/Runtime/ObjectPrototype.cpp
new file mode 100644
index 0000000000..cfc31c6b91
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/ObjectPrototype.cpp
@@ -0,0 +1,166 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * 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 <AK/Function.h>
+#include <AK/String.h>
+#include <LibJS/Heap/Heap.h>
+#include <LibJS/Runtime/BooleanObject.h>
+#include <LibJS/Runtime/Date.h>
+#include <LibJS/Runtime/GlobalObject.h>
+#include <LibJS/Runtime/NumberObject.h>
+#include <LibJS/Runtime/ObjectPrototype.h>
+#include <LibJS/Runtime/RegExpObject.h>
+#include <LibJS/Runtime/StringObject.h>
+#include <LibJS/Runtime/Value.h>
+
+namespace JS {
+
+ObjectPrototype::ObjectPrototype(GlobalObject& global_object)
+ : Object(Object::ConstructWithoutPrototypeTag::Tag, global_object)
+{
+}
+
+void ObjectPrototype::initialize(GlobalObject& global_object)
+{
+ auto& vm = this->vm();
+ Object::initialize(global_object);
+ // This must be called after the constructor has returned, so that the below code
+ // can find the ObjectPrototype through normal paths.
+ u8 attr = Attribute::Writable | Attribute::Configurable;
+ define_native_function(vm.names.hasOwnProperty, has_own_property, 1, attr);
+ define_native_function(vm.names.toString, to_string, 0, attr);
+ define_native_function(vm.names.toLocaleString, to_locale_string, 0, attr);
+ define_native_function(vm.names.valueOf, value_of, 0, attr);
+ define_native_function(vm.names.propertyIsEnumerable, property_is_enumerable, 1, attr);
+ define_native_function(vm.names.isPrototypeOf, is_prototype_of, 1, attr);
+}
+
+ObjectPrototype::~ObjectPrototype()
+{
+}
+
+JS_DEFINE_NATIVE_FUNCTION(ObjectPrototype::has_own_property)
+{
+ auto* this_object = vm.this_value(global_object).to_object(global_object);
+ if (!this_object)
+ return {};
+ auto name = vm.argument(0).to_string(global_object);
+ if (vm.exception())
+ return {};
+ return Value(this_object->has_own_property(name));
+}
+
+JS_DEFINE_NATIVE_FUNCTION(ObjectPrototype::to_string)
+{
+ auto this_value = vm.this_value(global_object);
+
+ if (this_value.is_undefined())
+ return js_string(vm, "[object Undefined]");
+ if (this_value.is_null())
+ return js_string(vm, "[object Null]");
+
+ auto* this_object = this_value.to_object(global_object);
+ if (!this_object)
+ return {};
+
+ String tag;
+ auto to_string_tag = this_object->get(global_object.vm().well_known_symbol_to_string_tag());
+
+ if (to_string_tag.is_string()) {
+ tag = to_string_tag.as_string().string();
+ } else if (this_object->is_array()) {
+ tag = "Array";
+ } else if (this_object->is_function()) {
+ tag = "Function";
+ } else if (is<Error>(this_object)) {
+ tag = "Error";
+ } else if (is<BooleanObject>(this_object)) {
+ tag = "Boolean";
+ } else if (is<NumberObject>(this_object)) {
+ tag = "Number";
+ } else if (is<StringObject>(this_object)) {
+ tag = "String";
+ } else if (is<Date>(this_object)) {
+ tag = "Date";
+ } else if (is<RegExpObject>(this_object)) {
+ tag = "RegExp";
+ } else {
+ tag = "Object";
+ }
+
+ return js_string(vm, String::formatted("[object {}]", tag));
+}
+
+JS_DEFINE_NATIVE_FUNCTION(ObjectPrototype::to_locale_string)
+{
+ auto* this_object = vm.this_value(global_object).to_object(global_object);
+ if (!this_object)
+ return {};
+ return this_object->invoke("toString");
+}
+
+JS_DEFINE_NATIVE_FUNCTION(ObjectPrototype::value_of)
+{
+ auto* this_object = vm.this_value(global_object).to_object(global_object);
+ if (!this_object)
+ return {};
+ return this_object->value_of();
+}
+
+JS_DEFINE_NATIVE_FUNCTION(ObjectPrototype::property_is_enumerable)
+{
+ auto name = vm.argument(0).to_string(global_object);
+ if (vm.exception())
+ return {};
+ auto* this_object = vm.this_value(global_object).to_object(global_object);
+ if (!this_object)
+ return {};
+ auto property_descriptor = this_object->get_own_property_descriptor(name);
+ if (!property_descriptor.has_value())
+ return Value(false);
+ return Value(property_descriptor.value().attributes.is_enumerable());
+}
+
+JS_DEFINE_NATIVE_FUNCTION(ObjectPrototype::is_prototype_of)
+{
+ auto object_argument = vm.argument(0);
+ if (!object_argument.is_object())
+ return Value(false);
+ auto* object = &object_argument.as_object();
+ auto* this_object = vm.this_value(global_object).to_object(global_object);
+ if (!this_object)
+ return {};
+
+ for (;;) {
+ object = object->prototype();
+ if (!object)
+ return Value(false);
+ if (same_value(this_object, object))
+ return Value(true);
+ }
+}
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/ObjectPrototype.h b/Userland/Libraries/LibJS/Runtime/ObjectPrototype.h
new file mode 100644
index 0000000000..a68857e8ae
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/ObjectPrototype.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * 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 ObjectPrototype final : public Object {
+ JS_OBJECT(ObjectPrototype, Object);
+
+public:
+ explicit ObjectPrototype(GlobalObject&);
+ virtual void initialize(GlobalObject&) override;
+ virtual ~ObjectPrototype() override;
+
+ // public to serve as intrinsic function %Object.prototype.toString%
+ JS_DECLARE_NATIVE_FUNCTION(to_string);
+
+private:
+ JS_DECLARE_NATIVE_FUNCTION(has_own_property);
+ JS_DECLARE_NATIVE_FUNCTION(to_locale_string);
+ JS_DECLARE_NATIVE_FUNCTION(value_of);
+ JS_DECLARE_NATIVE_FUNCTION(property_is_enumerable);
+ JS_DECLARE_NATIVE_FUNCTION(is_prototype_of);
+};
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/PrimitiveString.cpp b/Userland/Libraries/LibJS/Runtime/PrimitiveString.cpp
new file mode 100644
index 0000000000..b62377925d
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/PrimitiveString.cpp
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * 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/PrimitiveString.h>
+#include <LibJS/Runtime/VM.h>
+
+namespace JS {
+
+PrimitiveString::PrimitiveString(String string)
+ : m_string(move(string))
+{
+}
+
+PrimitiveString::~PrimitiveString()
+{
+}
+
+PrimitiveString* js_string(Heap& heap, String string)
+{
+ if (string.is_empty())
+ return &heap.vm().empty_string();
+
+ if (string.length() == 1 && (u8)string.characters()[0] < 0x80)
+ return &heap.vm().single_ascii_character_string(string.characters()[0]);
+
+ return heap.allocate_without_global_object<PrimitiveString>(move(string));
+}
+
+PrimitiveString* js_string(VM& vm, String string)
+{
+ return js_string(vm.heap(), move(string));
+}
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/PrimitiveString.h b/Userland/Libraries/LibJS/Runtime/PrimitiveString.h
new file mode 100644
index 0000000000..7b1a9d9e4d
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/PrimitiveString.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * 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 <AK/String.h>
+#include <LibJS/Runtime/Cell.h>
+
+namespace JS {
+
+class PrimitiveString final : public Cell {
+public:
+ explicit PrimitiveString(String);
+ virtual ~PrimitiveString();
+
+ const String& string() const { return m_string; }
+
+private:
+ virtual const char* class_name() const override { return "PrimitiveString"; }
+
+ String m_string;
+};
+
+PrimitiveString* js_string(Heap&, String);
+PrimitiveString* js_string(VM&, String);
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/PropertyAttributes.h b/Userland/Libraries/LibJS/Runtime/PropertyAttributes.h
new file mode 100644
index 0000000000..b3788bd897
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/PropertyAttributes.h
@@ -0,0 +1,104 @@
+/*
+ * 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 <AK/Format.h>
+#include <AK/Types.h>
+
+namespace JS {
+
+struct Attribute {
+ enum {
+ Configurable = 1 << 0,
+ Enumerable = 1 << 1,
+ Writable = 1 << 2,
+ HasGetter = 1 << 3,
+ HasSetter = 1 << 4,
+ HasConfigurable = 1 << 5,
+ HasEnumerable = 1 << 6,
+ HasWritable = 1 << 7,
+ };
+};
+
+class PropertyAttributes {
+public:
+ PropertyAttributes(u8 bits = 0)
+ {
+ m_bits = bits;
+ if (bits & Attribute::Configurable)
+ m_bits |= Attribute::HasConfigurable;
+ if (bits & Attribute::Enumerable)
+ m_bits |= Attribute::HasEnumerable;
+ if (bits & Attribute::Writable)
+ m_bits |= Attribute::HasWritable;
+ }
+
+ bool is_empty() const { return !m_bits; }
+
+ bool has_configurable() const { return m_bits & Attribute::HasConfigurable; }
+ bool has_enumerable() const { return m_bits & Attribute::HasEnumerable; }
+ bool has_writable() const { return m_bits & Attribute::HasWritable; }
+ bool has_getter() const { return m_bits & Attribute::HasGetter; }
+ bool has_setter() const { return m_bits & Attribute::HasSetter; }
+
+ bool is_configurable() const { return m_bits & Attribute::Configurable; }
+ bool is_enumerable() const { return m_bits & Attribute::Enumerable; }
+ bool is_writable() const { return m_bits & Attribute::Writable; }
+
+ void set_has_configurable() { m_bits |= Attribute::HasConfigurable; }
+ void set_has_enumerable() { m_bits |= Attribute::HasEnumerable; }
+ void set_has_writable() { m_bits |= Attribute::HasWritable; }
+ void set_configurable() { m_bits |= Attribute::Configurable; }
+ void set_enumerable() { m_bits |= Attribute::Enumerable; }
+ void set_writable() { m_bits |= Attribute::Writable; }
+ void set_has_getter() { m_bits |= Attribute::HasGetter; }
+ void set_has_setter() { m_bits |= Attribute::HasSetter; }
+
+ bool operator==(const PropertyAttributes& other) const { return m_bits == other.m_bits; }
+ bool operator!=(const PropertyAttributes& other) const { return m_bits != other.m_bits; }
+
+ u8 bits() const { return m_bits; }
+
+private:
+ u8 m_bits;
+};
+
+const PropertyAttributes default_attributes = Attribute::Configurable | Attribute::Writable | Attribute::Enumerable;
+
+}
+
+namespace AK {
+
+template<>
+struct Formatter<JS::PropertyAttributes> : Formatter<u8> {
+ void format(FormatBuilder& builder, const JS::PropertyAttributes& attributes)
+ {
+ Formatter<u8>::format(builder, attributes.bits());
+ }
+};
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/PropertyName.h b/Userland/Libraries/LibJS/Runtime/PropertyName.h
new file mode 100644
index 0000000000..bbd6a0c640
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/PropertyName.h
@@ -0,0 +1,163 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * 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 <AK/FlyString.h>
+#include <LibJS/Runtime/StringOrSymbol.h>
+
+namespace JS {
+
+class PropertyName {
+public:
+ enum class Type {
+ Invalid,
+ Number,
+ String,
+ Symbol,
+ };
+
+ static PropertyName from_value(GlobalObject& global_object, Value value)
+ {
+ if (value.is_empty())
+ return {};
+ if (value.is_symbol())
+ return &value.as_symbol();
+ if (value.is_integer() && value.as_i32() >= 0)
+ return value.as_i32();
+ auto string = value.to_string(global_object);
+ if (string.is_null())
+ return {};
+ return string;
+ }
+
+ PropertyName() { }
+
+ PropertyName(i32 index)
+ : m_type(Type::Number)
+ , m_number(index)
+ {
+ ASSERT(index >= 0);
+ }
+
+ PropertyName(const char* chars)
+ : m_type(Type::String)
+ , m_string(FlyString(chars))
+ {
+ }
+
+ PropertyName(const String& string)
+ : m_type(Type::String)
+ , m_string(FlyString(string))
+ {
+ ASSERT(!string.is_null());
+ }
+
+ PropertyName(const FlyString& string)
+ : m_type(Type::String)
+ , m_string(string)
+ {
+ ASSERT(!string.is_null());
+ }
+
+ PropertyName(Symbol* symbol)
+ : m_type(Type::Symbol)
+ , m_symbol(symbol)
+ {
+ ASSERT(symbol);
+ }
+
+ PropertyName(const StringOrSymbol& string_or_symbol)
+ {
+ if (string_or_symbol.is_string()) {
+ m_string = string_or_symbol.as_string();
+ m_type = Type::String;
+ } else if (string_or_symbol.is_symbol()) {
+ m_symbol = const_cast<Symbol*>(string_or_symbol.as_symbol());
+ m_type = Type::Symbol;
+ }
+ }
+
+ bool is_valid() const { return m_type != Type::Invalid; }
+ bool is_number() const { return m_type == Type::Number; }
+ bool is_string() const { return m_type == Type::String; }
+ bool is_symbol() const { return m_type == Type::Symbol; }
+
+ i32 as_number() const
+ {
+ ASSERT(is_number());
+ return m_number;
+ }
+
+ const FlyString& as_string() const
+ {
+ ASSERT(is_string());
+ return m_string;
+ }
+
+ const Symbol* as_symbol() const
+ {
+ ASSERT(is_symbol());
+ return m_symbol;
+ }
+
+ String to_string() const
+ {
+ ASSERT(is_valid());
+ ASSERT(!is_symbol());
+ if (is_string())
+ return as_string();
+ return String::number(as_number());
+ }
+
+ StringOrSymbol to_string_or_symbol() const
+ {
+ ASSERT(is_valid());
+ ASSERT(!is_number());
+ if (is_string())
+ return StringOrSymbol(as_string());
+ return StringOrSymbol(as_symbol());
+ }
+
+ Value to_value(VM& vm) const
+ {
+ if (is_string())
+ return js_string(vm, m_string);
+ if (is_number())
+ return Value(m_number);
+ if (is_symbol())
+ return m_symbol;
+ return js_undefined();
+ }
+
+private:
+ Type m_type { Type::Invalid };
+ FlyString m_string;
+ Symbol* m_symbol { nullptr };
+ u32 m_number { 0 };
+};
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/ProxyConstructor.cpp b/Userland/Libraries/LibJS/Runtime/ProxyConstructor.cpp
new file mode 100644
index 0000000000..f83c5f7727
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/ProxyConstructor.cpp
@@ -0,0 +1,80 @@
+/*
+ * 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/Error.h>
+#include <LibJS/Runtime/GlobalObject.h>
+#include <LibJS/Runtime/ProxyConstructor.h>
+#include <LibJS/Runtime/ProxyObject.h>
+
+namespace JS {
+
+ProxyConstructor::ProxyConstructor(GlobalObject& global_object)
+ : NativeFunction(vm().names.Proxy, *global_object.function_prototype())
+{
+}
+
+void ProxyConstructor::initialize(GlobalObject& global_object)
+{
+ auto& vm = this->vm();
+ NativeFunction::initialize(global_object);
+ define_property(vm.names.length, Value(2), Attribute::Configurable);
+}
+
+ProxyConstructor::~ProxyConstructor()
+{
+}
+
+Value ProxyConstructor::call()
+{
+ auto& vm = this->vm();
+ vm.throw_exception<TypeError>(global_object(), ErrorType::ConstructorWithoutNew, vm.names.Proxy);
+ return {};
+}
+
+Value ProxyConstructor::construct(Function&)
+{
+ auto& vm = this->vm();
+ if (vm.argument_count() < 2) {
+ vm.throw_exception<TypeError>(global_object(), ErrorType::ProxyTwoArguments);
+ return {};
+ }
+
+ auto target = vm.argument(0);
+ auto handler = vm.argument(1);
+
+ if (!target.is_object()) {
+ vm.throw_exception<TypeError>(global_object(), ErrorType::ProxyConstructorBadType, "target", target.to_string_without_side_effects());
+ return {};
+ }
+ if (!handler.is_object()) {
+ vm.throw_exception<TypeError>(global_object(), ErrorType::ProxyConstructorBadType, "handler", handler.to_string_without_side_effects());
+ return {};
+ }
+ return ProxyObject::create(global_object(), target.as_object(), handler.as_object());
+}
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/ProxyConstructor.h b/Userland/Libraries/LibJS/Runtime/ProxyConstructor.h
new file mode 100644
index 0000000000..69d23b847a
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/ProxyConstructor.h
@@ -0,0 +1,48 @@
+/*
+ * 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/NativeFunction.h>
+
+namespace JS {
+
+class ProxyConstructor final : public NativeFunction {
+ JS_OBJECT(ProxyConstructor, NativeFunction);
+
+public:
+ explicit ProxyConstructor(GlobalObject&);
+ virtual void initialize(GlobalObject&) override;
+ virtual ~ProxyConstructor() override;
+
+ virtual Value call() override;
+ virtual Value construct(Function& new_target) override;
+
+private:
+ virtual bool has_constructor() const override { return true; }
+};
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/ProxyObject.cpp b/Userland/Libraries/LibJS/Runtime/ProxyObject.cpp
new file mode 100644
index 0000000000..85a0d9db1b
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/ProxyObject.cpp
@@ -0,0 +1,559 @@
+/*
+ * 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/Accessor.h>
+#include <LibJS/Runtime/Array.h>
+#include <LibJS/Runtime/Error.h>
+#include <LibJS/Runtime/GlobalObject.h>
+#include <LibJS/Runtime/ProxyObject.h>
+
+namespace JS {
+
+bool static is_compatible_property_descriptor(bool is_extensible, PropertyDescriptor new_descriptor, Optional<PropertyDescriptor> current_descriptor_optional)
+{
+ if (!current_descriptor_optional.has_value())
+ return is_extensible;
+ auto current_descriptor = current_descriptor_optional.value();
+ if (new_descriptor.attributes.is_empty() && new_descriptor.value.is_empty() && !new_descriptor.getter && !new_descriptor.setter)
+ return true;
+ if (!current_descriptor.attributes.is_configurable()) {
+ if (new_descriptor.attributes.is_configurable())
+ return false;
+ if (new_descriptor.attributes.has_enumerable() && new_descriptor.attributes.is_enumerable() != current_descriptor.attributes.is_enumerable())
+ return false;
+ }
+ if (new_descriptor.is_generic_descriptor())
+ return true;
+ if (current_descriptor.is_data_descriptor() != new_descriptor.is_data_descriptor() && !current_descriptor.attributes.is_configurable())
+ return false;
+ if (current_descriptor.is_data_descriptor() && new_descriptor.is_data_descriptor() && !current_descriptor.attributes.is_configurable() && !current_descriptor.attributes.is_writable()) {
+ if (new_descriptor.attributes.is_writable())
+ return false;
+ return new_descriptor.value.is_empty() && same_value(new_descriptor.value, current_descriptor.value);
+ }
+ return true;
+}
+
+ProxyObject* ProxyObject::create(GlobalObject& global_object, Object& target, Object& handler)
+{
+ return global_object.heap().allocate<ProxyObject>(global_object, target, handler, *global_object.object_prototype());
+}
+
+ProxyObject::ProxyObject(Object& target, Object& handler, Object& prototype)
+ : Function(prototype)
+ , m_target(target)
+ , m_handler(handler)
+{
+}
+
+ProxyObject::~ProxyObject()
+{
+}
+
+Object* ProxyObject::prototype()
+{
+ auto& vm = this->vm();
+ if (m_is_revoked) {
+ vm.throw_exception<TypeError>(global_object(), ErrorType::ProxyRevoked);
+ return nullptr;
+ }
+ auto trap = m_handler.get(vm.names.getPrototypeOf);
+ if (vm.exception())
+ return nullptr;
+ if (trap.is_empty() || trap.is_nullish())
+ return m_target.prototype();
+ if (!trap.is_function()) {
+ vm.throw_exception<TypeError>(global_object(), ErrorType::ProxyInvalidTrap, "getPrototypeOf");
+ return nullptr;
+ }
+
+ auto trap_result = vm.call(trap.as_function(), Value(&m_handler), Value(&m_target));
+ if (vm.exception())
+ return nullptr;
+ if (!trap_result.is_object() && !trap_result.is_null()) {
+ vm.throw_exception<TypeError>(global_object(), ErrorType::ProxyGetPrototypeOfReturn);
+ return nullptr;
+ }
+ if (m_target.is_extensible()) {
+ if (vm.exception())
+ return nullptr;
+ if (trap_result.is_null())
+ return nullptr;
+ return &trap_result.as_object();
+ }
+ auto target_proto = m_target.prototype();
+ if (vm.exception())
+ return nullptr;
+ if (!same_value(trap_result, Value(target_proto))) {
+ vm.throw_exception<TypeError>(global_object(), ErrorType::ProxyGetPrototypeOfNonExtensible);
+ return nullptr;
+ }
+ return &trap_result.as_object();
+}
+
+const Object* ProxyObject::prototype() const
+{
+ auto& vm = this->vm();
+ if (m_is_revoked) {
+ vm.throw_exception<TypeError>(global_object(), ErrorType::ProxyRevoked);
+ return nullptr;
+ }
+ return const_cast<const Object*>(const_cast<ProxyObject*>(this)->prototype());
+}
+
+bool ProxyObject::set_prototype(Object* object)
+{
+ auto& vm = this->vm();
+ if (m_is_revoked) {
+ vm.throw_exception<TypeError>(global_object(), ErrorType::ProxyRevoked);
+ return false;
+ }
+ auto trap = m_handler.get(vm.names.setPrototypeOf);
+ if (vm.exception())
+ return false;
+ if (trap.is_empty() || trap.is_nullish())
+ return m_target.set_prototype(object);
+ if (!trap.is_function()) {
+ vm.throw_exception<TypeError>(global_object(), ErrorType::ProxyInvalidTrap, "setPrototypeOf");
+ return false;
+ }
+
+ auto trap_result = vm.call(trap.as_function(), Value(&m_handler), Value(&m_target), Value(object));
+ if (vm.exception() || !trap_result.to_boolean())
+ return false;
+ if (m_target.is_extensible())
+ return true;
+ auto* target_proto = m_target.prototype();
+ if (vm.exception())
+ return false;
+ if (!same_value(Value(object), Value(target_proto))) {
+ vm.throw_exception<TypeError>(global_object(), ErrorType::ProxySetPrototypeOfNonExtensible);
+ return false;
+ }
+ return true;
+}
+
+bool ProxyObject::is_extensible() const
+{
+ auto& vm = this->vm();
+ if (m_is_revoked) {
+ vm.throw_exception<TypeError>(global_object(), ErrorType::ProxyRevoked);
+ return false;
+ }
+ auto trap = m_handler.get(vm.names.isExtensible);
+ if (vm.exception())
+ return false;
+ if (trap.is_empty() || trap.is_nullish())
+ return m_target.is_extensible();
+ if (!trap.is_function()) {
+ vm.throw_exception<TypeError>(global_object(), ErrorType::ProxyInvalidTrap, "isExtensible");
+ return {};
+ }
+
+ auto trap_result = vm.call(trap.as_function(), Value(&m_handler), Value(&m_target));
+ if (vm.exception())
+ return false;
+ if (trap_result.to_boolean() != m_target.is_extensible()) {
+ if (!vm.exception())
+ vm.throw_exception<TypeError>(global_object(), ErrorType::ProxyIsExtensibleReturn);
+ return false;
+ }
+ return trap_result.to_boolean();
+}
+
+bool ProxyObject::prevent_extensions()
+{
+ auto& vm = this->vm();
+ if (m_is_revoked) {
+ vm.throw_exception<TypeError>(global_object(), ErrorType::ProxyRevoked);
+ return false;
+ }
+ auto trap = m_handler.get(vm.names.preventExtensions);
+ if (vm.exception())
+ return false;
+ if (trap.is_empty() || trap.is_nullish())
+ return m_target.prevent_extensions();
+ if (!trap.is_function()) {
+ vm.throw_exception<TypeError>(global_object(), ErrorType::ProxyInvalidTrap, "preventExtensions");
+ return {};
+ }
+
+ auto trap_result = vm.call(trap.as_function(), Value(&m_handler), Value(&m_target));
+ if (vm.exception())
+ return false;
+ if (trap_result.to_boolean() && m_target.is_extensible()) {
+ if (!vm.exception())
+ vm.throw_exception<TypeError>(global_object(), ErrorType::ProxyPreventExtensionsReturn);
+ return false;
+ }
+ return trap_result.to_boolean();
+}
+
+Optional<PropertyDescriptor> ProxyObject::get_own_property_descriptor(const PropertyName& name) const
+{
+ auto& vm = this->vm();
+ if (m_is_revoked) {
+ vm.throw_exception<TypeError>(global_object(), ErrorType::ProxyRevoked);
+ return {};
+ }
+ auto trap = m_handler.get(vm.names.getOwnPropertyDescriptor);
+ if (vm.exception())
+ return {};
+ if (trap.is_empty() || trap.is_nullish())
+ return m_target.get_own_property_descriptor(name);
+ if (!trap.is_function()) {
+ vm.throw_exception<TypeError>(global_object(), ErrorType::ProxyInvalidTrap, "getOwnPropertyDescriptor");
+ return {};
+ }
+
+ auto trap_result = vm.call(trap.as_function(), Value(&m_handler), Value(&m_target), name.to_value(vm));
+ if (vm.exception())
+ return {};
+ if (!trap_result.is_object() && !trap_result.is_undefined()) {
+ vm.throw_exception<TypeError>(global_object(), ErrorType::ProxyGetOwnDescriptorReturn);
+ return {};
+ }
+ auto target_desc = m_target.get_own_property_descriptor(name);
+ if (vm.exception())
+ return {};
+ if (trap_result.is_undefined()) {
+ if (!target_desc.has_value())
+ return {};
+ if (!target_desc.value().attributes.is_configurable()) {
+ vm.throw_exception<TypeError>(global_object(), ErrorType::ProxyGetOwnDescriptorNonConfigurable);
+ return {};
+ }
+ if (!m_target.is_extensible()) {
+ if (!vm.exception())
+ vm.throw_exception<TypeError>(global_object(), ErrorType::ProxyGetOwnDescriptorUndefReturn);
+ return {};
+ }
+ return {};
+ }
+ auto result_desc = PropertyDescriptor::from_dictionary(vm, trap_result.as_object());
+ if (vm.exception())
+ return {};
+ if (!is_compatible_property_descriptor(m_target.is_extensible(), result_desc, target_desc)) {
+ if (!vm.exception())
+ vm.throw_exception<TypeError>(global_object(), ErrorType::ProxyGetOwnDescriptorInvalidDescriptor);
+ return {};
+ }
+ if (!result_desc.attributes.is_configurable() && (!target_desc.has_value() || target_desc.value().attributes.is_configurable())) {
+ vm.throw_exception<TypeError>(global_object(), ErrorType::ProxyGetOwnDescriptorInvalidNonConfig);
+ return {};
+ }
+ return result_desc;
+}
+
+bool ProxyObject::define_property(const StringOrSymbol& property_name, const Object& descriptor, bool throw_exceptions)
+{
+ auto& vm = this->vm();
+ if (m_is_revoked) {
+ vm.throw_exception<TypeError>(global_object(), ErrorType::ProxyRevoked);
+ return false;
+ }
+ auto trap = m_handler.get(vm.names.defineProperty);
+ if (vm.exception())
+ return false;
+ if (trap.is_empty() || trap.is_nullish())
+ return m_target.define_property(property_name, descriptor, throw_exceptions);
+ if (!trap.is_function()) {
+ vm.throw_exception<TypeError>(global_object(), ErrorType::ProxyInvalidTrap, "defineProperty");
+ return false;
+ }
+
+ auto trap_result = vm.call(trap.as_function(), Value(&m_handler), Value(&m_target), property_name.to_value(vm), Value(const_cast<Object*>(&descriptor)));
+ if (vm.exception() || !trap_result.to_boolean())
+ return false;
+ auto target_desc = m_target.get_own_property_descriptor(property_name);
+ if (vm.exception())
+ return false;
+ bool setting_config_false = false;
+ if (descriptor.has_property(vm.names.configurable) && !descriptor.get(vm.names.configurable).to_boolean())
+ setting_config_false = true;
+ if (vm.exception())
+ return false;
+ if (!target_desc.has_value()) {
+ if (!m_target.is_extensible()) {
+ if (!vm.exception())
+ vm.throw_exception<TypeError>(global_object(), ErrorType::ProxyDefinePropNonExtensible);
+ return false;
+ }
+ if (setting_config_false) {
+ vm.throw_exception<TypeError>(global_object(), ErrorType::ProxyDefinePropNonConfigurableNonExisting);
+ return false;
+ }
+ } else {
+ if (!is_compatible_property_descriptor(m_target.is_extensible(), PropertyDescriptor::from_dictionary(vm, descriptor), target_desc)) {
+ if (!vm.exception())
+ vm.throw_exception<TypeError>(global_object(), ErrorType::ProxyDefinePropIncompatibleDescriptor);
+ return false;
+ }
+ if (setting_config_false && target_desc.value().attributes.is_configurable()) {
+ vm.throw_exception<TypeError>(global_object(), ErrorType::ProxyDefinePropExistingConfigurable);
+ return false;
+ }
+ }
+ return true;
+}
+
+bool ProxyObject::has_property(const PropertyName& name) const
+{
+ auto& vm = this->vm();
+ if (m_is_revoked) {
+ vm.throw_exception<TypeError>(global_object(), ErrorType::ProxyRevoked);
+ return false;
+ }
+ auto trap = m_handler.get(vm.names.has);
+ if (vm.exception())
+ return false;
+ if (trap.is_empty() || trap.is_nullish())
+ return m_target.has_property(name);
+ if (!trap.is_function()) {
+ vm.throw_exception<TypeError>(global_object(), ErrorType::ProxyInvalidTrap, "has");
+ return false;
+ }
+
+ auto trap_result = vm.call(trap.as_function(), Value(&m_handler), Value(&m_target), name.to_value(vm));
+ if (vm.exception())
+ return false;
+ if (!trap_result.to_boolean()) {
+ auto target_desc = m_target.get_own_property_descriptor(name);
+ if (vm.exception())
+ return false;
+ if (target_desc.has_value()) {
+ if (!target_desc.value().attributes.is_configurable()) {
+ vm.throw_exception<TypeError>(global_object(), ErrorType::ProxyHasExistingNonConfigurable);
+ return false;
+ }
+ if (!m_target.is_extensible()) {
+ if (!vm.exception())
+ vm.throw_exception<TypeError>(global_object(), ErrorType::ProxyHasExistingNonExtensible);
+ return false;
+ }
+ }
+ }
+ return trap_result.to_boolean();
+}
+
+Value ProxyObject::get(const PropertyName& name, Value receiver) const
+{
+ auto& vm = this->vm();
+ if (m_is_revoked) {
+ vm.throw_exception<TypeError>(global_object(), ErrorType::ProxyRevoked);
+ return {};
+ }
+ if (receiver.is_empty())
+ receiver = Value(const_cast<ProxyObject*>(this));
+ auto trap = m_handler.get(vm.names.get);
+ if (vm.exception())
+ return {};
+ if (trap.is_empty() || trap.is_nullish())
+ return m_target.get(name, receiver);
+ if (!trap.is_function()) {
+ vm.throw_exception<TypeError>(global_object(), ErrorType::ProxyInvalidTrap, "get");
+ return {};
+ }
+
+ auto trap_result = vm.call(trap.as_function(), Value(&m_handler), Value(&m_target), name.to_value(vm), receiver);
+ if (vm.exception())
+ return {};
+ auto target_desc = m_target.get_own_property_descriptor(name);
+ if (target_desc.has_value()) {
+ if (vm.exception())
+ return {};
+ if (target_desc.value().is_data_descriptor() && !target_desc.value().attributes.is_writable() && !same_value(trap_result, target_desc.value().value)) {
+ vm.throw_exception<TypeError>(global_object(), ErrorType::ProxyGetImmutableDataProperty);
+ return {};
+ }
+ if (target_desc.value().is_accessor_descriptor() && target_desc.value().getter == nullptr && !trap_result.is_undefined()) {
+ vm.throw_exception<TypeError>(global_object(), ErrorType::ProxyGetNonConfigurableAccessor);
+ return {};
+ }
+ }
+ return trap_result;
+}
+
+bool ProxyObject::put(const PropertyName& name, Value value, Value receiver)
+{
+ auto& vm = this->vm();
+ if (m_is_revoked) {
+ vm.throw_exception<TypeError>(global_object(), ErrorType::ProxyRevoked);
+ return false;
+ }
+ if (receiver.is_empty())
+ receiver = Value(const_cast<ProxyObject*>(this));
+ auto trap = m_handler.get(vm.names.set);
+ if (vm.exception())
+ return false;
+ if (trap.is_empty() || trap.is_nullish())
+ return m_target.put(name, value, receiver);
+ if (!trap.is_function()) {
+ vm.throw_exception<TypeError>(global_object(), ErrorType::ProxyInvalidTrap, "set");
+ return false;
+ }
+ auto trap_result = vm.call(trap.as_function(), Value(&m_handler), Value(&m_target), name.to_value(vm), value, receiver);
+ if (vm.exception() || !trap_result.to_boolean())
+ return false;
+ auto target_desc = m_target.get_own_property_descriptor(name);
+ if (vm.exception())
+ return false;
+ if (target_desc.has_value() && !target_desc.value().attributes.is_configurable()) {
+ if (target_desc.value().is_data_descriptor() && !target_desc.value().attributes.is_writable() && !same_value(value, target_desc.value().value)) {
+ vm.throw_exception<TypeError>(global_object(), ErrorType::ProxySetImmutableDataProperty);
+ return false;
+ }
+ if (target_desc.value().is_accessor_descriptor() && !target_desc.value().setter) {
+ vm.throw_exception<TypeError>(global_object(), ErrorType::ProxySetNonConfigurableAccessor);
+ }
+ }
+ return true;
+}
+
+Value ProxyObject::delete_property(const PropertyName& name)
+{
+ auto& vm = this->vm();
+ if (m_is_revoked) {
+ vm.throw_exception<TypeError>(global_object(), ErrorType::ProxyRevoked);
+ return {};
+ }
+ auto trap = m_handler.get(vm.names.deleteProperty);
+ if (vm.exception())
+ return {};
+ if (trap.is_empty() || trap.is_nullish())
+ return m_target.delete_property(name);
+ if (!trap.is_function()) {
+ vm.throw_exception<TypeError>(global_object(), ErrorType::ProxyInvalidTrap, "deleteProperty");
+ return {};
+ }
+
+ auto trap_result = vm.call(trap.as_function(), Value(&m_handler), Value(&m_target), name.to_value(vm));
+ if (vm.exception())
+ return {};
+ if (!trap_result.to_boolean())
+ return Value(false);
+ auto target_desc = m_target.get_own_property_descriptor(name);
+ if (vm.exception())
+ return {};
+ if (!target_desc.has_value())
+ return Value(true);
+ if (!target_desc.value().attributes.is_configurable()) {
+ vm.throw_exception<TypeError>(global_object(), ErrorType::ProxyDeleteNonConfigurable);
+ return {};
+ }
+ return Value(true);
+}
+
+void ProxyObject::visit_edges(Cell::Visitor& visitor)
+{
+ Function::visit_edges(visitor);
+ visitor.visit(&m_target);
+ visitor.visit(&m_handler);
+}
+
+Value ProxyObject::call()
+{
+ auto& vm = this->vm();
+ if (!is_function()) {
+ vm.throw_exception<TypeError>(global_object(), ErrorType::NotAFunction, Value(this).to_string_without_side_effects());
+ return {};
+ }
+ if (m_is_revoked) {
+ vm.throw_exception<TypeError>(global_object(), ErrorType::ProxyRevoked);
+ return {};
+ }
+ auto trap = m_handler.get(vm.names.apply);
+ if (vm.exception())
+ return {};
+ if (trap.is_empty() || trap.is_nullish())
+ return static_cast<Function&>(m_target).call();
+ if (!trap.is_function()) {
+ vm.throw_exception<TypeError>(global_object(), ErrorType::ProxyInvalidTrap, "apply");
+ return {};
+ }
+ MarkedValueList arguments(heap());
+ arguments.append(Value(&m_target));
+ arguments.append(Value(&m_handler));
+ // FIXME: Pass global object
+ auto arguments_array = Array::create(global_object());
+ vm.for_each_argument([&](auto& argument) {
+ arguments_array->indexed_properties().append(argument);
+ });
+ arguments.append(arguments_array);
+
+ return vm.call(trap.as_function(), Value(&m_handler), move(arguments));
+}
+
+Value ProxyObject::construct(Function& new_target)
+{
+ auto& vm = this->vm();
+ if (!is_function()) {
+ vm.throw_exception<TypeError>(global_object(), ErrorType::NotAConstructor, Value(this).to_string_without_side_effects());
+ return {};
+ }
+ if (m_is_revoked) {
+ vm.throw_exception<TypeError>(global_object(), ErrorType::ProxyRevoked);
+ return {};
+ }
+ auto trap = m_handler.get(vm.names.construct);
+ if (vm.exception())
+ return {};
+ if (trap.is_empty() || trap.is_nullish())
+ return static_cast<Function&>(m_target).construct(new_target);
+ if (!trap.is_function()) {
+ vm.throw_exception<TypeError>(global_object(), ErrorType::ProxyInvalidTrap, "construct");
+ return {};
+ }
+
+ MarkedValueList arguments(vm.heap());
+ arguments.append(Value(&m_target));
+ auto arguments_array = Array::create(global_object());
+ vm.for_each_argument([&](auto& argument) {
+ arguments_array->indexed_properties().append(argument);
+ });
+ arguments.append(arguments_array);
+ arguments.append(Value(&new_target));
+ auto result = vm.call(trap.as_function(), Value(&m_handler), move(arguments));
+ if (!result.is_object()) {
+ vm.throw_exception<TypeError>(global_object(), ErrorType::ProxyConstructBadReturnType);
+ return {};
+ }
+ return result;
+}
+
+const FlyString& ProxyObject::name() const
+{
+ ASSERT(is_function());
+ return static_cast<Function&>(m_target).name();
+}
+
+LexicalEnvironment* ProxyObject::create_environment()
+{
+ ASSERT(is_function());
+ return static_cast<Function&>(m_target).create_environment();
+}
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/ProxyObject.h b/Userland/Libraries/LibJS/Runtime/ProxyObject.h
new file mode 100644
index 0000000000..70f30fac2c
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/ProxyObject.h
@@ -0,0 +1,75 @@
+/*
+ * 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/Function.h>
+
+namespace JS {
+
+class ProxyObject final : public Function {
+ JS_OBJECT(ProxyObject, Function);
+
+public:
+ static ProxyObject* create(GlobalObject&, Object& target, Object& handler);
+
+ ProxyObject(Object& target, Object& handler, Object& prototype);
+ virtual ~ProxyObject() override;
+
+ virtual Value call() override;
+ virtual Value construct(Function& new_target) override;
+ virtual const FlyString& name() const override;
+ virtual LexicalEnvironment* create_environment() override;
+
+ const Object& target() const { return m_target; }
+ const Object& handler() const { return m_handler; }
+
+ virtual Object* prototype() override;
+ virtual const Object* prototype() const override;
+ virtual bool set_prototype(Object* object) override;
+ virtual bool is_extensible() const override;
+ virtual bool prevent_extensions() override;
+ virtual Optional<PropertyDescriptor> get_own_property_descriptor(const PropertyName&) const override;
+ virtual bool define_property(const StringOrSymbol& property_name, const Object& descriptor, bool throw_exceptions = true) override;
+ virtual bool has_property(const PropertyName& name) const override;
+ virtual Value get(const PropertyName& name, Value receiver) const override;
+ virtual bool put(const PropertyName& name, Value value, Value receiver) override;
+ virtual Value delete_property(const PropertyName& name) override;
+
+ void revoke() { m_is_revoked = true; }
+
+private:
+ virtual void visit_edges(Visitor&) override;
+
+ virtual bool is_function() const override { return m_target.is_function(); }
+ virtual bool is_array() const override { return m_target.is_array(); };
+
+ Object& m_target;
+ Object& m_handler;
+ bool m_is_revoked { false };
+};
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/Reference.cpp b/Userland/Libraries/LibJS/Runtime/Reference.cpp
new file mode 100644
index 0000000000..6b6b06f80f
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/Reference.cpp
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * 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 <AK/StringBuilder.h>
+#include <LibJS/Runtime/Error.h>
+#include <LibJS/Runtime/GlobalObject.h>
+#include <LibJS/Runtime/Object.h>
+#include <LibJS/Runtime/Reference.h>
+
+namespace JS {
+
+void Reference::put(GlobalObject& global_object, Value value)
+{
+ auto& vm = global_object.vm();
+
+ if (is_unresolvable()) {
+ throw_reference_error(global_object);
+ return;
+ }
+
+ if (is_local_variable() || is_global_variable()) {
+ if (is_local_variable())
+ vm.set_variable(m_name.to_string(), value, global_object);
+ else
+ global_object.put(m_name, value);
+ return;
+ }
+
+ if (!base().is_object() && vm.in_strict_mode()) {
+ vm.throw_exception<TypeError>(global_object, ErrorType::ReferencePrimitiveAssignment, m_name.to_value(global_object.vm()).to_string_without_side_effects());
+ return;
+ }
+
+ auto* object = base().to_object(global_object);
+ if (!object)
+ return;
+
+ object->put(m_name, value);
+}
+
+void Reference::throw_reference_error(GlobalObject& global_object)
+{
+ auto property_name = m_name.to_string();
+ String message;
+ if (property_name.is_empty()) {
+ global_object.vm().throw_exception<ReferenceError>(global_object, ErrorType::ReferenceUnresolvable);
+ } else {
+ global_object.vm().throw_exception<ReferenceError>(global_object, ErrorType::UnknownIdentifier, property_name);
+ }
+}
+
+Value Reference::get(GlobalObject& global_object)
+{
+ auto& vm = global_object.vm();
+
+ if (is_unresolvable()) {
+ throw_reference_error(global_object);
+ return {};
+ }
+
+ if (is_local_variable() || is_global_variable()) {
+ Value value;
+ if (is_local_variable())
+ value = vm.get_variable(m_name.to_string(), global_object);
+ else
+ value = global_object.get(m_name);
+ if (vm.exception())
+ return {};
+ if (value.is_empty()) {
+ throw_reference_error(global_object);
+ return {};
+ }
+ return value;
+ }
+
+ auto* object = base().to_object(global_object);
+ if (!object)
+ return {};
+
+ return object->get(m_name).value_or(js_undefined());
+}
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/Reference.h b/Userland/Libraries/LibJS/Runtime/Reference.h
new file mode 100644
index 0000000000..c81d964440
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/Reference.h
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * 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 <AK/String.h>
+#include <LibJS/Runtime/PropertyName.h>
+#include <LibJS/Runtime/Value.h>
+
+namespace JS {
+
+class Reference {
+public:
+ Reference() { }
+ Reference(Value base, const PropertyName& name, bool strict = false)
+ : m_base(base)
+ , m_name(name)
+ , m_strict(strict)
+ {
+ }
+
+ enum LocalVariableTag { LocalVariable };
+ Reference(LocalVariableTag, const String& name, bool strict = false)
+ : m_base(js_null())
+ , m_name(name)
+ , m_strict(strict)
+ , m_local_variable(true)
+ {
+ }
+
+ enum GlobalVariableTag { GlobalVariable };
+ Reference(GlobalVariableTag, const String& name, bool strict = false)
+ : m_base(js_null())
+ , m_name(name)
+ , m_strict(strict)
+ , m_global_variable(true)
+ {
+ }
+
+ Value base() const { return m_base; }
+ const PropertyName& name() const { return m_name; }
+ bool is_strict() const { return m_strict; }
+
+ bool is_unresolvable() const { return m_base.is_undefined(); }
+ bool is_property() const
+ {
+ return m_base.is_object() || has_primitive_base();
+ }
+
+ bool has_primitive_base() const
+ {
+ return m_base.is_boolean() || m_base.is_string() || m_base.is_number();
+ }
+
+ bool is_local_variable() const
+ {
+ return m_local_variable;
+ }
+
+ bool is_global_variable() const
+ {
+ return m_global_variable;
+ }
+
+ void put(GlobalObject&, Value);
+ Value get(GlobalObject&);
+
+private:
+ void throw_reference_error(GlobalObject&);
+
+ Value m_base { js_undefined() };
+ PropertyName m_name;
+ bool m_strict { false };
+ bool m_local_variable { false };
+ bool m_global_variable { false };
+};
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/ReflectObject.cpp b/Userland/Libraries/LibJS/Runtime/ReflectObject.cpp
new file mode 100644
index 0000000000..1b7865cc8e
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/ReflectObject.cpp
@@ -0,0 +1,280 @@
+/*
+ * Copyright (c) 2020, Linus Groh <mail@linusgroh.de>
+ * 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 <AK/Function.h>
+#include <LibJS/Runtime/Error.h>
+#include <LibJS/Runtime/Function.h>
+#include <LibJS/Runtime/GlobalObject.h>
+#include <LibJS/Runtime/NativeFunction.h>
+#include <LibJS/Runtime/ReflectObject.h>
+
+namespace JS {
+
+static Object* get_target_object_from(GlobalObject& global_object, const String& name)
+{
+ auto& vm = global_object.vm();
+ auto target = vm.argument(0);
+ if (!target.is_object()) {
+ vm.throw_exception<TypeError>(global_object, ErrorType::ReflectArgumentMustBeAnObject, name);
+ return nullptr;
+ }
+ return static_cast<Object*>(&target.as_object());
+}
+
+static Function* get_target_function_from(GlobalObject& global_object, const String& name)
+{
+ auto& vm = global_object.vm();
+ auto target = vm.argument(0);
+ if (!target.is_function()) {
+ vm.throw_exception<TypeError>(global_object, ErrorType::ReflectArgumentMustBeAFunction, name);
+ return nullptr;
+ }
+ return &target.as_function();
+}
+
+static void prepare_arguments_list(GlobalObject& global_object, Value value, MarkedValueList* arguments)
+{
+ auto& vm = global_object.vm();
+ if (!value.is_object()) {
+ vm.throw_exception<TypeError>(global_object, ErrorType::ReflectBadArgumentsList);
+ return;
+ }
+ auto& arguments_list = value.as_object();
+ auto length = length_of_array_like(global_object, arguments_list);
+ if (vm.exception())
+ return;
+ for (size_t i = 0; i < length; ++i) {
+ auto element = arguments_list.get(String::number(i));
+ if (vm.exception())
+ return;
+ arguments->append(element.value_or(js_undefined()));
+ }
+}
+
+ReflectObject::ReflectObject(GlobalObject& global_object)
+ : Object(*global_object.object_prototype())
+{
+}
+
+void ReflectObject::initialize(GlobalObject& global_object)
+{
+ auto& vm = this->vm();
+ Object::initialize(global_object);
+ u8 attr = Attribute::Writable | Attribute::Configurable;
+ define_native_function(vm.names.apply, apply, 3, attr);
+ define_native_function(vm.names.construct, construct, 2, attr);
+ define_native_function(vm.names.defineProperty, define_property, 3, attr);
+ define_native_function(vm.names.deleteProperty, delete_property, 2, attr);
+ define_native_function(vm.names.get, get, 2, attr);
+ define_native_function(vm.names.getOwnPropertyDescriptor, get_own_property_descriptor, 2, attr);
+ define_native_function(vm.names.getPrototypeOf, get_prototype_of, 1, attr);
+ define_native_function(vm.names.has, has, 2, attr);
+ define_native_function(vm.names.isExtensible, is_extensible, 1, attr);
+ define_native_function(vm.names.ownKeys, own_keys, 1, attr);
+ define_native_function(vm.names.preventExtensions, prevent_extensions, 1, attr);
+ define_native_function(vm.names.set, set, 3, attr);
+ define_native_function(vm.names.setPrototypeOf, set_prototype_of, 2, attr);
+}
+
+ReflectObject::~ReflectObject()
+{
+}
+
+JS_DEFINE_NATIVE_FUNCTION(ReflectObject::apply)
+{
+ auto* target = get_target_function_from(global_object, "apply");
+ if (!target)
+ return {};
+ auto this_arg = vm.argument(1);
+ MarkedValueList arguments(vm.heap());
+ prepare_arguments_list(global_object, vm.argument(2), &arguments);
+ if (vm.exception())
+ return {};
+ return vm.call(*target, this_arg, move(arguments));
+}
+
+JS_DEFINE_NATIVE_FUNCTION(ReflectObject::construct)
+{
+ auto* target = get_target_function_from(global_object, "construct");
+ if (!target)
+ return {};
+ MarkedValueList arguments(vm.heap());
+ prepare_arguments_list(global_object, vm.argument(1), &arguments);
+ if (vm.exception())
+ return {};
+ auto* new_target = target;
+ if (vm.argument_count() > 2) {
+ auto new_target_value = vm.argument(2);
+ if (!new_target_value.is_function()
+ || (is<NativeFunction>(new_target_value.as_object()) && !static_cast<NativeFunction&>(new_target_value.as_object()).has_constructor())) {
+ vm.throw_exception<TypeError>(global_object, ErrorType::ReflectBadNewTarget);
+ return {};
+ }
+ new_target = &new_target_value.as_function();
+ }
+ return vm.construct(*target, *new_target, move(arguments), global_object);
+}
+
+JS_DEFINE_NATIVE_FUNCTION(ReflectObject::define_property)
+{
+ auto* target = get_target_object_from(global_object, "defineProperty");
+ if (!target)
+ return {};
+ if (!vm.argument(2).is_object()) {
+ vm.throw_exception<TypeError>(global_object, ErrorType::ReflectBadDescriptorArgument);
+ return {};
+ }
+ auto property_key = StringOrSymbol::from_value(global_object, vm.argument(1));
+ if (vm.exception())
+ return {};
+ auto& descriptor = vm.argument(2).as_object();
+ auto success = target->define_property(property_key, descriptor, false);
+ if (vm.exception())
+ return {};
+ return Value(success);
+}
+
+JS_DEFINE_NATIVE_FUNCTION(ReflectObject::delete_property)
+{
+ auto* target = get_target_object_from(global_object, "deleteProperty");
+ if (!target)
+ return {};
+
+ auto property_key = vm.argument(1);
+ auto property_name = PropertyName::from_value(global_object, property_key);
+ if (vm.exception())
+ return {};
+ auto property_key_number = property_key.to_number(global_object);
+ if (vm.exception())
+ return {};
+ if (property_key_number.is_finite_number()) {
+ auto property_key_as_double = property_key_number.as_double();
+ if (property_key_as_double >= 0 && (i32)property_key_as_double == property_key_as_double)
+ property_name = PropertyName(property_key_as_double);
+ }
+ return target->delete_property(property_name);
+}
+
+JS_DEFINE_NATIVE_FUNCTION(ReflectObject::get)
+{
+ auto* target = get_target_object_from(global_object, "get");
+ if (!target)
+ return {};
+ auto property_key = PropertyName::from_value(global_object, vm.argument(1));
+ if (vm.exception())
+ return {};
+ Value receiver = {};
+ if (vm.argument_count() > 2)
+ receiver = vm.argument(2);
+ return target->get(property_key, receiver).value_or(js_undefined());
+}
+
+JS_DEFINE_NATIVE_FUNCTION(ReflectObject::get_own_property_descriptor)
+{
+ auto* target = get_target_object_from(global_object, "getOwnPropertyDescriptor");
+ if (!target)
+ return {};
+ auto property_key = PropertyName::from_value(global_object, vm.argument(1));
+ if (vm.exception())
+ return {};
+ return target->get_own_property_descriptor_object(property_key);
+}
+
+JS_DEFINE_NATIVE_FUNCTION(ReflectObject::get_prototype_of)
+{
+ auto* target = get_target_object_from(global_object, "getPrototypeOf");
+ if (!target)
+ return {};
+ return target->prototype();
+}
+
+JS_DEFINE_NATIVE_FUNCTION(ReflectObject::has)
+{
+ auto* target = get_target_object_from(global_object, "has");
+ if (!target)
+ return {};
+ auto property_key = PropertyName::from_value(global_object, vm.argument(1));
+ if (vm.exception())
+ return {};
+ return Value(target->has_property(property_key));
+}
+
+JS_DEFINE_NATIVE_FUNCTION(ReflectObject::is_extensible)
+{
+ auto* target = get_target_object_from(global_object, "isExtensible");
+ if (!target)
+ return {};
+ return Value(target->is_extensible());
+}
+
+JS_DEFINE_NATIVE_FUNCTION(ReflectObject::own_keys)
+{
+ auto* target = get_target_object_from(global_object, "ownKeys");
+ if (!target)
+ return {};
+ return target->get_own_properties(*target, PropertyKind::Key);
+}
+
+JS_DEFINE_NATIVE_FUNCTION(ReflectObject::prevent_extensions)
+{
+ auto* target = get_target_object_from(global_object, "preventExtensions");
+ if (!target)
+ return {};
+ return Value(target->prevent_extensions());
+}
+
+JS_DEFINE_NATIVE_FUNCTION(ReflectObject::set)
+{
+ auto* target = get_target_object_from(global_object, "set");
+ if (!target)
+ return {};
+ auto property_key = vm.argument(1).to_string(global_object);
+ if (vm.exception())
+ return {};
+ auto value = vm.argument(2);
+ Value receiver = {};
+ if (vm.argument_count() > 3)
+ receiver = vm.argument(3);
+ return Value(target->put(property_key, value, receiver));
+}
+
+JS_DEFINE_NATIVE_FUNCTION(ReflectObject::set_prototype_of)
+{
+ auto* target = get_target_object_from(global_object, "setPrototypeOf");
+ if (!target)
+ return {};
+ auto prototype_value = vm.argument(1);
+ if (!prototype_value.is_object() && !prototype_value.is_null()) {
+ vm.throw_exception<TypeError>(global_object, ErrorType::ObjectPrototypeWrongType);
+ return {};
+ }
+ Object* prototype = nullptr;
+ if (!prototype_value.is_null())
+ prototype = const_cast<Object*>(&prototype_value.as_object());
+ return Value(target->set_prototype(prototype));
+}
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/ReflectObject.h b/Userland/Libraries/LibJS/Runtime/ReflectObject.h
new file mode 100644
index 0000000000..68f2fd497e
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/ReflectObject.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2020, Linus Groh <mail@linusgroh.de>
+ * 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 ReflectObject final : public Object {
+ JS_OBJECT(ReflectObject, Object);
+
+public:
+ explicit ReflectObject(GlobalObject&);
+ virtual void initialize(GlobalObject&) override;
+ virtual ~ReflectObject() override;
+
+private:
+ JS_DECLARE_NATIVE_FUNCTION(apply);
+ JS_DECLARE_NATIVE_FUNCTION(construct);
+ JS_DECLARE_NATIVE_FUNCTION(define_property);
+ JS_DECLARE_NATIVE_FUNCTION(delete_property);
+ JS_DECLARE_NATIVE_FUNCTION(get);
+ JS_DECLARE_NATIVE_FUNCTION(get_own_property_descriptor);
+ JS_DECLARE_NATIVE_FUNCTION(get_prototype_of);
+ JS_DECLARE_NATIVE_FUNCTION(has);
+ JS_DECLARE_NATIVE_FUNCTION(is_extensible);
+ JS_DECLARE_NATIVE_FUNCTION(own_keys);
+ JS_DECLARE_NATIVE_FUNCTION(prevent_extensions);
+ JS_DECLARE_NATIVE_FUNCTION(set);
+ JS_DECLARE_NATIVE_FUNCTION(set_prototype_of);
+};
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/RegExpConstructor.cpp b/Userland/Libraries/LibJS/Runtime/RegExpConstructor.cpp
new file mode 100644
index 0000000000..477261b664
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/RegExpConstructor.cpp
@@ -0,0 +1,74 @@
+/*
+ * 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/Error.h>
+#include <LibJS/Runtime/GlobalObject.h>
+#include <LibJS/Runtime/RegExpConstructor.h>
+#include <LibJS/Runtime/RegExpObject.h>
+
+namespace JS {
+
+RegExpConstructor::RegExpConstructor(GlobalObject& global_object)
+ : NativeFunction(vm().names.RegExp, *global_object.function_prototype())
+{
+}
+
+void RegExpConstructor::initialize(GlobalObject& global_object)
+{
+ auto& vm = this->vm();
+ NativeFunction::initialize(global_object);
+ define_property(vm.names.prototype, global_object.regexp_prototype(), 0);
+ define_property(vm.names.length, Value(2), Attribute::Configurable);
+}
+
+RegExpConstructor::~RegExpConstructor()
+{
+}
+
+Value RegExpConstructor::call()
+{
+ return construct(*this);
+}
+
+Value RegExpConstructor::construct(Function&)
+{
+ auto& vm = this->vm();
+ String pattern = "";
+ String flags = "";
+ if (!vm.argument(0).is_undefined()) {
+ pattern = vm.argument(0).to_string(global_object());
+ if (vm.exception())
+ return {};
+ }
+ if (!vm.argument(1).is_undefined()) {
+ flags = vm.argument(1).to_string(global_object());
+ if (vm.exception())
+ return {};
+ }
+ return RegExpObject::create(global_object(), pattern, flags);
+}
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/RegExpConstructor.h b/Userland/Libraries/LibJS/Runtime/RegExpConstructor.h
new file mode 100644
index 0000000000..0c3d055636
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/RegExpConstructor.h
@@ -0,0 +1,48 @@
+/*
+ * 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/NativeFunction.h>
+
+namespace JS {
+
+class RegExpConstructor final : public NativeFunction {
+ JS_OBJECT(RegExpConstructor, NativeFunction);
+
+public:
+ explicit RegExpConstructor(GlobalObject&);
+ virtual void initialize(GlobalObject&) override;
+ virtual ~RegExpConstructor() override;
+
+ virtual Value call() override;
+ virtual Value construct(Function& new_target) override;
+
+private:
+ virtual bool has_constructor() const override { return true; }
+};
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/RegExpObject.cpp b/Userland/Libraries/LibJS/Runtime/RegExpObject.cpp
new file mode 100644
index 0000000000..cfe0364497
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/RegExpObject.cpp
@@ -0,0 +1,170 @@
+/*
+ * 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 <AK/Function.h>
+#include <AK/StringBuilder.h>
+#include <LibJS/Heap/Heap.h>
+#include <LibJS/Runtime/GlobalObject.h>
+#include <LibJS/Runtime/PrimitiveString.h>
+#include <LibJS/Runtime/RegExpObject.h>
+#include <LibJS/Runtime/Value.h>
+
+namespace JS {
+
+static Flags options_from(const String& flags, VM& vm, GlobalObject& global_object)
+{
+ bool g = false, i = false, m = false, s = false, u = false, y = false;
+ Flags options {
+ { (regex::ECMAScriptFlags)regex::AllFlags::Global }, // JS regexps are all 'global' by default as per our definition, but the "global" flag enables "stateful".
+ {},
+ };
+
+ for (auto ch : flags) {
+ switch (ch) {
+ case 'g':
+ if (g)
+ vm.throw_exception<SyntaxError>(global_object, ErrorType::RegExpObjectRepeatedFlag, ch);
+ g = true;
+ options.effective_flags |= regex::ECMAScriptFlags::Global;
+ options.declared_flags |= regex::ECMAScriptFlags::Global;
+ break;
+ case 'i':
+ if (i)
+ vm.throw_exception<SyntaxError>(global_object, ErrorType::RegExpObjectRepeatedFlag, ch);
+ i = true;
+ options.effective_flags |= regex::ECMAScriptFlags::Insensitive;
+ options.declared_flags |= regex::ECMAScriptFlags::Insensitive;
+ break;
+ case 'm':
+ if (m)
+ vm.throw_exception<SyntaxError>(global_object, ErrorType::RegExpObjectRepeatedFlag, ch);
+ m = true;
+ options.effective_flags |= regex::ECMAScriptFlags::Multiline;
+ options.declared_flags |= regex::ECMAScriptFlags::Multiline;
+ break;
+ case 's':
+ if (s)
+ vm.throw_exception<SyntaxError>(global_object, ErrorType::RegExpObjectRepeatedFlag, ch);
+ s = true;
+ options.effective_flags |= regex::ECMAScriptFlags::SingleLine;
+ options.declared_flags |= regex::ECMAScriptFlags::SingleLine;
+ break;
+ case 'u':
+ if (u)
+ vm.throw_exception<SyntaxError>(global_object, ErrorType::RegExpObjectRepeatedFlag, ch);
+ u = true;
+ options.effective_flags |= regex::ECMAScriptFlags::Unicode;
+ options.declared_flags |= regex::ECMAScriptFlags::Unicode;
+ break;
+ case 'y':
+ if (y)
+ vm.throw_exception<SyntaxError>(global_object, ErrorType::RegExpObjectRepeatedFlag, ch);
+ y = true;
+ // Now for the more interesting flag, 'sticky' actually unsets 'global', part of which is the default.
+ options.effective_flags.reset_flag(regex::ECMAScriptFlags::Global);
+ // "What's the difference between sticky and global, then", that's simple.
+ // all the other flags imply 'global', and the "global" flag implies 'stateful';
+ // however, the "sticky" flag does *not* imply 'global', only 'stateful'.
+ options.effective_flags |= (regex::ECMAScriptFlags)regex::AllFlags::Internal_Stateful;
+ options.effective_flags |= regex::ECMAScriptFlags::Sticky;
+ options.declared_flags |= regex::ECMAScriptFlags::Sticky;
+ break;
+ default:
+ vm.throw_exception<SyntaxError>(global_object, ErrorType::RegExpObjectBadFlag, ch);
+ return options;
+ }
+ }
+
+ return options;
+}
+
+RegExpObject* RegExpObject::create(GlobalObject& global_object, String pattern, String flags)
+{
+ return global_object.heap().allocate<RegExpObject>(global_object, pattern, flags, *global_object.regexp_prototype());
+}
+
+RegExpObject::RegExpObject(String pattern, String flags, Object& prototype)
+ : Object(prototype)
+ , m_pattern(pattern)
+ , m_flags(flags)
+ , m_active_flags(options_from(m_flags, this->vm(), this->global_object()))
+ , m_regex(pattern, m_active_flags.effective_flags)
+{
+ if (m_regex.parser_result.error != regex::Error::NoError) {
+ vm().throw_exception<SyntaxError>(global_object(), ErrorType::RegExpCompileError, m_regex.error_string());
+ }
+}
+
+void RegExpObject::initialize(GlobalObject& global_object)
+{
+ auto& vm = this->vm();
+ Object::initialize(global_object);
+
+ define_native_property(vm.names.lastIndex, last_index, set_last_index, Attribute::Writable);
+}
+
+RegExpObject::~RegExpObject()
+{
+}
+
+static RegExpObject* regexp_object_from(VM& vm, GlobalObject& global_object)
+{
+ auto* this_object = vm.this_value(global_object).to_object(global_object);
+ if (!this_object)
+ return nullptr;
+ if (!is<RegExpObject>(this_object)) {
+ vm.throw_exception<TypeError>(global_object, ErrorType::NotA, "RegExp");
+ return nullptr;
+ }
+ return static_cast<RegExpObject*>(this_object);
+}
+
+JS_DEFINE_NATIVE_GETTER(RegExpObject::last_index)
+{
+ auto regexp_object = regexp_object_from(vm, global_object);
+ if (!regexp_object)
+ return {};
+
+ return Value((unsigned)regexp_object->regex().start_offset);
+}
+
+JS_DEFINE_NATIVE_SETTER(RegExpObject::set_last_index)
+{
+ auto regexp_object = regexp_object_from(vm, global_object);
+ if (!regexp_object)
+ return;
+
+ auto index = value.to_i32(global_object);
+ if (vm.exception())
+ return;
+
+ if (index < 0)
+ index = 0;
+
+ regexp_object->regex().start_offset = index;
+}
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/RegExpObject.h b/Userland/Libraries/LibJS/Runtime/RegExpObject.h
new file mode 100644
index 0000000000..7be6b96069
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/RegExpObject.h
@@ -0,0 +1,66 @@
+/*
+ * 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/AST.h>
+#include <LibJS/Runtime/Object.h>
+#include <LibRegex/Regex.h>
+
+struct Flags {
+ regex::RegexOptions<ECMAScriptFlags> effective_flags;
+ regex::RegexOptions<ECMAScriptFlags> declared_flags;
+};
+
+namespace JS {
+
+class RegExpObject : public Object {
+ JS_OBJECT(RegExpObject, Object);
+
+public:
+ static RegExpObject* create(GlobalObject&, String pattern, String flags);
+
+ RegExpObject(String pattern, String flags, Object& prototype);
+ virtual void initialize(GlobalObject&) override;
+ virtual ~RegExpObject() override;
+
+ const String& pattern() const { return m_pattern; }
+ const String& flags() const { return m_flags; }
+ const regex::RegexOptions<ECMAScriptFlags>& declared_options() { return m_active_flags.declared_flags; }
+ const Regex<ECMA262>& regex() { return m_regex; }
+ const Regex<ECMA262>& regex() const { return m_regex; }
+
+private:
+ JS_DECLARE_NATIVE_GETTER(last_index);
+ JS_DECLARE_NATIVE_SETTER(set_last_index);
+
+ String m_pattern;
+ String m_flags;
+ Flags m_active_flags;
+ Regex<ECMA262> m_regex;
+};
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/RegExpPrototype.cpp b/Userland/Libraries/LibJS/Runtime/RegExpPrototype.cpp
new file mode 100644
index 0000000000..8540b7bc06
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/RegExpPrototype.cpp
@@ -0,0 +1,254 @@
+/*
+ * Copyright (c) 2020, Matthew Olsson <matthewcolsson@gmail.com>
+ * Copyright (c) 2020, Linus Groh <mail@linusgroh.de>
+ * 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 <AK/Function.h>
+#include <LibJS/Runtime/Array.h>
+#include <LibJS/Runtime/Error.h>
+#include <LibJS/Runtime/GlobalObject.h>
+#include <LibJS/Runtime/RegExpObject.h>
+#include <LibJS/Runtime/RegExpPrototype.h>
+#include <LibJS/Token.h>
+
+namespace JS {
+
+RegExpPrototype::RegExpPrototype(GlobalObject& global_object)
+ : RegExpObject({}, {}, *global_object.object_prototype())
+{
+}
+
+void RegExpPrototype::initialize(GlobalObject& global_object)
+{
+ auto& vm = this->vm();
+ Object::initialize(global_object);
+ u8 attr = Attribute::Writable | Attribute::Configurable;
+ define_native_function(vm.names.toString, to_string, 0, attr);
+ define_native_function(vm.names.test, test, 1, attr);
+ define_native_function(vm.names.exec, exec, 1, attr);
+
+ u8 readable_attr = Attribute::Configurable;
+ define_native_property(vm.names.flags, flags, {}, readable_attr);
+ define_native_property(vm.names.source, source, {}, readable_attr);
+
+#define __JS_ENUMERATE(flagName, flag_name, flag_char, ECMAScriptFlagName) \
+ define_native_property(vm.names.flagName, flag_name, {}, readable_attr);
+ JS_ENUMERATE_REGEXP_FLAGS
+#undef __JS_ENUMERATE
+}
+
+RegExpPrototype::~RegExpPrototype()
+{
+}
+
+static Object* this_object_from(VM& vm, GlobalObject& global_object)
+{
+ auto this_value = vm.this_value(global_object);
+ if (!this_value.is_object()) {
+ vm.throw_exception<TypeError>(global_object, ErrorType::NotAnObject, this_value.to_string_without_side_effects());
+ return {};
+ }
+ return &this_value.as_object();
+}
+
+static RegExpObject* regexp_object_from(VM& vm, GlobalObject& global_object)
+{
+ auto* this_object = vm.this_value(global_object).to_object(global_object);
+ if (!this_object)
+ return nullptr;
+ if (!is<RegExpObject>(this_object)) {
+ vm.throw_exception<TypeError>(global_object, ErrorType::NotA, "RegExp");
+ return nullptr;
+ }
+ return static_cast<RegExpObject*>(this_object);
+}
+
+static String escape_regexp_pattern(const RegExpObject& regexp_object)
+{
+ auto pattern = regexp_object.pattern();
+ if (pattern.is_empty())
+ return "(?:)";
+ // FIXME: Check u flag and escape accordingly
+ pattern.replace("\n", "\\n", true);
+ pattern.replace("\r", "\\r", true);
+ pattern.replace(LINE_SEPARATOR, "\\u2028", true);
+ pattern.replace(PARAGRAPH_SEPARATOR, "\\u2029", true);
+ pattern.replace("/", "\\/", true);
+ return pattern;
+}
+
+#define __JS_ENUMERATE(flagName, flag_name, flag_char, ECMAScriptFlagName) \
+ JS_DEFINE_NATIVE_GETTER(RegExpPrototype::flag_name) \
+ { \
+ auto regexp_object = regexp_object_from(vm, global_object); \
+ if (!regexp_object) \
+ return {}; \
+ \
+ return Value(regexp_object->declared_options().has_flag_set(ECMAScriptFlags::ECMAScriptFlagName)); \
+ }
+JS_ENUMERATE_REGEXP_FLAGS
+#undef __JS_ENUMERATE
+
+JS_DEFINE_NATIVE_GETTER(RegExpPrototype::flags)
+{
+ auto this_object = this_object_from(vm, global_object);
+ if (!this_object)
+ return {};
+
+ StringBuilder builder(8);
+
+#define __JS_ENUMERATE(flagName, flag_name, flag_char, ECMAScriptFlagName) \
+ auto flag_##flag_name = this_object->get(vm.names.flagName).value_or(js_undefined()); \
+ if (vm.exception()) \
+ return {}; \
+ if (flag_##flag_name.to_boolean()) \
+ builder.append(#flag_char);
+ JS_ENUMERATE_REGEXP_FLAGS
+#undef __JS_ENUMERATE
+
+ return js_string(vm, builder.to_string());
+}
+
+JS_DEFINE_NATIVE_GETTER(RegExpPrototype::source)
+{
+ auto this_object = this_object_from(vm, global_object);
+ if (!this_object)
+ return {};
+
+ // FIXME: This is obnoxious - we should have an easier way of looking up %RegExp.prototype%.
+ auto& regexp_prototype = global_object.get(vm.names.RegExp).as_object().get(vm.names.prototype).as_object();
+ if (this_object == &regexp_prototype)
+ return js_string(vm, "(?:)");
+
+ auto regexp_object = regexp_object_from(vm, global_object);
+ if (!regexp_object)
+ return {};
+
+ return js_string(vm, escape_regexp_pattern(*regexp_object));
+}
+
+RegexResult RegExpPrototype::do_match(const Regex<ECMA262>& re, const StringView& subject)
+{
+ auto result = re.match(subject);
+ // The 'lastIndex' property is reset on failing tests (if 'global')
+ if (!result.success && re.options().has_flag_set(ECMAScriptFlags::Global))
+ re.start_offset = 0;
+
+ return result;
+}
+
+JS_DEFINE_NATIVE_FUNCTION(RegExpPrototype::exec)
+{
+ // FIXME: This should try using dynamic properties for 'lastIndex',
+ // and internal slots [[RegExpMatcher]], [[OriginalFlags]], etc.
+ auto regexp_object = regexp_object_from(vm, global_object);
+ if (!regexp_object)
+ return {};
+
+ auto str = vm.argument(0).to_string(global_object);
+ if (vm.exception())
+ return {};
+
+ StringView str_to_match = str;
+
+ // RegExps without "global" and "sticky" always start at offset 0.
+ if (!regexp_object->regex().options().has_flag_set((ECMAScriptFlags)regex::AllFlags::Internal_Stateful))
+ regexp_object->regex().start_offset = 0;
+
+ auto result = do_match(regexp_object->regex(), str_to_match);
+ if (!result.success)
+ return js_null();
+
+ auto& match = result.matches[0];
+
+ // FIXME: Do code point index correction if the Unicode flag is set.
+ auto* array = Array::create(global_object);
+ array->indexed_properties().set_array_like_size(result.n_capture_groups + 1);
+ array->define_property(vm.names.index, Value((i32)match.column));
+ array->define_property(vm.names.input, js_string(vm, str));
+ array->indexed_properties().put(array, 0, js_string(vm, match.view.to_string()));
+
+ for (size_t i = 0; i < result.n_capture_groups; ++i) {
+ auto& capture = result.capture_group_matches[0][i];
+ array->indexed_properties().put(array, i + 1, js_string(vm, capture.view.to_string()));
+ }
+
+ Value groups = js_undefined();
+ if (result.n_named_capture_groups > 0) {
+ auto groups_object = create_empty(global_object);
+ for (auto& entry : result.named_capture_group_matches[0])
+ groups_object->define_property(entry.key, js_string(vm, entry.value.view.to_string()));
+ groups = move(groups_object);
+ }
+
+ array->define_property(vm.names.groups, groups);
+
+ return array;
+}
+
+JS_DEFINE_NATIVE_FUNCTION(RegExpPrototype::test)
+{
+ // FIXME: This should try using dynamic properties for 'exec' first,
+ // before falling back to builtin_exec.
+ auto regexp_object = regexp_object_from(vm, global_object);
+ if (!regexp_object)
+ return {};
+
+ auto str = vm.argument(0).to_string(global_object);
+ if (vm.exception())
+ return {};
+
+ // RegExps without "global" and "sticky" always start at offset 0.
+ if (!regexp_object->regex().options().has_flag_set((ECMAScriptFlags)regex::AllFlags::Internal_Stateful))
+ regexp_object->regex().start_offset = 0;
+
+ auto result = do_match(regexp_object->regex(), str);
+ return Value(result.success);
+}
+
+JS_DEFINE_NATIVE_FUNCTION(RegExpPrototype::to_string)
+{
+ auto this_object = this_object_from(vm, global_object);
+ if (!this_object)
+ return {};
+
+ auto source_attr = this_object->get(vm.names.source).value_or(js_undefined());
+ if (vm.exception())
+ return {};
+ auto pattern = source_attr.to_string(global_object);
+ if (vm.exception())
+ return {};
+
+ auto flags_attr = this_object->get(vm.names.flags).value_or(js_undefined());
+ if (vm.exception())
+ return {};
+ auto flags = flags_attr.to_string(global_object);
+ if (vm.exception())
+ return {};
+
+ return js_string(vm, String::formatted("/{}/{}", pattern, flags));
+}
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/RegExpPrototype.h b/Userland/Libraries/LibJS/Runtime/RegExpPrototype.h
new file mode 100644
index 0000000000..69b0708ce1
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/RegExpPrototype.h
@@ -0,0 +1,57 @@
+/*
+ * 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/RegExpObject.h>
+
+namespace JS {
+
+class RegExpPrototype final : public RegExpObject {
+ JS_OBJECT(RegExpPrototype, RegExpObject);
+
+public:
+ explicit RegExpPrototype(GlobalObject&);
+ virtual void initialize(GlobalObject&) override;
+ virtual ~RegExpPrototype() override;
+
+private:
+ static RegexResult do_match(const Regex<ECMA262>&, const StringView&);
+
+ JS_DECLARE_NATIVE_GETTER(flags);
+ JS_DECLARE_NATIVE_GETTER(source);
+
+ JS_DECLARE_NATIVE_FUNCTION(exec);
+ JS_DECLARE_NATIVE_FUNCTION(test);
+ JS_DECLARE_NATIVE_FUNCTION(to_string);
+
+#define __JS_ENUMERATE(_, flag_name, ...) \
+ JS_DECLARE_NATIVE_GETTER(flag_name);
+ JS_ENUMERATE_REGEXP_FLAGS
+#undef __JS_ENUMERATE
+};
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/ScopeObject.cpp b/Userland/Libraries/LibJS/Runtime/ScopeObject.cpp
new file mode 100644
index 0000000000..5a67011a55
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/ScopeObject.cpp
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * 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/ScopeObject.h>
+#include <LibJS/Runtime/VM.h>
+
+namespace JS {
+
+ScopeObject::ScopeObject(ScopeObject* parent)
+ : Object(vm().scope_object_shape())
+ , m_parent(parent)
+{
+}
+
+ScopeObject::ScopeObject(GlobalObjectTag tag)
+ : Object(tag)
+{
+}
+
+void ScopeObject::visit_edges(Visitor& visitor)
+{
+ Base::visit_edges(visitor);
+ visitor.visit(m_parent);
+}
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/ScopeObject.h b/Userland/Libraries/LibJS/Runtime/ScopeObject.h
new file mode 100644
index 0000000000..ac29c11503
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/ScopeObject.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * 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 {
+
+struct Variable {
+ Value value;
+ DeclarationKind declaration_kind;
+};
+
+class ScopeObject : public Object {
+ JS_OBJECT(ScopeObject, Object);
+
+public:
+ virtual Optional<Variable> get_from_scope(const FlyString&) const = 0;
+ virtual void put_to_scope(const FlyString&, Variable) = 0;
+ virtual bool has_this_binding() const = 0;
+ virtual Value get_this_binding(GlobalObject&) const = 0;
+
+ ScopeObject* parent() { return m_parent; }
+ const ScopeObject* parent() const { return m_parent; }
+
+protected:
+ explicit ScopeObject(ScopeObject* parent);
+ explicit ScopeObject(GlobalObjectTag);
+
+ virtual void visit_edges(Visitor&) override;
+
+private:
+ ScopeObject* m_parent { nullptr };
+};
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/ScriptFunction.cpp b/Userland/Libraries/LibJS/Runtime/ScriptFunction.cpp
new file mode 100644
index 0000000000..56ce94edd2
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/ScriptFunction.cpp
@@ -0,0 +1,187 @@
+/*
+ * Copyright (c) 2020, Stephan Unverwerth <s.unverwerth@gmx.de>
+ * 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 <AK/Function.h>
+#include <LibJS/AST.h>
+#include <LibJS/Interpreter.h>
+#include <LibJS/Runtime/Array.h>
+#include <LibJS/Runtime/Error.h>
+#include <LibJS/Runtime/GlobalObject.h>
+#include <LibJS/Runtime/ScriptFunction.h>
+#include <LibJS/Runtime/Value.h>
+
+namespace JS {
+
+static ScriptFunction* typed_this(VM& vm, GlobalObject& global_object)
+{
+ auto* this_object = vm.this_value(global_object).to_object(global_object);
+ if (!this_object)
+ return nullptr;
+ if (!this_object->is_function()) {
+ vm.throw_exception<TypeError>(global_object, ErrorType::NotAFunctionNoParam);
+ return nullptr;
+ }
+ return static_cast<ScriptFunction*>(this_object);
+}
+
+ScriptFunction* ScriptFunction::create(GlobalObject& global_object, const FlyString& name, const Statement& body, Vector<FunctionNode::Parameter> parameters, i32 m_function_length, ScopeObject* parent_scope, bool is_strict, bool is_arrow_function)
+{
+ return global_object.heap().allocate<ScriptFunction>(global_object, global_object, name, body, move(parameters), m_function_length, parent_scope, *global_object.function_prototype(), is_strict, is_arrow_function);
+}
+
+ScriptFunction::ScriptFunction(GlobalObject& global_object, const FlyString& name, const Statement& body, Vector<FunctionNode::Parameter> parameters, i32 m_function_length, ScopeObject* parent_scope, Object& prototype, bool is_strict, bool is_arrow_function)
+ : Function(prototype, is_arrow_function ? vm().this_value(global_object) : Value(), {})
+ , m_name(name)
+ , m_body(body)
+ , m_parameters(move(parameters))
+ , m_parent_scope(parent_scope)
+ , m_function_length(m_function_length)
+ , m_is_strict(is_strict)
+ , m_is_arrow_function(is_arrow_function)
+{
+}
+
+void ScriptFunction::initialize(GlobalObject& global_object)
+{
+ auto& vm = this->vm();
+ Function::initialize(global_object);
+ if (!m_is_arrow_function) {
+ Object* prototype = vm.heap().allocate<Object>(global_object, *global_object.new_script_function_prototype_object_shape());
+ prototype->define_property(vm.names.constructor, this, Attribute::Writable | Attribute::Configurable);
+ define_property(vm.names.prototype, prototype, Attribute::Writable);
+ }
+ define_native_property(vm.names.length, length_getter, {}, Attribute::Configurable);
+ define_native_property(vm.names.name, name_getter, {}, Attribute::Configurable);
+}
+
+ScriptFunction::~ScriptFunction()
+{
+}
+
+void ScriptFunction::visit_edges(Visitor& visitor)
+{
+ Function::visit_edges(visitor);
+ visitor.visit(m_parent_scope);
+}
+
+LexicalEnvironment* ScriptFunction::create_environment()
+{
+ HashMap<FlyString, Variable> variables;
+ for (auto& parameter : m_parameters) {
+ variables.set(parameter.name, { js_undefined(), DeclarationKind::Var });
+ }
+
+ if (is<ScopeNode>(body())) {
+ for (auto& declaration : static_cast<const ScopeNode&>(body()).variables()) {
+ for (auto& declarator : declaration.declarations()) {
+ variables.set(declarator.id().string(), { js_undefined(), DeclarationKind::Var });
+ }
+ }
+ }
+
+ auto* environment = heap().allocate<LexicalEnvironment>(global_object(), move(variables), m_parent_scope, LexicalEnvironment::EnvironmentRecordType::Function);
+ environment->set_home_object(home_object());
+ environment->set_current_function(*this);
+ if (m_is_arrow_function) {
+ if (is<LexicalEnvironment>(m_parent_scope))
+ environment->set_new_target(static_cast<LexicalEnvironment*>(m_parent_scope)->new_target());
+ }
+ return environment;
+}
+
+Value ScriptFunction::execute_function_body()
+{
+ auto& vm = this->vm();
+
+ OwnPtr<Interpreter> local_interpreter;
+ Interpreter* interpreter = vm.interpreter_if_exists();
+
+ if (!interpreter) {
+ local_interpreter = Interpreter::create_with_existing_global_object(global_object());
+ interpreter = local_interpreter.ptr();
+ }
+
+ VM::InterpreterExecutionScope scope(*interpreter);
+
+ auto& call_frame_args = vm.call_frame().arguments;
+ for (size_t i = 0; i < m_parameters.size(); ++i) {
+ auto parameter = m_parameters[i];
+ Value argument_value;
+ if (parameter.is_rest) {
+ auto* array = Array::create(global_object());
+ for (size_t rest_index = i; rest_index < call_frame_args.size(); ++rest_index)
+ array->indexed_properties().append(call_frame_args[rest_index]);
+ argument_value = move(array);
+ } else if (i < call_frame_args.size() && !call_frame_args[i].is_undefined()) {
+ argument_value = call_frame_args[i];
+ } else if (parameter.default_value) {
+ argument_value = parameter.default_value->execute(*interpreter, global_object());
+ if (vm.exception())
+ return {};
+ } else {
+ argument_value = js_undefined();
+ }
+ vm.current_scope()->put_to_scope(parameter.name, { argument_value, DeclarationKind::Var });
+ }
+
+ return interpreter->execute_statement(global_object(), m_body, ScopeType::Function);
+}
+
+Value ScriptFunction::call()
+{
+ if (m_is_class_constructor) {
+ vm().throw_exception<TypeError>(global_object(), ErrorType::ClassConstructorWithoutNew, m_name);
+ return {};
+ }
+ return execute_function_body();
+}
+
+Value ScriptFunction::construct(Function&)
+{
+ if (m_is_arrow_function) {
+ vm().throw_exception<TypeError>(global_object(), ErrorType::NotAConstructor, m_name);
+ return {};
+ }
+ return execute_function_body();
+}
+
+JS_DEFINE_NATIVE_GETTER(ScriptFunction::length_getter)
+{
+ auto* function = typed_this(vm, global_object);
+ if (!function)
+ return {};
+ return Value(static_cast<i32>(function->m_function_length));
+}
+
+JS_DEFINE_NATIVE_GETTER(ScriptFunction::name_getter)
+{
+ auto* function = typed_this(vm, global_object);
+ if (!function)
+ return {};
+ return js_string(vm, function->name().is_null() ? "" : function->name());
+}
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/ScriptFunction.h b/Userland/Libraries/LibJS/Runtime/ScriptFunction.h
new file mode 100644
index 0000000000..e3765509ab
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/ScriptFunction.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * 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/AST.h>
+#include <LibJS/Runtime/Function.h>
+
+namespace JS {
+
+class ScriptFunction final : public Function {
+ JS_OBJECT(ScriptFunction, Function);
+
+public:
+ static ScriptFunction* create(GlobalObject&, const FlyString& name, const Statement& body, Vector<FunctionNode::Parameter> parameters, i32 m_function_length, ScopeObject* parent_scope, bool is_strict, bool is_arrow_function = false);
+
+ ScriptFunction(GlobalObject&, const FlyString& name, const Statement& body, Vector<FunctionNode::Parameter> parameters, i32 m_function_length, ScopeObject* parent_scope, Object& prototype, bool is_strict, bool is_arrow_function = false);
+ virtual void initialize(GlobalObject&) override;
+ virtual ~ScriptFunction();
+
+ const Statement& body() const { return m_body; }
+ const Vector<FunctionNode::Parameter>& parameters() const { return m_parameters; };
+
+ virtual Value call() override;
+ virtual Value construct(Function& new_target) override;
+
+ virtual const FlyString& name() const override { return m_name; };
+ void set_name(const FlyString& name) { m_name = name; };
+
+ void set_is_class_constructor() { m_is_class_constructor = true; };
+
+protected:
+ virtual bool is_strict_mode() const final { return m_is_strict; }
+
+private:
+ virtual LexicalEnvironment* create_environment() override;
+ virtual void visit_edges(Visitor&) override;
+
+ Value execute_function_body();
+
+ JS_DECLARE_NATIVE_GETTER(length_getter);
+ JS_DECLARE_NATIVE_GETTER(name_getter);
+
+ FlyString m_name;
+ NonnullRefPtr<Statement> m_body;
+ const Vector<FunctionNode::Parameter> m_parameters;
+ ScopeObject* m_parent_scope { nullptr };
+ i32 m_function_length { 0 };
+ bool m_is_strict { false };
+ bool m_is_arrow_function { false };
+ bool m_is_class_constructor { false };
+};
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/Shape.cpp b/Userland/Libraries/LibJS/Runtime/Shape.cpp
new file mode 100644
index 0000000000..1f74b14471
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/Shape.cpp
@@ -0,0 +1,227 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * 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/Heap/DeferGC.h>
+#include <LibJS/Runtime/GlobalObject.h>
+#include <LibJS/Runtime/Shape.h>
+
+namespace JS {
+
+Shape* Shape::create_unique_clone() const
+{
+ ASSERT(m_global_object);
+ auto* new_shape = heap().allocate_without_global_object<Shape>(*m_global_object);
+ new_shape->m_unique = true;
+ new_shape->m_prototype = m_prototype;
+ ensure_property_table();
+ new_shape->ensure_property_table();
+ (*new_shape->m_property_table) = *m_property_table;
+ new_shape->m_property_count = new_shape->m_property_table->size();
+ return new_shape;
+}
+
+Shape* Shape::create_put_transition(const StringOrSymbol& property_name, PropertyAttributes attributes)
+{
+ TransitionKey key { property_name, attributes };
+ if (auto* existing_shape = m_forward_transitions.get(key).value_or(nullptr))
+ return existing_shape;
+ auto* new_shape = heap().allocate_without_global_object<Shape>(*this, property_name, attributes, TransitionType::Put);
+ m_forward_transitions.set(key, new_shape);
+ return new_shape;
+}
+
+Shape* Shape::create_configure_transition(const StringOrSymbol& property_name, PropertyAttributes attributes)
+{
+ TransitionKey key { property_name, attributes };
+ if (auto* existing_shape = m_forward_transitions.get(key).value_or(nullptr))
+ return existing_shape;
+ auto* new_shape = heap().allocate_without_global_object<Shape>(*this, property_name, attributes, TransitionType::Configure);
+ m_forward_transitions.set(key, new_shape);
+ return new_shape;
+}
+
+Shape* Shape::create_prototype_transition(Object* new_prototype)
+{
+ return heap().allocate_without_global_object<Shape>(*this, new_prototype);
+}
+
+Shape::Shape(ShapeWithoutGlobalObjectTag)
+{
+}
+
+Shape::Shape(Object& global_object)
+ : m_global_object(&global_object)
+{
+}
+
+Shape::Shape(Shape& previous_shape, const StringOrSymbol& property_name, PropertyAttributes attributes, TransitionType transition_type)
+ : m_attributes(attributes)
+ , m_transition_type(transition_type)
+ , m_global_object(previous_shape.m_global_object)
+ , m_previous(&previous_shape)
+ , m_property_name(property_name)
+ , m_prototype(previous_shape.m_prototype)
+ , m_property_count(transition_type == TransitionType::Put ? previous_shape.m_property_count + 1 : previous_shape.m_property_count)
+{
+}
+
+Shape::Shape(Shape& previous_shape, Object* new_prototype)
+ : m_transition_type(TransitionType::Prototype)
+ , m_global_object(previous_shape.m_global_object)
+ , m_previous(&previous_shape)
+ , m_prototype(new_prototype)
+ , m_property_count(previous_shape.m_property_count)
+{
+}
+
+Shape::~Shape()
+{
+}
+
+void Shape::visit_edges(Cell::Visitor& visitor)
+{
+ Cell::visit_edges(visitor);
+ visitor.visit(m_global_object);
+ visitor.visit(m_prototype);
+ visitor.visit(m_previous);
+ m_property_name.visit_edges(visitor);
+ for (auto& it : m_forward_transitions)
+ visitor.visit(it.value);
+
+ if (m_property_table) {
+ for (auto& it : *m_property_table)
+ it.key.visit_edges(visitor);
+ }
+}
+
+Optional<PropertyMetadata> Shape::lookup(const StringOrSymbol& property_name) const
+{
+ if (m_property_count == 0)
+ return {};
+ auto property = property_table().get(property_name);
+ if (!property.has_value())
+ return {};
+ return property;
+}
+
+const HashMap<StringOrSymbol, PropertyMetadata>& Shape::property_table() const
+{
+ ensure_property_table();
+ return *m_property_table;
+}
+
+size_t Shape::property_count() const
+{
+ return m_property_count;
+}
+
+Vector<Shape::Property> Shape::property_table_ordered() const
+{
+ auto vec = Vector<Shape::Property>();
+ vec.resize(property_count());
+
+ for (auto& it : property_table()) {
+ vec[it.value.offset] = { it.key, it.value };
+ }
+
+ return vec;
+}
+
+void Shape::ensure_property_table() const
+{
+ if (m_property_table)
+ return;
+ m_property_table = make<HashMap<StringOrSymbol, PropertyMetadata>>();
+
+ u32 next_offset = 0;
+
+ Vector<const Shape*, 64> transition_chain;
+ for (auto* shape = m_previous; shape; shape = shape->m_previous) {
+ if (shape->m_property_table) {
+ *m_property_table = *shape->m_property_table;
+ next_offset = shape->m_property_count;
+ break;
+ }
+ transition_chain.append(shape);
+ }
+ transition_chain.append(this);
+
+ for (ssize_t i = transition_chain.size() - 1; i >= 0; --i) {
+ auto* shape = transition_chain[i];
+ if (!shape->m_property_name.is_valid()) {
+ // Ignore prototype transitions as they don't affect the key map.
+ continue;
+ }
+ if (shape->m_transition_type == TransitionType::Put) {
+ m_property_table->set(shape->m_property_name, { next_offset++, shape->m_attributes });
+ } else if (shape->m_transition_type == TransitionType::Configure) {
+ auto it = m_property_table->find(shape->m_property_name);
+ ASSERT(it != m_property_table->end());
+ it->value.attributes = shape->m_attributes;
+ }
+ }
+}
+
+void Shape::add_property_to_unique_shape(const StringOrSymbol& property_name, PropertyAttributes attributes)
+{
+ ASSERT(is_unique());
+ ASSERT(m_property_table);
+ ASSERT(!m_property_table->contains(property_name));
+ m_property_table->set(property_name, { m_property_table->size(), attributes });
+ ++m_property_count;
+}
+
+void Shape::reconfigure_property_in_unique_shape(const StringOrSymbol& property_name, PropertyAttributes attributes)
+{
+ ASSERT(is_unique());
+ ASSERT(m_property_table);
+ auto it = m_property_table->find(property_name);
+ ASSERT(it != m_property_table->end());
+ it->value.attributes = attributes;
+ m_property_table->set(property_name, it->value);
+}
+
+void Shape::remove_property_from_unique_shape(const StringOrSymbol& property_name, size_t offset)
+{
+ ASSERT(is_unique());
+ ASSERT(m_property_table);
+ if (m_property_table->remove(property_name))
+ --m_property_count;
+ for (auto& it : *m_property_table) {
+ ASSERT(it.value.offset != offset);
+ if (it.value.offset > offset)
+ --it.value.offset;
+ }
+}
+
+void Shape::add_property_without_transition(const StringOrSymbol& property_name, PropertyAttributes attributes)
+{
+ ensure_property_table();
+ if (m_property_table->set(property_name, { m_property_count, attributes }) == AK::HashSetResult::InsertedNewEntry)
+ ++m_property_count;
+}
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/Shape.h b/Userland/Libraries/LibJS/Runtime/Shape.h
new file mode 100644
index 0000000000..1d2bd51037
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/Shape.h
@@ -0,0 +1,132 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * 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 <AK/HashMap.h>
+#include <AK/OwnPtr.h>
+#include <LibJS/Forward.h>
+#include <LibJS/Runtime/Cell.h>
+#include <LibJS/Runtime/PropertyAttributes.h>
+#include <LibJS/Runtime/StringOrSymbol.h>
+#include <LibJS/Runtime/Value.h>
+
+namespace JS {
+
+struct PropertyMetadata {
+ size_t offset { 0 };
+ PropertyAttributes attributes { 0 };
+};
+
+struct TransitionKey {
+ StringOrSymbol property_name;
+ PropertyAttributes attributes { 0 };
+
+ bool operator==(const TransitionKey& other) const
+ {
+ return property_name == other.property_name && attributes == other.attributes;
+ }
+};
+
+class Shape final : public Cell {
+public:
+ virtual ~Shape() override;
+
+ enum class TransitionType {
+ Invalid,
+ Put,
+ Configure,
+ Prototype,
+ };
+
+ enum class ShapeWithoutGlobalObjectTag { Tag };
+
+ explicit Shape(ShapeWithoutGlobalObjectTag);
+ explicit Shape(Object& global_object);
+ Shape(Shape& previous_shape, const StringOrSymbol& property_name, PropertyAttributes attributes, TransitionType);
+ Shape(Shape& previous_shape, Object* new_prototype);
+
+ Shape* create_put_transition(const StringOrSymbol&, PropertyAttributes attributes);
+ Shape* create_configure_transition(const StringOrSymbol&, PropertyAttributes attributes);
+ Shape* create_prototype_transition(Object* new_prototype);
+
+ void add_property_without_transition(const StringOrSymbol&, PropertyAttributes);
+
+ bool is_unique() const { return m_unique; }
+ Shape* create_unique_clone() const;
+
+ GlobalObject* global_object() const;
+
+ Object* prototype() { return m_prototype; }
+ const Object* prototype() const { return m_prototype; }
+
+ Optional<PropertyMetadata> lookup(const StringOrSymbol&) const;
+ const HashMap<StringOrSymbol, PropertyMetadata>& property_table() const;
+ size_t property_count() const;
+
+ struct Property {
+ StringOrSymbol key;
+ PropertyMetadata value;
+ };
+
+ Vector<Property> property_table_ordered() const;
+
+ void set_prototype_without_transition(Object* new_prototype) { m_prototype = new_prototype; }
+
+ void remove_property_from_unique_shape(const StringOrSymbol&, size_t offset);
+ void add_property_to_unique_shape(const StringOrSymbol&, PropertyAttributes attributes);
+ void reconfigure_property_in_unique_shape(const StringOrSymbol& property_name, PropertyAttributes attributes);
+
+private:
+ virtual const char* class_name() const override { return "Shape"; }
+ virtual void visit_edges(Visitor&) override;
+
+ void ensure_property_table() const;
+
+ PropertyAttributes m_attributes { 0 };
+ TransitionType m_transition_type : 6 { TransitionType::Invalid };
+ bool m_unique : 1 { false };
+
+ Object* m_global_object { nullptr };
+
+ mutable OwnPtr<HashMap<StringOrSymbol, PropertyMetadata>> m_property_table;
+
+ HashMap<TransitionKey, Shape*> m_forward_transitions;
+ Shape* m_previous { nullptr };
+ StringOrSymbol m_property_name;
+ Object* m_prototype { nullptr };
+ size_t m_property_count { 0 };
+};
+
+}
+
+template<>
+struct AK::Traits<JS::TransitionKey> : public GenericTraits<JS::TransitionKey> {
+ static unsigned hash(const JS::TransitionKey& key)
+ {
+ return pair_int_hash(key.attributes.bits(), Traits<JS::StringOrSymbol>::hash(key.property_name));
+ }
+};
diff --git a/Userland/Libraries/LibJS/Runtime/StringConstructor.cpp b/Userland/Libraries/LibJS/Runtime/StringConstructor.cpp
new file mode 100644
index 0000000000..50872f2387
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/StringConstructor.cpp
@@ -0,0 +1,135 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * 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 <AK/StringBuilder.h>
+#include <AK/Utf32View.h>
+#include <LibJS/Runtime/Array.h>
+#include <LibJS/Runtime/Error.h>
+#include <LibJS/Runtime/GlobalObject.h>
+#include <LibJS/Runtime/StringConstructor.h>
+#include <LibJS/Runtime/StringObject.h>
+
+namespace JS {
+
+StringConstructor::StringConstructor(GlobalObject& global_object)
+ : NativeFunction(vm().names.String, *global_object.function_prototype())
+{
+}
+
+void StringConstructor::initialize(GlobalObject& global_object)
+{
+ auto& vm = this->vm();
+ NativeFunction::initialize(global_object);
+ define_property(vm.names.prototype, global_object.string_prototype(), 0);
+ define_property(vm.names.length, Value(1), Attribute::Configurable);
+
+ u8 attr = Attribute::Writable | Attribute::Configurable;
+ define_native_function(vm.names.raw, raw, 1, attr);
+ define_native_function(vm.names.fromCharCode, from_char_code, 1, attr);
+}
+
+StringConstructor::~StringConstructor()
+{
+}
+
+Value StringConstructor::call()
+{
+ if (!vm().argument_count())
+ return js_string(heap(), "");
+ if (vm().argument(0).is_symbol())
+ return js_string(heap(), vm().argument(0).as_symbol().to_string());
+ auto* string = vm().argument(0).to_primitive_string(global_object());
+ if (vm().exception())
+ return {};
+ return string;
+}
+
+Value StringConstructor::construct(Function&)
+{
+ PrimitiveString* primitive_string = nullptr;
+ if (!vm().argument_count())
+ primitive_string = js_string(vm(), "");
+ else
+ primitive_string = vm().argument(0).to_primitive_string(global_object());
+ if (!primitive_string)
+ return {};
+ return StringObject::create(global_object(), *primitive_string);
+}
+
+JS_DEFINE_NATIVE_FUNCTION(StringConstructor::raw)
+{
+ auto* template_object = vm.argument(0).to_object(global_object);
+ if (vm.exception())
+ return {};
+
+ auto raw = template_object->get(vm.names.raw);
+ if (vm.exception())
+ return {};
+ if (raw.is_empty() || raw.is_nullish()) {
+ vm.throw_exception<TypeError>(global_object, ErrorType::StringRawCannotConvert, raw.is_null() ? "null" : "undefined");
+ return {};
+ }
+ if (!raw.is_array())
+ return js_string(vm, "");
+
+ auto* array = static_cast<Array*>(raw.to_object(global_object));
+ auto& raw_array_elements = array->indexed_properties();
+ StringBuilder builder;
+
+ for (size_t i = 0; i < raw_array_elements.array_like_size(); ++i) {
+ auto result = raw_array_elements.get(array, i);
+ if (vm.exception())
+ return {};
+ if (!result.has_value())
+ continue;
+ builder.append(result.value().value.to_string(global_object));
+ if (vm.exception())
+ return {};
+ if (i + 1 < vm.argument_count() && i < raw_array_elements.array_like_size() - 1) {
+ builder.append(vm.argument(i + 1).to_string(global_object));
+ if (vm.exception())
+ return {};
+ }
+ }
+
+ return js_string(vm, builder.build());
+}
+
+JS_DEFINE_NATIVE_FUNCTION(StringConstructor::from_char_code)
+{
+ StringBuilder builder;
+ for (size_t i = 0; i < vm.argument_count(); ++i) {
+ auto char_code = vm.argument(i).to_i32(global_object);
+ if (vm.exception())
+ return {};
+ auto truncated = char_code & 0xffff;
+ // FIXME: We need an Utf16View :^)
+ builder.append(Utf32View((u32*)&truncated, 1));
+ }
+ return js_string(vm, builder.build());
+}
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/StringConstructor.h b/Userland/Libraries/LibJS/Runtime/StringConstructor.h
new file mode 100644
index 0000000000..239d9e7596
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/StringConstructor.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * 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/NativeFunction.h>
+
+namespace JS {
+
+class StringConstructor final : public NativeFunction {
+ JS_OBJECT(StringConstructor, NativeFunction);
+
+public:
+ explicit StringConstructor(GlobalObject&);
+ virtual void initialize(GlobalObject&) override;
+ virtual ~StringConstructor() override;
+
+ virtual Value call() override;
+ virtual Value construct(Function& new_target) override;
+
+private:
+ virtual bool has_constructor() const override { return true; }
+
+ JS_DECLARE_NATIVE_FUNCTION(raw);
+ JS_DECLARE_NATIVE_FUNCTION(from_char_code);
+};
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/StringIterator.cpp b/Userland/Libraries/LibJS/Runtime/StringIterator.cpp
new file mode 100644
index 0000000000..4c0b9a3355
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/StringIterator.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 <AK/Utf8View.h>
+#include <LibJS/Runtime/GlobalObject.h>
+#include <LibJS/Runtime/StringIterator.h>
+
+namespace JS {
+
+StringIterator* StringIterator::create(GlobalObject& global_object, String string)
+{
+ return global_object.heap().allocate<StringIterator>(global_object, *global_object.string_iterator_prototype(), move(string));
+}
+
+StringIterator::StringIterator(Object& prototype, String string)
+ : Object(prototype)
+ , m_string(move(string))
+ , m_iterator(Utf8View(m_string).begin())
+{
+}
+
+StringIterator::~StringIterator()
+{
+}
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/StringIterator.h b/Userland/Libraries/LibJS/Runtime/StringIterator.h
new file mode 100644
index 0000000000..c5754cf147
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/StringIterator.h
@@ -0,0 +1,54 @@
+/*
+ * 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 <AK/Utf8View.h>
+#include <LibJS/Runtime/Object.h>
+
+namespace JS {
+
+class StringIterator final : public Object {
+ JS_OBJECT(StringIterator, Object);
+
+public:
+ static StringIterator* create(GlobalObject&, String string);
+
+ explicit StringIterator(Object& prototype, String string);
+ virtual ~StringIterator() override;
+
+ Utf8CodepointIterator& iterator() { return m_iterator; }
+ bool done() const { return m_done; }
+
+private:
+ friend class StringIteratorPrototype;
+
+ String m_string;
+ Utf8CodepointIterator m_iterator;
+ bool m_done { false };
+};
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/StringIteratorPrototype.cpp b/Userland/Libraries/LibJS/Runtime/StringIteratorPrototype.cpp
new file mode 100644
index 0000000000..2259ae7b26
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/StringIteratorPrototype.cpp
@@ -0,0 +1,80 @@
+/*
+ * 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 <AK/StringBuilder.h>
+#include <LibJS/Runtime/Error.h>
+#include <LibJS/Runtime/GlobalObject.h>
+#include <LibJS/Runtime/IteratorOperations.h>
+#include <LibJS/Runtime/StringIterator.h>
+#include <LibJS/Runtime/StringIteratorPrototype.h>
+
+namespace JS {
+
+StringIteratorPrototype::StringIteratorPrototype(GlobalObject& global_object)
+ : Object(*global_object.iterator_prototype())
+{
+}
+
+void StringIteratorPrototype::initialize(GlobalObject& global_object)
+{
+ auto& vm = this->vm();
+ Object::initialize(global_object);
+ define_native_function(vm.names.next, next, 0, Attribute::Configurable | Attribute::Writable);
+ define_property(vm.well_known_symbol_to_string_tag(), js_string(global_object.heap(), "String Iterator"), Attribute::Configurable);
+}
+
+StringIteratorPrototype::~StringIteratorPrototype()
+{
+}
+
+JS_DEFINE_NATIVE_FUNCTION(StringIteratorPrototype::next)
+{
+ auto this_value = vm.this_value(global_object);
+ if (!this_value.is_object() || !is<StringIterator>(this_value.as_object())) {
+ vm.throw_exception<TypeError>(global_object, ErrorType::NotA, "String Iterator");
+ return {};
+ }
+
+ auto& this_object = this_value.as_object();
+ auto& iterator = static_cast<StringIterator&>(this_object);
+ if (iterator.done())
+ return create_iterator_result_object(global_object, js_undefined(), true);
+
+ auto& utf8_iterator = iterator.iterator();
+
+ if (utf8_iterator.done()) {
+ iterator.m_done = true;
+ return create_iterator_result_object(global_object, js_undefined(), true);
+ }
+
+ StringBuilder builder;
+ builder.append_code_point(*utf8_iterator);
+ ++utf8_iterator;
+
+ return create_iterator_result_object(global_object, js_string(vm, builder.to_string()), false);
+}
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/StringIteratorPrototype.h b/Userland/Libraries/LibJS/Runtime/StringIteratorPrototype.h
new file mode 100644
index 0000000000..a17e2507eb
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/StringIteratorPrototype.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 StringIteratorPrototype final : public Object {
+ JS_OBJECT(StringIteratorPrototype, Object)
+
+public:
+ StringIteratorPrototype(GlobalObject&);
+ virtual void initialize(GlobalObject&) override;
+ virtual ~StringIteratorPrototype() override;
+
+private:
+ JS_DECLARE_NATIVE_FUNCTION(next);
+};
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/StringObject.cpp b/Userland/Libraries/LibJS/Runtime/StringObject.cpp
new file mode 100644
index 0000000000..cfe1fd6fd8
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/StringObject.cpp
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * 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/Heap/Heap.h>
+#include <LibJS/Runtime/GlobalObject.h>
+#include <LibJS/Runtime/PrimitiveString.h>
+#include <LibJS/Runtime/StringObject.h>
+#include <LibJS/Runtime/StringPrototype.h>
+#include <LibJS/Runtime/Value.h>
+
+namespace JS {
+
+StringObject* StringObject::create(GlobalObject& global_object, PrimitiveString& primitive_string)
+{
+ return global_object.heap().allocate<StringObject>(global_object, primitive_string, *global_object.string_prototype());
+}
+
+StringObject::StringObject(PrimitiveString& string, Object& prototype)
+ : Object(prototype)
+ , m_string(string)
+{
+}
+
+StringObject::~StringObject()
+{
+}
+
+void StringObject::visit_edges(Cell::Visitor& visitor)
+{
+ Object::visit_edges(visitor);
+ visitor.visit(&m_string);
+}
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/StringObject.h b/Userland/Libraries/LibJS/Runtime/StringObject.h
new file mode 100644
index 0000000000..7fb32ebc63
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/StringObject.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * 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 StringObject : public Object {
+ JS_OBJECT(StringObject, Object);
+
+public:
+ static StringObject* create(GlobalObject&, PrimitiveString&);
+
+ StringObject(PrimitiveString&, Object& prototype);
+ virtual ~StringObject() override;
+
+ const PrimitiveString& primitive_string() const { return m_string; }
+ virtual Value value_of() const override
+ {
+ return Value(&m_string);
+ }
+
+private:
+ virtual void visit_edges(Visitor&) override;
+
+ PrimitiveString& m_string;
+};
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/StringOrSymbol.h b/Userland/Libraries/LibJS/Runtime/StringOrSymbol.h
new file mode 100644
index 0000000000..38311b61b2
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/StringOrSymbol.h
@@ -0,0 +1,196 @@
+/*
+ * 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 <AK/FlyString.h>
+#include <LibJS/Runtime/PrimitiveString.h>
+#include <LibJS/Runtime/Symbol.h>
+#include <LibJS/Runtime/Value.h>
+
+namespace JS {
+
+class StringOrSymbol {
+public:
+ static StringOrSymbol from_value(GlobalObject& global_object, Value value)
+ {
+ if (value.is_empty())
+ return {};
+ if (value.is_symbol())
+ return &value.as_symbol();
+ auto string = value.to_string(global_object);
+ if (string.is_null())
+ return {};
+ return string;
+ }
+
+ StringOrSymbol() = default;
+
+ StringOrSymbol(const char* chars)
+ : m_ptr(StringImpl::create(chars).leak_ref())
+ {
+ }
+
+ StringOrSymbol(const String& string)
+ : m_ptr(string.impl())
+ {
+ ASSERT(!string.is_null());
+ as_string_impl().ref();
+ }
+
+ StringOrSymbol(const FlyString& string)
+ : m_ptr(string.impl())
+ {
+ ASSERT(!string.is_null());
+ as_string_impl().ref();
+ }
+
+ ~StringOrSymbol()
+ {
+ if (is_string())
+ as_string_impl().unref();
+ }
+
+ StringOrSymbol(const Symbol* symbol)
+ : m_ptr(symbol)
+ {
+ set_symbol_flag();
+ }
+
+ StringOrSymbol(const StringOrSymbol& other)
+ {
+ m_ptr = other.m_ptr;
+ if (is_string())
+ as_string_impl().ref();
+ }
+
+ StringOrSymbol(StringOrSymbol&& other)
+ {
+ m_ptr = exchange(other.m_ptr, nullptr);
+ }
+
+ ALWAYS_INLINE bool is_valid() const { return m_ptr != nullptr; }
+ ALWAYS_INLINE bool is_symbol() const { return is_valid() && (bits() & 1ul); }
+ ALWAYS_INLINE bool is_string() const { return is_valid() && !(bits() & 1ul); }
+
+ ALWAYS_INLINE String as_string() const
+ {
+ ASSERT(is_string());
+ return as_string_impl();
+ }
+
+ ALWAYS_INLINE const Symbol* as_symbol() const
+ {
+ ASSERT(is_symbol());
+ return reinterpret_cast<const Symbol*>(bits() & ~1ul);
+ }
+
+ String to_display_string() const
+ {
+ if (is_string())
+ return as_string();
+ if (is_symbol())
+ return as_symbol()->to_string();
+ ASSERT_NOT_REACHED();
+ }
+
+ Value to_value(VM& vm) const
+ {
+ if (is_string())
+ return js_string(vm, as_string());
+ if (is_symbol())
+ return const_cast<Symbol*>(as_symbol());
+ return {};
+ }
+
+ void visit_edges(Cell::Visitor& visitor)
+ {
+ if (is_symbol())
+ visitor.visit(const_cast<Symbol*>(as_symbol()));
+ }
+
+ ALWAYS_INLINE bool operator==(const StringOrSymbol& other) const
+ {
+ if (is_string())
+ return other.is_string() && as_string_impl() == other.as_string_impl();
+ if (is_symbol())
+ return other.is_symbol() && as_symbol() == other.as_symbol();
+ return true;
+ }
+
+ StringOrSymbol& operator=(const StringOrSymbol& other)
+ {
+ if (this == &other)
+ return *this;
+ m_ptr = other.m_ptr;
+ if (is_string())
+ as_string_impl().ref();
+ return *this;
+ }
+
+ StringOrSymbol& operator=(StringOrSymbol&& other)
+ {
+ if (this != &other)
+ m_ptr = exchange(other.m_ptr, nullptr);
+ return *this;
+ }
+
+ unsigned hash() const
+ {
+ if (is_string())
+ return as_string_impl().hash();
+ return ptr_hash(as_symbol());
+ }
+
+private:
+ ALWAYS_INLINE u64 bits() const
+ {
+ return reinterpret_cast<uintptr_t>(m_ptr);
+ }
+
+ ALWAYS_INLINE void set_symbol_flag()
+ {
+ m_ptr = reinterpret_cast<const void*>(bits() | 1ul);
+ }
+
+ ALWAYS_INLINE const StringImpl& as_string_impl() const
+ {
+ ASSERT(is_string());
+ return *reinterpret_cast<const StringImpl*>(m_ptr);
+ }
+
+ const void* m_ptr { nullptr };
+};
+
+}
+
+template<>
+struct AK::Traits<JS::StringOrSymbol> : public GenericTraits<JS::StringOrSymbol> {
+ static unsigned hash(const JS::StringOrSymbol& key)
+ {
+ return key.hash();
+ }
+};
diff --git a/Userland/Libraries/LibJS/Runtime/StringPrototype.cpp b/Userland/Libraries/LibJS/Runtime/StringPrototype.cpp
new file mode 100644
index 0000000000..d0f4d495ff
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/StringPrototype.cpp
@@ -0,0 +1,629 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * Copyright (c) 2020, Linus Groh <mail@linusgroh.de>
+ * 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 <AK/Function.h>
+#include <AK/StringBuilder.h>
+#include <LibJS/Heap/Heap.h>
+#include <LibJS/Runtime/Array.h>
+#include <LibJS/Runtime/Error.h>
+#include <LibJS/Runtime/GlobalObject.h>
+#include <LibJS/Runtime/PrimitiveString.h>
+#include <LibJS/Runtime/StringIterator.h>
+#include <LibJS/Runtime/StringObject.h>
+#include <LibJS/Runtime/StringPrototype.h>
+#include <LibJS/Runtime/Value.h>
+#include <string.h>
+
+namespace JS {
+
+static StringObject* typed_this(VM& vm, GlobalObject& global_object)
+{
+ auto* this_object = vm.this_value(global_object).to_object(global_object);
+ if (!this_object)
+ return nullptr;
+ if (!is<StringObject>(this_object)) {
+ vm.throw_exception<TypeError>(global_object, ErrorType::NotA, "String");
+ return nullptr;
+ }
+ return static_cast<StringObject*>(this_object);
+}
+
+static String ak_string_from(VM& vm, GlobalObject& global_object)
+{
+ auto* this_object = vm.this_value(global_object).to_object(global_object);
+ if (!this_object)
+ return {};
+ return Value(this_object).to_string(global_object);
+}
+
+static Optional<size_t> split_match(const String& haystack, size_t start, const String& needle)
+{
+ auto r = needle.length();
+ auto s = haystack.length();
+ if (start + r > s)
+ return {};
+ if (!haystack.substring_view(start).starts_with(needle))
+ return {};
+ return start + r;
+}
+
+StringPrototype::StringPrototype(GlobalObject& global_object)
+ : StringObject(*js_string(global_object.heap(), String::empty()), *global_object.object_prototype())
+{
+}
+
+void StringPrototype::initialize(GlobalObject& global_object)
+{
+ auto& vm = this->vm();
+ StringObject::initialize(global_object);
+ u8 attr = Attribute::Writable | Attribute::Configurable;
+
+ define_native_property(vm.names.length, length_getter, {}, 0);
+ define_native_function(vm.names.charAt, char_at, 1, attr);
+ define_native_function(vm.names.charCodeAt, char_code_at, 1, attr);
+ define_native_function(vm.names.repeat, repeat, 1, attr);
+ define_native_function(vm.names.startsWith, starts_with, 1, attr);
+ define_native_function(vm.names.endsWith, ends_with, 1, attr);
+ define_native_function(vm.names.indexOf, index_of, 1, attr);
+ define_native_function(vm.names.toLowerCase, to_lowercase, 0, attr);
+ define_native_function(vm.names.toUpperCase, to_uppercase, 0, attr);
+ define_native_function(vm.names.toString, to_string, 0, attr);
+ define_native_function(vm.names.padStart, pad_start, 1, attr);
+ define_native_function(vm.names.padEnd, pad_end, 1, attr);
+ define_native_function(vm.names.trim, trim, 0, attr);
+ define_native_function(vm.names.trimStart, trim_start, 0, attr);
+ define_native_function(vm.names.trimEnd, trim_end, 0, attr);
+ define_native_function(vm.names.concat, concat, 1, attr);
+ define_native_function(vm.names.substr, substr, 2, attr);
+ define_native_function(vm.names.substring, substring, 2, attr);
+ define_native_function(vm.names.includes, includes, 1, attr);
+ define_native_function(vm.names.slice, slice, 2, attr);
+ define_native_function(vm.names.split, split, 2, attr);
+ define_native_function(vm.names.lastIndexOf, last_index_of, 1, attr);
+ define_native_function(vm.well_known_symbol_iterator(), symbol_iterator, 0, attr);
+}
+
+StringPrototype::~StringPrototype()
+{
+}
+
+JS_DEFINE_NATIVE_FUNCTION(StringPrototype::char_at)
+{
+ auto string = ak_string_from(vm, global_object);
+ if (string.is_null())
+ return {};
+ i32 index = 0;
+ if (vm.argument_count()) {
+ index = vm.argument(0).to_i32(global_object);
+ if (vm.exception())
+ return {};
+ }
+ if (index < 0 || index >= static_cast<i32>(string.length()))
+ return js_string(vm, String::empty());
+ // FIXME: This should return a character corresponding to the i'th UTF-16 code point.
+ return js_string(vm, string.substring(index, 1));
+}
+
+JS_DEFINE_NATIVE_FUNCTION(StringPrototype::char_code_at)
+{
+ auto string = ak_string_from(vm, global_object);
+ if (string.is_null())
+ return {};
+ i32 index = 0;
+ if (vm.argument_count()) {
+ index = vm.argument(0).to_i32(global_object);
+ if (vm.exception())
+ return {};
+ }
+ if (index < 0 || index >= static_cast<i32>(string.length()))
+ return js_nan();
+ // FIXME: This should return the i'th UTF-16 code point.
+ return Value((i32)string[index]);
+}
+
+JS_DEFINE_NATIVE_FUNCTION(StringPrototype::repeat)
+{
+ auto string = ak_string_from(vm, global_object);
+ if (string.is_null())
+ return {};
+ if (!vm.argument_count())
+ return js_string(vm, String::empty());
+ auto count = vm.argument(0).to_integer_or_infinity(global_object);
+ if (vm.exception())
+ return {};
+ if (count < 0) {
+ vm.throw_exception<RangeError>(global_object, ErrorType::StringRepeatCountMustBe, "positive");
+ return {};
+ }
+ if (Value(count).is_infinity()) {
+ vm.throw_exception<RangeError>(global_object, ErrorType::StringRepeatCountMustBe, "finite");
+ return {};
+ }
+ StringBuilder builder;
+ for (size_t i = 0; i < count; ++i)
+ builder.append(string);
+ return js_string(vm, builder.to_string());
+}
+
+JS_DEFINE_NATIVE_FUNCTION(StringPrototype::starts_with)
+{
+ auto string = ak_string_from(vm, global_object);
+ if (string.is_null())
+ return {};
+ if (!vm.argument_count())
+ return Value(false);
+ auto search_string = vm.argument(0).to_string(global_object);
+ if (vm.exception())
+ return {};
+ auto string_length = string.length();
+ auto search_string_length = search_string.length();
+ size_t start = 0;
+ if (!vm.argument(1).is_undefined()) {
+ auto position = vm.argument(1).to_integer_or_infinity(global_object);
+ if (vm.exception())
+ return {};
+ start = clamp(position, static_cast<double>(0), static_cast<double>(string_length));
+ }
+ if (start + search_string_length > string_length)
+ return Value(false);
+ if (search_string_length == 0)
+ return Value(true);
+ return Value(string.substring(start, search_string_length) == search_string);
+}
+
+JS_DEFINE_NATIVE_FUNCTION(StringPrototype::ends_with)
+{
+ auto string = ak_string_from(vm, global_object);
+ if (string.is_null())
+ return {};
+
+ auto search_string_value = vm.argument(0);
+
+ bool search_is_regexp = search_string_value.is_regexp(global_object);
+ if (vm.exception())
+ return {};
+ if (search_is_regexp) {
+ vm.throw_exception<TypeError>(global_object, ErrorType::IsNotA, "searchString", "string, but a regular expression");
+ return {};
+ }
+
+ auto search_string = search_string_value.to_string(global_object);
+ if (vm.exception())
+ return {};
+
+ auto string_length = string.length();
+ auto search_string_length = search_string.length();
+
+ size_t pos = string_length;
+
+ auto end_position_value = vm.argument(1);
+ if (!end_position_value.is_undefined()) {
+ double pos_as_double = end_position_value.to_integer_or_infinity(global_object);
+ if (vm.exception())
+ return {};
+ pos = clamp(pos_as_double, static_cast<double>(0), static_cast<double>(string_length));
+ }
+
+ if (search_string_length == 0)
+ return Value(true);
+ if (pos < search_string_length)
+ return Value(false);
+
+ auto start = pos - search_string_length;
+ return Value(string.substring(start, search_string_length) == search_string);
+}
+
+JS_DEFINE_NATIVE_FUNCTION(StringPrototype::index_of)
+{
+ auto string = ak_string_from(vm, global_object);
+ if (string.is_null())
+ return {};
+ auto needle = vm.argument(0).to_string(global_object);
+ if (vm.exception())
+ return {};
+ return Value((i32)string.index_of(needle).value_or(-1));
+}
+
+JS_DEFINE_NATIVE_FUNCTION(StringPrototype::to_lowercase)
+{
+ auto string = ak_string_from(vm, global_object);
+ if (string.is_null())
+ return {};
+ return js_string(vm, string.to_lowercase());
+}
+
+JS_DEFINE_NATIVE_FUNCTION(StringPrototype::to_uppercase)
+{
+ auto string = ak_string_from(vm, global_object);
+ if (string.is_null())
+ return {};
+ return js_string(vm, string.to_uppercase());
+}
+
+JS_DEFINE_NATIVE_GETTER(StringPrototype::length_getter)
+{
+ auto* string_object = typed_this(vm, global_object);
+ if (!string_object)
+ return {};
+ return Value((i32)string_object->primitive_string().string().length());
+}
+
+JS_DEFINE_NATIVE_FUNCTION(StringPrototype::to_string)
+{
+ auto* string_object = typed_this(vm, global_object);
+ if (!string_object)
+ return {};
+ return js_string(vm, string_object->primitive_string().string());
+}
+
+enum class PadPlacement {
+ Start,
+ End,
+};
+
+static Value pad_string(GlobalObject& global_object, const String& string, PadPlacement placement)
+{
+ auto& vm = global_object.vm();
+ auto max_length = vm.argument(0).to_length(global_object);
+ if (vm.exception())
+ return {};
+ if (max_length <= string.length())
+ return js_string(vm, string);
+
+ String fill_string = " ";
+ if (!vm.argument(1).is_undefined()) {
+ fill_string = vm.argument(1).to_string(global_object);
+ if (vm.exception())
+ return {};
+ if (fill_string.is_empty())
+ return js_string(vm, string);
+ }
+
+ auto fill_length = max_length - string.length();
+
+ StringBuilder filler_builder;
+ while (filler_builder.length() < fill_length)
+ filler_builder.append(fill_string);
+ auto filler = filler_builder.build().substring(0, fill_length);
+
+ auto formatted = placement == PadPlacement::Start
+ ? String::formatted("{}{}", filler, string)
+ : String::formatted("{}{}", string, filler);
+ return js_string(vm, formatted);
+}
+
+JS_DEFINE_NATIVE_FUNCTION(StringPrototype::pad_start)
+{
+ auto string = ak_string_from(vm, global_object);
+ if (string.is_null())
+ return {};
+ return pad_string(global_object, string, PadPlacement::Start);
+}
+
+JS_DEFINE_NATIVE_FUNCTION(StringPrototype::pad_end)
+{
+ auto string = ak_string_from(vm, global_object);
+ if (string.is_null())
+ return {};
+ return pad_string(global_object, string, PadPlacement::End);
+}
+
+JS_DEFINE_NATIVE_FUNCTION(StringPrototype::trim)
+{
+ auto string = ak_string_from(vm, global_object);
+ if (string.is_null())
+ return {};
+ return js_string(vm, string.trim_whitespace(TrimMode::Both));
+}
+
+JS_DEFINE_NATIVE_FUNCTION(StringPrototype::trim_start)
+{
+ auto string = ak_string_from(vm, global_object);
+ if (string.is_null())
+ return {};
+ return js_string(vm, string.trim_whitespace(TrimMode::Left));
+}
+
+JS_DEFINE_NATIVE_FUNCTION(StringPrototype::trim_end)
+{
+ auto string = ak_string_from(vm, global_object);
+ if (string.is_null())
+ return {};
+ return js_string(vm, string.trim_whitespace(TrimMode::Right));
+}
+
+JS_DEFINE_NATIVE_FUNCTION(StringPrototype::concat)
+{
+ auto string = ak_string_from(vm, global_object);
+ if (string.is_null())
+ return {};
+ StringBuilder builder;
+ builder.append(string);
+ for (size_t i = 0; i < vm.argument_count(); ++i) {
+ auto string_argument = vm.argument(i).to_string(global_object);
+ if (vm.exception())
+ return {};
+ builder.append(string_argument);
+ }
+ return js_string(vm, builder.to_string());
+}
+
+JS_DEFINE_NATIVE_FUNCTION(StringPrototype::substring)
+{
+ auto string = ak_string_from(vm, global_object);
+ if (string.is_null())
+ return {};
+ if (vm.argument_count() == 0)
+ return js_string(vm, string);
+
+ // FIXME: index_start and index_end should index a UTF-16 code_point view of the string.
+ auto string_length = string.length();
+ auto start = vm.argument(0).to_integer_or_infinity(global_object);
+ if (vm.exception())
+ return {};
+ auto end = (double)string_length;
+ if (!vm.argument(1).is_undefined()) {
+ end = vm.argument(1).to_integer_or_infinity(global_object);
+ if (vm.exception())
+ return {};
+ }
+ size_t index_start = clamp(start, static_cast<double>(0), static_cast<double>(string_length));
+ size_t index_end = clamp(end, static_cast<double>(0), static_cast<double>(string_length));
+
+ if (index_start == index_end)
+ return js_string(vm, String(""));
+
+ if (index_start > index_end) {
+ if (vm.argument_count() == 1)
+ return js_string(vm, String(""));
+ auto temp_index_start = index_start;
+ index_start = index_end;
+ index_end = temp_index_start;
+ }
+
+ auto part_length = index_end - index_start;
+ auto string_part = string.substring(index_start, part_length);
+ return js_string(vm, string_part);
+}
+
+JS_DEFINE_NATIVE_FUNCTION(StringPrototype::substr)
+{
+ auto string = ak_string_from(vm, global_object);
+ if (string.is_null())
+ return {};
+ if (vm.argument_count() == 0)
+ return js_string(vm, string);
+
+ // FIXME: this should index a UTF-16 code_point view of the string.
+ auto string_length = (i32)string.length();
+
+ auto start_argument = vm.argument(0).to_i32(global_object);
+ if (vm.exception())
+ return {};
+
+ auto start = start_argument < 0 ? (string_length - -start_argument) : start_argument;
+
+ auto length = string_length - start;
+ if (vm.argument_count() >= 2) {
+ auto length_argument = vm.argument(1).to_i32(global_object);
+ if (vm.exception())
+ return {};
+ length = max(0, min(length_argument, length));
+ if (vm.exception())
+ return {};
+ }
+
+ if (length == 0)
+ return js_string(vm, String(""));
+
+ auto string_part = string.substring(start, length);
+ return js_string(vm, string_part);
+}
+
+JS_DEFINE_NATIVE_FUNCTION(StringPrototype::includes)
+{
+ auto string = ak_string_from(vm, global_object);
+ if (string.is_null())
+ return {};
+ auto search_string = vm.argument(0).to_string(global_object);
+ if (vm.exception())
+ return {};
+ auto string_length = string.length();
+ // FIXME: start should index a UTF-16 code_point view of the string.
+ size_t start = 0;
+ if (!vm.argument(1).is_undefined()) {
+ auto position = vm.argument(1).to_integer_or_infinity(global_object);
+ if (vm.exception())
+ return {};
+ start = clamp(position, static_cast<double>(0), static_cast<double>(string_length));
+ }
+ if (start == 0)
+ return Value(string.contains(search_string));
+ auto substring_length = string_length - start;
+ auto substring_search = string.substring(start, substring_length);
+ return Value(substring_search.contains(search_string));
+}
+
+JS_DEFINE_NATIVE_FUNCTION(StringPrototype::slice)
+{
+ auto string = ak_string_from(vm, global_object);
+ if (string.is_null())
+ return {};
+
+ if (vm.argument_count() == 0)
+ return js_string(vm, string);
+
+ // FIXME: index_start and index_end should index a UTF-16 code_point view of the string.
+ auto string_length = static_cast<i32>(string.length());
+ auto index_start = vm.argument(0).to_i32(global_object);
+ if (vm.exception())
+ return {};
+ auto index_end = string_length;
+
+ auto negative_min_index = -(string_length - 1);
+ if (index_start < negative_min_index)
+ index_start = 0;
+ else if (index_start < 0)
+ index_start = string_length + index_start;
+
+ if (vm.argument_count() >= 2) {
+ index_end = vm.argument(1).to_i32(global_object);
+ if (vm.exception())
+ return {};
+
+ if (index_end < negative_min_index)
+ return js_string(vm, String::empty());
+
+ if (index_end > string_length)
+ index_end = string_length;
+ else if (index_end < 0)
+ index_end = string_length + index_end;
+ }
+
+ if (index_start >= index_end)
+ return js_string(vm, String::empty());
+
+ auto part_length = index_end - index_start;
+ auto string_part = string.substring(index_start, part_length);
+ return js_string(vm, string_part);
+}
+
+JS_DEFINE_NATIVE_FUNCTION(StringPrototype::split)
+{
+ // FIXME Implement the @@split part
+
+ auto string = ak_string_from(vm, global_object);
+ if (string.is_null())
+ return {};
+
+ auto* result = Array::create(global_object);
+ size_t result_len = 0;
+
+ auto limit = static_cast<u32>(MAX_U32);
+ if (!vm.argument(1).is_undefined()) {
+ limit = vm.argument(1).to_u32(global_object);
+ if (vm.exception())
+ return {};
+ }
+
+ auto separator = vm.argument(0).to_string(global_object);
+ if (vm.exception())
+ return {};
+
+ if (limit == 0)
+ return result;
+
+ if (vm.argument(0).is_undefined()) {
+ result->define_property(0, js_string(vm, string));
+ return result;
+ }
+
+ auto len = string.length();
+ auto separator_len = separator.length();
+ if (len == 0) {
+ if (separator_len > 0)
+ result->define_property(0, js_string(vm, string));
+ return result;
+ }
+
+ size_t start = 0;
+ auto pos = start;
+ if (separator_len == 0) {
+ for (pos = 0; pos < len; pos++)
+ result->define_property(pos, js_string(vm, string.substring(pos, 1)));
+ return result;
+ }
+
+ while (pos != len) {
+ auto e = split_match(string, pos, separator);
+ if (!e.has_value()) {
+ pos += 1;
+ continue;
+ }
+
+ auto segment = string.substring_view(start, pos - start);
+ result->define_property(result_len, js_string(vm, segment));
+ result_len++;
+ if (result_len == limit)
+ return result;
+ start = e.value();
+ pos = start;
+ }
+
+ auto rest = string.substring(start, len - start);
+ result->define_property(result_len, js_string(vm, rest));
+
+ return result;
+}
+
+JS_DEFINE_NATIVE_FUNCTION(StringPrototype::last_index_of)
+{
+ auto string = ak_string_from(vm, global_object);
+ if (string.is_null())
+ return {};
+ auto search_string = vm.argument(0).to_string(global_object);
+ if (vm.exception())
+ return {};
+ auto position = vm.argument(1).to_number(global_object);
+ if (vm.exception())
+ return {};
+ if (search_string.length() > string.length())
+ return Value(-1);
+ auto max_index = string.length() - search_string.length();
+ auto from_index = max_index;
+ if (!position.is_nan()) {
+ // FIXME: from_index should index a UTF-16 code_point view of the string.
+ auto p = position.to_integer_or_infinity(global_object);
+ if (vm.exception())
+ return {};
+ from_index = clamp(p, static_cast<double>(0), static_cast<double>(max_index));
+ }
+
+ for (i32 i = from_index; i >= 0; --i) {
+ auto part_view = string.substring_view(i, search_string.length());
+ if (part_view == search_string)
+ return Value(i);
+ }
+
+ return Value(-1);
+}
+
+JS_DEFINE_NATIVE_FUNCTION(StringPrototype::symbol_iterator)
+{
+ auto this_object = vm.this_value(global_object);
+ if (this_object.is_nullish()) {
+ vm.throw_exception<TypeError>(global_object, ErrorType::ToObjectNullOrUndef);
+ return {};
+ }
+
+ auto string = this_object.to_string(global_object);
+ if (vm.exception())
+ return {};
+ return StringIterator::create(global_object, string);
+}
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/StringPrototype.h b/Userland/Libraries/LibJS/Runtime/StringPrototype.h
new file mode 100644
index 0000000000..5847ea2117
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/StringPrototype.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * 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/StringObject.h>
+
+namespace JS {
+
+class StringPrototype final : public StringObject {
+ JS_OBJECT(StringPrototype, StringObject);
+
+public:
+ explicit StringPrototype(GlobalObject&);
+ virtual void initialize(GlobalObject&) override;
+ virtual ~StringPrototype() override;
+
+private:
+ JS_DECLARE_NATIVE_FUNCTION(char_at);
+ JS_DECLARE_NATIVE_FUNCTION(char_code_at);
+ JS_DECLARE_NATIVE_FUNCTION(repeat);
+ JS_DECLARE_NATIVE_FUNCTION(starts_with);
+ JS_DECLARE_NATIVE_FUNCTION(ends_with);
+ JS_DECLARE_NATIVE_FUNCTION(index_of);
+ JS_DECLARE_NATIVE_FUNCTION(to_lowercase);
+ JS_DECLARE_NATIVE_FUNCTION(to_uppercase);
+ JS_DECLARE_NATIVE_FUNCTION(to_string);
+ JS_DECLARE_NATIVE_FUNCTION(pad_start);
+ JS_DECLARE_NATIVE_FUNCTION(pad_end);
+ JS_DECLARE_NATIVE_FUNCTION(substring);
+ JS_DECLARE_NATIVE_FUNCTION(substr);
+
+ JS_DECLARE_NATIVE_GETTER(length_getter);
+
+ JS_DECLARE_NATIVE_FUNCTION(trim);
+ JS_DECLARE_NATIVE_FUNCTION(trim_start);
+ JS_DECLARE_NATIVE_FUNCTION(trim_end);
+ JS_DECLARE_NATIVE_FUNCTION(concat);
+ JS_DECLARE_NATIVE_FUNCTION(includes);
+ JS_DECLARE_NATIVE_FUNCTION(slice);
+ JS_DECLARE_NATIVE_FUNCTION(split);
+ JS_DECLARE_NATIVE_FUNCTION(last_index_of);
+
+ JS_DECLARE_NATIVE_FUNCTION(symbol_iterator);
+};
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/Symbol.cpp b/Userland/Libraries/LibJS/Runtime/Symbol.cpp
new file mode 100644
index 0000000000..c25ebf2fce
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/Symbol.cpp
@@ -0,0 +1,53 @@
+/*
+ * 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/Heap/Heap.h>
+#include <LibJS/Runtime/Symbol.h>
+#include <LibJS/Runtime/VM.h>
+
+namespace JS {
+
+Symbol::Symbol(String description, bool is_global)
+ : m_description(move(description))
+ , m_is_global(is_global)
+{
+}
+
+Symbol::~Symbol()
+{
+}
+
+Symbol* js_symbol(Heap& heap, String description, bool is_global)
+{
+ return heap.allocate_without_global_object<Symbol>(move(description), is_global);
+}
+
+Symbol* js_symbol(VM& vm, String description, bool is_global)
+{
+ return js_symbol(vm.heap(), move(description), is_global);
+}
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/Symbol.h b/Userland/Libraries/LibJS/Runtime/Symbol.h
new file mode 100644
index 0000000000..d01179add7
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/Symbol.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 <AK/String.h>
+#include <LibJS/Runtime/Cell.h>
+
+namespace JS {
+
+class Symbol final : public Cell {
+ AK_MAKE_NONCOPYABLE(Symbol);
+ AK_MAKE_NONMOVABLE(Symbol);
+
+public:
+ Symbol(String, bool);
+ virtual ~Symbol();
+
+ const String& description() const { return m_description; }
+ bool is_global() const { return m_is_global; }
+ String to_string() const { return String::formatted("Symbol({})", description()); }
+
+private:
+ virtual const char* class_name() const override { return "Symbol"; }
+
+ String m_description;
+ bool m_is_global;
+};
+
+Symbol* js_symbol(Heap&, String description, bool is_global);
+Symbol* js_symbol(VM&, String description, bool is_global);
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/SymbolConstructor.cpp b/Userland/Libraries/LibJS/Runtime/SymbolConstructor.cpp
new file mode 100644
index 0000000000..0697ad98e6
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/SymbolConstructor.cpp
@@ -0,0 +1,99 @@
+/*
+ * 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/Error.h>
+#include <LibJS/Runtime/GlobalObject.h>
+#include <LibJS/Runtime/SymbolConstructor.h>
+#include <LibJS/Runtime/SymbolObject.h>
+
+namespace JS {
+
+SymbolConstructor::SymbolConstructor(GlobalObject& global_object)
+ : NativeFunction(vm().names.Symbol, *global_object.function_prototype())
+{
+}
+
+void SymbolConstructor::initialize(GlobalObject& global_object)
+{
+ auto& vm = this->vm();
+ NativeFunction::initialize(global_object);
+ define_property(vm.names.prototype, global_object.symbol_prototype(), 0);
+ define_property(vm.names.length, Value(0), Attribute::Configurable);
+
+ define_native_function(vm.names.for_, for_, 1, Attribute::Writable | Attribute::Configurable);
+ define_native_function(vm.names.keyFor, key_for, 1, Attribute::Writable | Attribute::Configurable);
+
+#define __JS_ENUMERATE(SymbolName, snake_name) \
+ define_property(vm.names.SymbolName, vm.well_known_symbol_##snake_name(), 0);
+ JS_ENUMERATE_WELL_KNOWN_SYMBOLS
+#undef __JS_ENUMERATE
+}
+
+SymbolConstructor::~SymbolConstructor()
+{
+}
+
+Value SymbolConstructor::call()
+{
+ if (!vm().argument_count())
+ return js_symbol(heap(), "", false);
+ return js_symbol(heap(), vm().argument(0).to_string(global_object()), false);
+}
+
+Value SymbolConstructor::construct(Function&)
+{
+ vm().throw_exception<TypeError>(global_object(), ErrorType::NotAConstructor, "Symbol");
+ return {};
+}
+
+JS_DEFINE_NATIVE_FUNCTION(SymbolConstructor::for_)
+{
+ String description;
+ if (!vm.argument_count()) {
+ description = "undefined";
+ } else {
+ description = vm.argument(0).to_string(global_object);
+ }
+
+ return global_object.vm().get_global_symbol(description);
+}
+
+JS_DEFINE_NATIVE_FUNCTION(SymbolConstructor::key_for)
+{
+ auto argument = vm.argument(0);
+ if (!argument.is_symbol()) {
+ vm.throw_exception<TypeError>(global_object, ErrorType::NotASymbol, argument.to_string_without_side_effects());
+ return {};
+ }
+
+ auto& symbol = argument.as_symbol();
+ if (symbol.is_global())
+ return js_string(vm, symbol.description());
+
+ return js_undefined();
+}
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/SymbolConstructor.h b/Userland/Libraries/LibJS/Runtime/SymbolConstructor.h
new file mode 100644
index 0000000000..8afba45fb5
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/SymbolConstructor.h
@@ -0,0 +1,51 @@
+/*
+ * 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/NativeFunction.h>
+
+namespace JS {
+
+class SymbolConstructor final : public NativeFunction {
+ JS_OBJECT(SymbolConstructor, NativeFunction);
+
+public:
+ explicit SymbolConstructor(GlobalObject&);
+ virtual void initialize(GlobalObject&) override;
+ virtual ~SymbolConstructor() override;
+
+ virtual Value call() override;
+ virtual Value construct(Function& new_target) override;
+
+private:
+ virtual bool has_constructor() const override { return true; }
+
+ JS_DECLARE_NATIVE_FUNCTION(for_);
+ JS_DECLARE_NATIVE_FUNCTION(key_for);
+};
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/SymbolObject.cpp b/Userland/Libraries/LibJS/Runtime/SymbolObject.cpp
new file mode 100644
index 0000000000..deb1170928
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/SymbolObject.cpp
@@ -0,0 +1,57 @@
+/*
+ * 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/Heap/Heap.h>
+#include <LibJS/Runtime/GlobalObject.h>
+#include <LibJS/Runtime/Symbol.h>
+#include <LibJS/Runtime/SymbolObject.h>
+#include <LibJS/Runtime/SymbolPrototype.h>
+#include <LibJS/Runtime/Value.h>
+
+namespace JS {
+
+SymbolObject* SymbolObject::create(GlobalObject& global_object, Symbol& primitive_symbol)
+{
+ return global_object.heap().allocate<SymbolObject>(global_object, primitive_symbol, *global_object.symbol_prototype());
+}
+
+SymbolObject::SymbolObject(Symbol& symbol, Object& prototype)
+ : Object(prototype)
+ , m_symbol(symbol)
+{
+}
+
+SymbolObject::~SymbolObject()
+{
+}
+
+void SymbolObject::visit_edges(Cell::Visitor& visitor)
+{
+ Object::visit_edges(visitor);
+ visitor.visit(&m_symbol);
+}
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/SymbolObject.h b/Userland/Libraries/LibJS/Runtime/SymbolObject.h
new file mode 100644
index 0000000000..0ff212ffb5
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/SymbolObject.h
@@ -0,0 +1,60 @@
+/*
+ * 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>
+#include <LibJS/Runtime/Symbol.h>
+
+namespace JS {
+
+class SymbolObject : public Object {
+ JS_OBJECT(SymbolObject, Object);
+
+public:
+ static SymbolObject* create(GlobalObject&, Symbol&);
+
+ SymbolObject(Symbol&, Object& prototype);
+ virtual ~SymbolObject() override;
+
+ Symbol& primitive_symbol() { return m_symbol; }
+ const Symbol& primitive_symbol() const { return m_symbol; }
+
+ const String& description() const { return m_symbol.description(); }
+ bool is_global() const { return m_symbol.is_global(); }
+
+ virtual Value value_of() const override
+ {
+ return Value(&m_symbol);
+ }
+
+private:
+ virtual void visit_edges(Visitor&) override;
+
+ Symbol& m_symbol;
+};
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/SymbolPrototype.cpp b/Userland/Libraries/LibJS/Runtime/SymbolPrototype.cpp
new file mode 100644
index 0000000000..0a36ab4a20
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/SymbolPrototype.cpp
@@ -0,0 +1,97 @@
+/*
+ * 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 <AK/Function.h>
+#include <AK/StringBuilder.h>
+#include <LibJS/Heap/Heap.h>
+#include <LibJS/Runtime/Error.h>
+#include <LibJS/Runtime/GlobalObject.h>
+#include <LibJS/Runtime/Object.h>
+#include <LibJS/Runtime/PrimitiveString.h>
+#include <LibJS/Runtime/SymbolObject.h>
+#include <LibJS/Runtime/SymbolPrototype.h>
+#include <LibJS/Runtime/Value.h>
+#include <string.h>
+
+namespace JS {
+
+SymbolPrototype::SymbolPrototype(GlobalObject& global_object)
+ : Object(*global_object.object_prototype())
+{
+}
+
+void SymbolPrototype::initialize(GlobalObject& global_object)
+{
+ auto& vm = this->vm();
+ Object::initialize(global_object);
+ define_native_property(vm.names.description, description_getter, {}, Attribute::Configurable);
+ define_native_function(vm.names.toString, to_string, 0, Attribute::Writable | Attribute::Configurable);
+ define_native_function(vm.names.valueOf, value_of, 0, Attribute::Writable | Attribute::Configurable);
+
+ define_property(global_object.vm().well_known_symbol_to_string_tag(), js_string(global_object.heap(), "Symbol"), Attribute::Configurable);
+}
+
+SymbolPrototype::~SymbolPrototype()
+{
+}
+
+static SymbolObject* typed_this(VM& vm, GlobalObject& global_object)
+{
+ auto* this_object = vm.this_value(global_object).to_object(global_object);
+ if (!this_object)
+ return nullptr;
+ if (!is<SymbolObject>(this_object)) {
+ vm.throw_exception<TypeError>(global_object, ErrorType::NotA, "Symbol");
+ return nullptr;
+ }
+ return static_cast<SymbolObject*>(this_object);
+}
+
+JS_DEFINE_NATIVE_GETTER(SymbolPrototype::description_getter)
+{
+ auto* this_object = typed_this(vm, global_object);
+ if (!this_object)
+ return {};
+ return js_string(vm, this_object->description());
+}
+
+JS_DEFINE_NATIVE_FUNCTION(SymbolPrototype::to_string)
+{
+ auto* this_object = typed_this(vm, global_object);
+ if (!this_object)
+ return {};
+ auto string = this_object->primitive_symbol().to_string();
+ return js_string(vm, move(string));
+}
+
+JS_DEFINE_NATIVE_FUNCTION(SymbolPrototype::value_of)
+{
+ auto* this_object = typed_this(vm, global_object);
+ if (!this_object)
+ return {};
+ return this_object->value_of();
+}
+}
diff --git a/Userland/Libraries/LibJS/Runtime/SymbolPrototype.h b/Userland/Libraries/LibJS/Runtime/SymbolPrototype.h
new file mode 100644
index 0000000000..589b46e7a7
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/SymbolPrototype.h
@@ -0,0 +1,48 @@
+/*
+ * 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 SymbolPrototype final : public Object {
+ JS_OBJECT(SymbolPrototype, Object);
+
+public:
+ explicit SymbolPrototype(GlobalObject&);
+ virtual void initialize(GlobalObject&) override;
+ virtual ~SymbolPrototype() override;
+
+private:
+ JS_DECLARE_NATIVE_GETTER(description_getter);
+
+ JS_DECLARE_NATIVE_FUNCTION(to_string);
+ JS_DECLARE_NATIVE_FUNCTION(value_of);
+};
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/TypedArray.cpp b/Userland/Libraries/LibJS/Runtime/TypedArray.cpp
new file mode 100644
index 0000000000..d5bc286e41
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/TypedArray.cpp
@@ -0,0 +1,163 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * Copyright (c) 2020, Linus Groh <mail@linusgroh.de>
+ * 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/ArrayBuffer.h>
+#include <LibJS/Runtime/GlobalObject.h>
+#include <LibJS/Runtime/TypedArray.h>
+#include <LibJS/Runtime/TypedArrayConstructor.h>
+
+namespace JS {
+
+static void initialize_typed_array_from_array_buffer(GlobalObject& global_object, TypedArrayBase& typed_array, ArrayBuffer& array_buffer, Value byte_offset, Value length)
+{
+ // 22.2.5.1.3 InitializeTypedArrayFromArrayBuffer, https://tc39.es/ecma262/#sec-initializetypedarrayfromarraybuffer
+
+ auto& vm = global_object.vm();
+ auto element_size = typed_array.element_size();
+ auto offset = byte_offset.to_index(global_object);
+ if (vm.exception())
+ return;
+ if (offset % element_size != 0) {
+ vm.throw_exception<RangeError>(global_object, ErrorType::TypedArrayInvalidByteOffset, typed_array.class_name(), element_size, offset);
+ return;
+ }
+ size_t new_length { 0 };
+ if (!length.is_undefined()) {
+ new_length = length.to_index(global_object);
+ if (vm.exception())
+ return;
+ }
+ // FIXME: 8. If IsDetachedBuffer(buffer) is true, throw a TypeError exception.
+ auto buffer_byte_length = array_buffer.byte_length();
+ size_t new_byte_length;
+ if (length.is_undefined()) {
+ if (buffer_byte_length % element_size != 0) {
+ vm.throw_exception<RangeError>(global_object, ErrorType::TypedArrayInvalidBufferLength, typed_array.class_name(), element_size, buffer_byte_length);
+ return;
+ }
+ if (offset > buffer_byte_length) {
+ vm.throw_exception<RangeError>(global_object, ErrorType::TypedArrayOutOfRangeByteOffset, offset, buffer_byte_length);
+ return;
+ }
+ new_byte_length = buffer_byte_length - offset;
+ } else {
+ new_byte_length = new_length * element_size;
+ if (offset + new_byte_length > buffer_byte_length) {
+ vm.throw_exception<RangeError>(global_object, ErrorType::TypedArrayOutOfRangeByteOffsetOrLength, offset, offset + new_byte_length, buffer_byte_length);
+ return;
+ }
+ }
+ typed_array.set_viewed_array_buffer(&array_buffer);
+ typed_array.set_byte_length(new_byte_length);
+ typed_array.set_byte_offset(offset);
+ typed_array.set_array_length(new_byte_length / element_size);
+}
+
+void TypedArrayBase::visit_edges(Visitor& visitor)
+{
+ Object::visit_edges(visitor);
+ visitor.visit(m_viewed_array_buffer);
+}
+
+#define JS_DEFINE_TYPED_ARRAY(ClassName, snake_name, PrototypeName, ConstructorName, Type) \
+ ClassName* ClassName::create(GlobalObject& global_object, u32 length) \
+ { \
+ return global_object.heap().allocate<ClassName>(global_object, length, *global_object.snake_name##_prototype()); \
+ } \
+ \
+ ClassName::ClassName(u32 length, Object& prototype) \
+ : TypedArray(length, prototype) \
+ { \
+ } \
+ ClassName::~ClassName() { } \
+ \
+ PrototypeName::PrototypeName(GlobalObject& global_object) \
+ : Object(*global_object.typed_array_prototype()) \
+ { \
+ } \
+ PrototypeName::~PrototypeName() { } \
+ \
+ ConstructorName::ConstructorName(GlobalObject& global_object) \
+ : TypedArrayConstructor(vm().names.ClassName, *global_object.typed_array_constructor()) \
+ { \
+ } \
+ ConstructorName::~ConstructorName() { } \
+ void ConstructorName::initialize(GlobalObject& global_object) \
+ { \
+ auto& vm = this->vm(); \
+ NativeFunction::initialize(global_object); \
+ define_property(vm.names.prototype, global_object.snake_name##_prototype(), 0); \
+ define_property(vm.names.length, Value(1), Attribute::Configurable); \
+ define_property(vm.names.BYTES_PER_ELEMENT, Value((i32)sizeof(Type)), 0); \
+ } \
+ Value ConstructorName::call() \
+ { \
+ auto& vm = this->vm(); \
+ vm.throw_exception<TypeError>(global_object(), ErrorType::ConstructorWithoutNew, vm.names.ClassName); \
+ return {}; \
+ } \
+ Value ConstructorName::construct(Function&) \
+ { \
+ auto& vm = this->vm(); \
+ if (vm.argument_count() == 0) \
+ return ClassName::create(global_object(), 0); \
+ \
+ auto first_argument = vm.argument(0); \
+ if (first_argument.is_object()) { \
+ auto* typed_array = ClassName::create(global_object(), 0); \
+ if (first_argument.as_object().is_typed_array()) { \
+ /* FIXME: Initialize from TypedArray */ \
+ TODO(); \
+ } else if (is<ArrayBuffer>(first_argument.as_object())) { \
+ auto& array_buffer = static_cast<ArrayBuffer&>(first_argument.as_object()); \
+ initialize_typed_array_from_array_buffer(global_object(), *typed_array, array_buffer, vm.argument(1), vm.argument(2)); \
+ if (vm.exception()) \
+ return {}; \
+ } else { \
+ /* FIXME: Initialize from Iterator or Array-like object */ \
+ TODO(); \
+ } \
+ return typed_array; \
+ } \
+ \
+ auto array_length = first_argument.to_index(global_object()); \
+ if (vm.exception()) { \
+ /* Re-throw more specific RangeError */ \
+ vm.clear_exception(); \
+ vm.throw_exception<RangeError>(global_object(), ErrorType::InvalidLength, "typed array"); \
+ return {}; \
+ } \
+ return ClassName::create(global_object(), array_length); \
+ }
+
+#undef __JS_ENUMERATE
+#define __JS_ENUMERATE(ClassName, snake_name, PrototypeName, ConstructorName, ArrayType) \
+ JS_DEFINE_TYPED_ARRAY(ClassName, snake_name, PrototypeName, ConstructorName, ArrayType);
+JS_ENUMERATE_TYPED_ARRAYS
+#undef __JS_ENUMERATE
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/TypedArray.h b/Userland/Libraries/LibJS/Runtime/TypedArray.h
new file mode 100644
index 0000000000..1a10a1a93f
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/TypedArray.h
@@ -0,0 +1,180 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * 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/ArrayBuffer.h>
+#include <LibJS/Runtime/GlobalObject.h>
+#include <LibJS/Runtime/TypedArrayConstructor.h>
+#include <LibJS/Runtime/VM.h>
+
+namespace JS {
+
+class TypedArrayBase : public Object {
+ JS_OBJECT(TypedArrayBase, Object);
+
+public:
+ u32 array_length() const { return m_array_length; }
+ u32 byte_length() const { return m_byte_length; }
+ u32 byte_offset() const { return m_byte_offset; }
+ ArrayBuffer* viewed_array_buffer() const { return m_viewed_array_buffer; }
+
+ void set_array_length(u32 length) { m_array_length = length; }
+ void set_byte_length(u32 length) { m_byte_length = length; }
+ void set_byte_offset(u32 offset) { m_byte_offset = offset; }
+ void set_viewed_array_buffer(ArrayBuffer* array_buffer) { m_viewed_array_buffer = array_buffer; }
+
+ virtual size_t element_size() const = 0;
+
+protected:
+ TypedArrayBase(Object& prototype)
+ : Object(prototype)
+ {
+ }
+
+ u32 m_array_length { 0 };
+ u32 m_byte_length { 0 };
+ u32 m_byte_offset { 0 };
+ ArrayBuffer* m_viewed_array_buffer { nullptr };
+
+private:
+ virtual void visit_edges(Visitor&) override;
+};
+
+template<typename T>
+class TypedArray : public TypedArrayBase {
+ JS_OBJECT(TypedArray, TypedArrayBase);
+
+public:
+ virtual bool put_by_index(u32 property_index, Value value) override
+ {
+ property_index += m_byte_offset / sizeof(T);
+ if (property_index >= m_array_length)
+ return Base::put_by_index(property_index, value);
+
+ if constexpr (sizeof(T) < 4) {
+ auto number = value.to_i32(global_object());
+ if (vm().exception())
+ return {};
+ data()[property_index] = number;
+ } else if constexpr (sizeof(T) == 4 || sizeof(T) == 8) {
+ auto number = value.to_double(global_object());
+ if (vm().exception())
+ return {};
+ data()[property_index] = number;
+ } else {
+ static_assert(DependentFalse<T>, "TypedArray::put_by_index with unhandled type size");
+ }
+ return true;
+ }
+
+ virtual Value get_by_index(u32 property_index) const override
+ {
+ property_index += m_byte_offset / sizeof(T);
+ if (property_index >= m_array_length)
+ return Base::get_by_index(property_index);
+
+ if constexpr (sizeof(T) < 4) {
+ return Value((i32)data()[property_index]);
+ } else if constexpr (sizeof(T) == 4 || sizeof(T) == 8) {
+ auto value = data()[property_index];
+ if constexpr (IsFloatingPoint<T>::value) {
+ return Value((double)value);
+ } else if constexpr (NumericLimits<T>::is_signed()) {
+ if (value > NumericLimits<i32>::max() || value < NumericLimits<i32>::min())
+ return Value((double)value);
+ } else {
+ if (value > NumericLimits<i32>::max())
+ return Value((double)value);
+ }
+ return Value((i32)value);
+ } else {
+ static_assert(DependentFalse<T>, "TypedArray::get_by_index with unhandled type size");
+ }
+ }
+
+ T* data() const { return reinterpret_cast<T*>(m_viewed_array_buffer->buffer().data()); }
+
+ virtual size_t element_size() const override { return sizeof(T); };
+
+protected:
+ TypedArray(ArrayBuffer& array_buffer, u32 array_length, Object& prototype)
+ : TypedArrayBase(prototype)
+ {
+ m_viewed_array_buffer = &array_buffer;
+ m_array_length = array_length;
+ m_byte_length = m_viewed_array_buffer->byte_length();
+ }
+
+ TypedArray(u32 array_length, Object& prototype)
+ : TypedArrayBase(prototype)
+ {
+ m_viewed_array_buffer = ArrayBuffer::create(global_object(), array_length * sizeof(T));
+ m_array_length = array_length;
+ m_byte_length = m_viewed_array_buffer->byte_length();
+ }
+
+private:
+ virtual bool is_typed_array() const final { return true; }
+};
+
+#define JS_DECLARE_TYPED_ARRAY(ClassName, snake_name, PrototypeName, ConstructorName, Type) \
+ class ClassName : public TypedArray<Type> { \
+ JS_OBJECT(ClassName, TypedArray); \
+ \
+ public: \
+ virtual ~ClassName(); \
+ static ClassName* create(GlobalObject&, u32 length); \
+ ClassName(u32 length, Object& prototype); \
+ }; \
+ class PrototypeName final : public Object { \
+ JS_OBJECT(PrototypeName, Object); \
+ \
+ public: \
+ PrototypeName(GlobalObject&); \
+ virtual ~PrototypeName() override; \
+ }; \
+ class ConstructorName final : public TypedArrayConstructor { \
+ JS_OBJECT(ConstructorName, TypedArrayConstructor); \
+ \
+ public: \
+ explicit ConstructorName(GlobalObject&); \
+ virtual void initialize(GlobalObject&) override; \
+ virtual ~ConstructorName() override; \
+ \
+ virtual Value call() override; \
+ virtual Value construct(Function& new_target) override; \
+ \
+ private: \
+ virtual bool has_constructor() const override { return true; } \
+ };
+
+#define __JS_ENUMERATE(ClassName, snake_name, PrototypeName, ConstructorName, Type) \
+ JS_DECLARE_TYPED_ARRAY(ClassName, snake_name, PrototypeName, ConstructorName, Type);
+JS_ENUMERATE_TYPED_ARRAYS
+#undef __JS_ENUMERATE
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/TypedArrayConstructor.cpp b/Userland/Libraries/LibJS/Runtime/TypedArrayConstructor.cpp
new file mode 100644
index 0000000000..d7d57cb50b
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/TypedArrayConstructor.cpp
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2020, Linus Groh <mail@linusgroh.de>
+ * 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/TypedArrayConstructor.h>
+
+namespace JS {
+
+TypedArrayConstructor::TypedArrayConstructor(const FlyString& name, Object& prototype)
+ : NativeFunction(name, prototype)
+{
+}
+
+TypedArrayConstructor::TypedArrayConstructor(GlobalObject& global_object)
+ : NativeFunction(vm().names.TypedArray, *global_object.function_prototype())
+{
+}
+
+void TypedArrayConstructor::initialize(GlobalObject& global_object)
+{
+ auto& vm = this->vm();
+ NativeFunction::initialize(global_object);
+ define_property(vm.names.prototype, global_object.typed_array_prototype(), 0);
+ define_property(vm.names.length, Value(0), Attribute::Configurable);
+}
+
+TypedArrayConstructor::~TypedArrayConstructor()
+{
+}
+
+Value TypedArrayConstructor::call()
+{
+ return construct(*this);
+}
+
+Value TypedArrayConstructor::construct(Function&)
+{
+ vm().throw_exception<TypeError>(global_object(), ErrorType::ClassIsAbstract, "TypedArray");
+ return {};
+}
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/TypedArrayConstructor.h b/Userland/Libraries/LibJS/Runtime/TypedArrayConstructor.h
new file mode 100644
index 0000000000..b99a957ae4
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/TypedArrayConstructor.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2020, Linus Groh <mail@linusgroh.de>
+ * 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/NativeFunction.h>
+
+namespace JS {
+
+class TypedArrayConstructor : public NativeFunction {
+ JS_OBJECT(TypedArrayConstructor, NativeFunction);
+
+public:
+ TypedArrayConstructor(const FlyString& name, Object& prototype);
+ explicit TypedArrayConstructor(GlobalObject&);
+ virtual void initialize(GlobalObject&) override;
+ virtual ~TypedArrayConstructor() override;
+
+ virtual Value call() override;
+ virtual Value construct(Function& new_target) override;
+
+private:
+ virtual bool has_constructor() const override { return true; }
+};
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/TypedArrayPrototype.cpp b/Userland/Libraries/LibJS/Runtime/TypedArrayPrototype.cpp
new file mode 100644
index 0000000000..da0c43faf1
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/TypedArrayPrototype.cpp
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2020, Linus Groh <mail@linusgroh.de>
+ * 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/TypedArray.h>
+#include <LibJS/Runtime/TypedArrayPrototype.h>
+
+namespace JS {
+
+TypedArrayPrototype::TypedArrayPrototype(GlobalObject& global_object)
+ : Object(*global_object.object_prototype())
+{
+}
+
+void TypedArrayPrototype::initialize(GlobalObject& object)
+{
+ auto& vm = this->vm();
+ Object::initialize(object);
+ // FIXME: This should be an accessor property
+ define_native_property(vm.names.length, length_getter, {}, Attribute::Configurable);
+}
+
+TypedArrayPrototype::~TypedArrayPrototype()
+{
+}
+
+static TypedArrayBase* typed_array_from(VM& vm, GlobalObject& global_object)
+{
+ auto* this_object = vm.this_value(global_object).to_object(global_object);
+ if (!this_object)
+ return nullptr;
+ if (!this_object->is_typed_array()) {
+ vm.throw_exception<TypeError>(global_object, ErrorType::NotA, "TypedArray");
+ return nullptr;
+ }
+ return static_cast<TypedArrayBase*>(this_object);
+}
+
+JS_DEFINE_NATIVE_GETTER(TypedArrayPrototype::length_getter)
+{
+ auto typed_array = typed_array_from(vm, global_object);
+ if (!typed_array)
+ return {};
+ return Value(typed_array->array_length());
+}
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/TypedArrayPrototype.h b/Userland/Libraries/LibJS/Runtime/TypedArrayPrototype.h
new file mode 100644
index 0000000000..8c39f31c31
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/TypedArrayPrototype.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2020, Linus Groh <mail@linusgroh.de>
+ * 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 TypedArrayPrototype final : public Object {
+ JS_OBJECT(TypedArrayPrototype, Object);
+
+public:
+ explicit TypedArrayPrototype(GlobalObject&);
+ virtual void initialize(GlobalObject&) override;
+ virtual ~TypedArrayPrototype() override;
+
+private:
+ JS_DECLARE_NATIVE_GETTER(length_getter);
+};
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/Uint8ClampedArray.cpp b/Userland/Libraries/LibJS/Runtime/Uint8ClampedArray.cpp
new file mode 100644
index 0000000000..c558f41119
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/Uint8ClampedArray.cpp
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * 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 <AK/Function.h>
+#include <LibJS/Runtime/Error.h>
+#include <LibJS/Runtime/GlobalObject.h>
+#include <LibJS/Runtime/Uint8ClampedArray.h>
+#include <string.h>
+
+namespace JS {
+
+Uint8ClampedArray* Uint8ClampedArray::create(GlobalObject& global_object, u32 length)
+{
+ return global_object.heap().allocate<Uint8ClampedArray>(global_object, length, *global_object.array_prototype());
+}
+
+Uint8ClampedArray::Uint8ClampedArray(u32 length, Object& prototype)
+ : Object(prototype)
+ , m_length(length)
+{
+ auto& vm = this->vm();
+ define_native_property(vm.names.length, length_getter, {});
+ m_data = (u8*)calloc(m_length, 1);
+}
+
+Uint8ClampedArray::~Uint8ClampedArray()
+{
+ ASSERT(m_data);
+ free(m_data);
+ m_data = nullptr;
+}
+
+JS_DEFINE_NATIVE_GETTER(Uint8ClampedArray::length_getter)
+{
+ auto* this_object = vm.this_value(global_object).to_object(global_object);
+ if (!this_object)
+ return {};
+ if (StringView(this_object->class_name()) != "Uint8ClampedArray") {
+ vm.throw_exception<TypeError>(global_object, ErrorType::NotA, "Uint8ClampedArray");
+ return {};
+ }
+ return Value(static_cast<const Uint8ClampedArray*>(this_object)->length());
+}
+
+bool Uint8ClampedArray::put_by_index(u32 property_index, Value value)
+{
+ if (property_index >= m_length)
+ return Base::put_by_index(property_index, value);
+ auto number = value.to_i32(global_object());
+ if (vm().exception())
+ return {};
+ m_data[property_index] = clamp(number, 0, 255);
+ return true;
+}
+
+Value Uint8ClampedArray::get_by_index(u32 property_index) const
+{
+ if (property_index >= m_length)
+ return Base::get_by_index(property_index);
+ return Value((i32)m_data[property_index]);
+}
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/Uint8ClampedArray.h b/Userland/Libraries/LibJS/Runtime/Uint8ClampedArray.h
new file mode 100644
index 0000000000..056d490eab
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/Uint8ClampedArray.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * 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 Uint8ClampedArray final : public Object {
+ JS_OBJECT(Uint8ClampedArray, Object);
+
+public:
+ static Uint8ClampedArray* create(GlobalObject&, u32 length);
+
+ Uint8ClampedArray(u32 length, Object& prototype);
+ virtual ~Uint8ClampedArray() override;
+
+ i32 length() const { return m_length; }
+
+ virtual bool put_by_index(u32 property_index, Value value) override;
+ virtual Value get_by_index(u32 property_index) const override;
+
+ u8* data() { return m_data; }
+ const u8* data() const { return m_data; }
+
+private:
+ JS_DECLARE_NATIVE_GETTER(length_getter);
+
+ u8* m_data { nullptr };
+ u32 m_length { 0 };
+};
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/VM.cpp b/Userland/Libraries/LibJS/Runtime/VM.cpp
new file mode 100644
index 0000000000..73842974c7
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/VM.cpp
@@ -0,0 +1,365 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * 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 <AK/ScopeGuard.h>
+#include <AK/StringBuilder.h>
+#include <LibJS/Interpreter.h>
+#include <LibJS/Runtime/Array.h>
+#include <LibJS/Runtime/Error.h>
+#include <LibJS/Runtime/GlobalObject.h>
+#include <LibJS/Runtime/Reference.h>
+#include <LibJS/Runtime/ScriptFunction.h>
+#include <LibJS/Runtime/Symbol.h>
+#include <LibJS/Runtime/VM.h>
+
+namespace JS {
+
+NonnullRefPtr<VM> VM::create()
+{
+ return adopt(*new VM);
+}
+
+VM::VM()
+ : m_heap(*this)
+{
+ m_empty_string = m_heap.allocate_without_global_object<PrimitiveString>(String::empty());
+ for (size_t i = 0; i < 128; ++i) {
+ m_single_ascii_character_strings[i] = m_heap.allocate_without_global_object<PrimitiveString>(String::formatted("{:c}", i));
+ }
+
+ m_scope_object_shape = m_heap.allocate_without_global_object<Shape>(Shape::ShapeWithoutGlobalObjectTag::Tag);
+
+#define __JS_ENUMERATE(SymbolName, snake_name) \
+ m_well_known_symbol_##snake_name = js_symbol(*this, "Symbol." #SymbolName, false);
+ JS_ENUMERATE_WELL_KNOWN_SYMBOLS
+#undef __JS_ENUMERATE
+}
+
+VM::~VM()
+{
+}
+
+Interpreter& VM::interpreter()
+{
+ ASSERT(!m_interpreters.is_empty());
+ return *m_interpreters.last();
+}
+
+Interpreter* VM::interpreter_if_exists()
+{
+ if (m_interpreters.is_empty())
+ return nullptr;
+ return m_interpreters.last();
+}
+
+void VM::push_interpreter(Interpreter& interpreter)
+{
+ m_interpreters.append(&interpreter);
+}
+
+void VM::pop_interpreter(Interpreter& interpreter)
+{
+ ASSERT(!m_interpreters.is_empty());
+ auto* popped_interpreter = m_interpreters.take_last();
+ ASSERT(popped_interpreter == &interpreter);
+}
+
+VM::InterpreterExecutionScope::InterpreterExecutionScope(Interpreter& interpreter)
+ : m_interpreter(interpreter)
+{
+ m_interpreter.vm().push_interpreter(m_interpreter);
+}
+
+VM::InterpreterExecutionScope::~InterpreterExecutionScope()
+{
+ m_interpreter.vm().pop_interpreter(m_interpreter);
+}
+
+void VM::gather_roots(HashTable<Cell*>& roots)
+{
+ roots.set(m_empty_string);
+ for (auto* string : m_single_ascii_character_strings)
+ roots.set(string);
+
+ roots.set(m_scope_object_shape);
+ roots.set(m_exception);
+
+ if (m_last_value.is_cell())
+ roots.set(m_last_value.as_cell());
+
+ for (auto& call_frame : m_call_stack) {
+ if (call_frame->this_value.is_cell())
+ roots.set(call_frame->this_value.as_cell());
+ roots.set(call_frame->arguments_object);
+ for (auto& argument : call_frame->arguments) {
+ if (argument.is_cell())
+ roots.set(argument.as_cell());
+ }
+ roots.set(call_frame->scope);
+ }
+
+#define __JS_ENUMERATE(SymbolName, snake_name) \
+ roots.set(well_known_symbol_##snake_name());
+ JS_ENUMERATE_WELL_KNOWN_SYMBOLS
+#undef __JS_ENUMERATE
+
+ for (auto& symbol : m_global_symbol_map)
+ roots.set(symbol.value);
+}
+
+Symbol* VM::get_global_symbol(const String& description)
+{
+ auto result = m_global_symbol_map.get(description);
+ if (result.has_value())
+ return result.value();
+
+ auto new_global_symbol = js_symbol(*this, description, true);
+ m_global_symbol_map.set(description, new_global_symbol);
+ return new_global_symbol;
+}
+
+void VM::set_variable(const FlyString& name, Value value, GlobalObject& global_object, bool first_assignment)
+{
+ if (m_call_stack.size()) {
+ for (auto* scope = current_scope(); scope; scope = scope->parent()) {
+ auto possible_match = scope->get_from_scope(name);
+ if (possible_match.has_value()) {
+ if (!first_assignment && possible_match.value().declaration_kind == DeclarationKind::Const) {
+ throw_exception<TypeError>(global_object, ErrorType::InvalidAssignToConst);
+ return;
+ }
+
+ scope->put_to_scope(name, { value, possible_match.value().declaration_kind });
+ return;
+ }
+ }
+ }
+
+ global_object.put(move(name), move(value));
+}
+
+Value VM::get_variable(const FlyString& name, GlobalObject& global_object)
+{
+ if (m_call_stack.size()) {
+ if (name == names.arguments) {
+ // HACK: Special handling for the name "arguments":
+ // If the name "arguments" is defined in the current scope, for example via
+ // a function parameter, or by a local var declaration, we use that.
+ // Otherwise, we return a lazily constructed Array with all the argument values.
+ // FIXME: Do something much more spec-compliant.
+ auto possible_match = current_scope()->get_from_scope(name);
+ if (possible_match.has_value())
+ return possible_match.value().value;
+ if (!call_frame().arguments_object) {
+ call_frame().arguments_object = Array::create(global_object);
+ for (auto argument : call_frame().arguments) {
+ call_frame().arguments_object->indexed_properties().append(argument);
+ }
+ }
+ return call_frame().arguments_object;
+ }
+
+ for (auto* scope = current_scope(); scope; scope = scope->parent()) {
+ auto possible_match = scope->get_from_scope(name);
+ if (possible_match.has_value())
+ return possible_match.value().value;
+ }
+ }
+ auto value = global_object.get(name);
+ if (m_underscore_is_last_value && name == "_" && value.is_empty())
+ return m_last_value;
+ return value;
+}
+
+Reference VM::get_reference(const FlyString& name)
+{
+ if (m_call_stack.size()) {
+ for (auto* scope = current_scope(); scope; scope = scope->parent()) {
+ if (is<GlobalObject>(scope))
+ break;
+ auto possible_match = scope->get_from_scope(name);
+ if (possible_match.has_value())
+ return { Reference::LocalVariable, name };
+ }
+ }
+ return { Reference::GlobalVariable, name };
+}
+
+Value VM::construct(Function& function, Function& new_target, Optional<MarkedValueList> arguments, GlobalObject& global_object)
+{
+ CallFrame call_frame;
+ call_frame.is_strict_mode = function.is_strict_mode();
+
+ push_call_frame(call_frame, function.global_object());
+ if (exception())
+ return {};
+ ArmedScopeGuard call_frame_popper = [&] {
+ pop_call_frame();
+ };
+
+ call_frame.function_name = function.name();
+ call_frame.arguments = function.bound_arguments();
+ if (arguments.has_value())
+ call_frame.arguments.append(arguments.value().values());
+ auto* environment = function.create_environment();
+ call_frame.scope = environment;
+ environment->set_new_target(&new_target);
+
+ Object* new_object = nullptr;
+ if (function.constructor_kind() == Function::ConstructorKind::Base) {
+ new_object = Object::create_empty(global_object);
+ environment->bind_this_value(global_object, new_object);
+ if (exception())
+ return {};
+ auto prototype = new_target.get(names.prototype);
+ if (exception())
+ return {};
+ if (prototype.is_object()) {
+ new_object->set_prototype(&prototype.as_object());
+ if (exception())
+ return {};
+ }
+ }
+
+ // If we are a Derived constructor, |this| has not been constructed before super is called.
+ Value this_value = function.constructor_kind() == Function::ConstructorKind::Base ? new_object : Value {};
+ call_frame.this_value = this_value;
+ auto result = function.construct(new_target);
+
+ this_value = call_frame.scope->get_this_binding(global_object);
+ pop_call_frame();
+ call_frame_popper.disarm();
+
+ // If we are constructing an instance of a derived class,
+ // set the prototype on objects created by constructors that return an object (i.e. NativeFunction subclasses).
+ if (function.constructor_kind() == Function::ConstructorKind::Base && new_target.constructor_kind() == Function::ConstructorKind::Derived && result.is_object()) {
+ ASSERT(is<LexicalEnvironment>(current_scope()));
+ static_cast<LexicalEnvironment*>(current_scope())->replace_this_binding(result);
+ auto prototype = new_target.get(names.prototype);
+ if (exception())
+ return {};
+ if (prototype.is_object()) {
+ result.as_object().set_prototype(&prototype.as_object());
+ if (exception())
+ return {};
+ }
+ return result;
+ }
+
+ if (exception())
+ return {};
+
+ if (result.is_object())
+ return result;
+
+ return this_value;
+}
+
+void VM::throw_exception(Exception* exception)
+{
+ if (should_log_exceptions() && exception->value().is_object() && is<Error>(exception->value().as_object())) {
+ auto& error = static_cast<Error&>(exception->value().as_object());
+ dbgln("Throwing JavaScript Error: {}, {}", error.name(), error.message());
+
+ for (ssize_t i = m_call_stack.size() - 1; i >= 0; --i) {
+ auto function_name = m_call_stack[i]->function_name;
+ if (function_name.is_empty())
+ function_name = "<anonymous>";
+ dbgln(" {}", function_name);
+ }
+ }
+
+ m_exception = exception;
+ unwind(ScopeType::Try);
+}
+
+String VM::join_arguments() const
+{
+ StringBuilder joined_arguments;
+ for (size_t i = 0; i < argument_count(); ++i) {
+ joined_arguments.append(argument(i).to_string_without_side_effects().characters());
+ if (i != argument_count() - 1)
+ joined_arguments.append(' ');
+ }
+ return joined_arguments.build();
+}
+
+Value VM::resolve_this_binding(GlobalObject& global_object) const
+{
+ return find_this_scope()->get_this_binding(global_object);
+}
+
+const ScopeObject* VM::find_this_scope() const
+{
+ // We will always return because the Global environment will always be reached, which has a |this| binding.
+ for (auto* scope = current_scope(); scope; scope = scope->parent()) {
+ if (scope->has_this_binding())
+ return scope;
+ }
+ ASSERT_NOT_REACHED();
+}
+
+Value VM::get_new_target() const
+{
+ ASSERT(is<LexicalEnvironment>(find_this_scope()));
+ return static_cast<const LexicalEnvironment*>(find_this_scope())->new_target();
+}
+
+Value VM::call_internal(Function& function, Value this_value, Optional<MarkedValueList> arguments)
+{
+ ASSERT(!exception());
+
+ CallFrame call_frame;
+ call_frame.is_strict_mode = function.is_strict_mode();
+ call_frame.function_name = function.name();
+ call_frame.this_value = function.bound_this().value_or(this_value);
+ call_frame.arguments = function.bound_arguments();
+ if (arguments.has_value())
+ call_frame.arguments.append(move(arguments.release_value().values()));
+ auto* environment = function.create_environment();
+ call_frame.scope = environment;
+
+ ASSERT(environment->this_binding_status() == LexicalEnvironment::ThisBindingStatus::Uninitialized);
+ environment->bind_this_value(function.global_object(), call_frame.this_value);
+ if (exception())
+ return {};
+
+ push_call_frame(call_frame, function.global_object());
+ if (exception())
+ return {};
+ auto result = function.call();
+ pop_call_frame();
+ return result;
+}
+
+bool VM::in_strict_mode() const
+{
+ if (call_stack().is_empty())
+ return false;
+ return call_frame().is_strict_mode;
+}
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/VM.h b/Userland/Libraries/LibJS/Runtime/VM.h
new file mode 100644
index 0000000000..320ea53405
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/VM.h
@@ -0,0 +1,290 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * 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 <AK/FlyString.h>
+#include <AK/HashMap.h>
+#include <AK/RefCounted.h>
+#include <AK/StackInfo.h>
+#include <LibJS/Heap/Heap.h>
+#include <LibJS/Runtime/CommonPropertyNames.h>
+#include <LibJS/Runtime/Error.h>
+#include <LibJS/Runtime/ErrorTypes.h>
+#include <LibJS/Runtime/Exception.h>
+#include <LibJS/Runtime/MarkedValueList.h>
+#include <LibJS/Runtime/Value.h>
+
+namespace JS {
+
+enum class ScopeType {
+ None,
+ Function,
+ Block,
+ Try,
+ Breakable,
+ Continuable,
+};
+
+struct ScopeFrame {
+ ScopeType type;
+ NonnullRefPtr<ScopeNode> scope_node;
+ bool pushed_environment { false };
+};
+
+struct CallFrame {
+ FlyString function_name;
+ Value this_value;
+ Vector<Value> arguments;
+ Array* arguments_object { nullptr };
+ ScopeObject* scope { nullptr };
+ bool is_strict_mode { false };
+};
+
+class VM : public RefCounted<VM> {
+public:
+ static NonnullRefPtr<VM> create();
+ ~VM();
+
+ bool should_log_exceptions() const { return m_should_log_exceptions; }
+ void set_should_log_exceptions(bool b) { m_should_log_exceptions = b; }
+
+ Heap& heap() { return m_heap; }
+ const Heap& heap() const { return m_heap; }
+
+ Interpreter& interpreter();
+ Interpreter* interpreter_if_exists();
+
+ void push_interpreter(Interpreter&);
+ void pop_interpreter(Interpreter&);
+
+ Exception* exception()
+ {
+ return m_exception;
+ }
+
+ void clear_exception() { m_exception = nullptr; }
+
+ class InterpreterExecutionScope {
+ public:
+ InterpreterExecutionScope(Interpreter&);
+ ~InterpreterExecutionScope();
+
+ private:
+ Interpreter& m_interpreter;
+ };
+
+ void gather_roots(HashTable<Cell*>&);
+
+#define __JS_ENUMERATE(SymbolName, snake_name) \
+ Symbol* well_known_symbol_##snake_name() const { return m_well_known_symbol_##snake_name; }
+ JS_ENUMERATE_WELL_KNOWN_SYMBOLS
+#undef __JS_ENUMERATE
+
+ Symbol* get_global_symbol(const String& description);
+
+ PrimitiveString& empty_string() { return *m_empty_string; }
+ PrimitiveString& single_ascii_character_string(u8 character)
+ {
+ ASSERT(character < 0x80);
+ return *m_single_ascii_character_strings[character];
+ }
+
+ void push_call_frame(CallFrame& call_frame, GlobalObject& global_object)
+ {
+ ASSERT(!exception());
+ // Ensure we got some stack space left, so the next function call doesn't kill us.
+ // This value is merely a guess and might need tweaking at a later point.
+ if (m_stack_info.size_free() < 16 * KiB)
+ throw_exception<Error>(global_object, "RuntimeError", "Call stack size limit exceeded");
+ else
+ m_call_stack.append(&call_frame);
+ }
+
+ void pop_call_frame() { m_call_stack.take_last(); }
+
+ void push_ast_node(const ASTNode& node) { m_ast_nodes.append(&node); }
+ void pop_ast_node() { m_ast_nodes.take_last(); }
+
+ CallFrame& call_frame() { return *m_call_stack.last(); }
+ const CallFrame& call_frame() const { return *m_call_stack.last(); }
+ const Vector<CallFrame*>& call_stack() const { return m_call_stack; }
+ Vector<CallFrame*>& call_stack() { return m_call_stack; }
+ const Vector<const ASTNode*>& node_stack() const { return m_ast_nodes; }
+
+ const ScopeObject* current_scope() const { return call_frame().scope; }
+ ScopeObject* current_scope() { return call_frame().scope; }
+
+ bool in_strict_mode() const;
+
+ template<typename Callback>
+ void for_each_argument(Callback callback)
+ {
+ if (m_call_stack.is_empty())
+ return;
+ for (auto& value : call_frame().arguments)
+ callback(value);
+ }
+
+ size_t argument_count() const
+ {
+ if (m_call_stack.is_empty())
+ return 0;
+ return call_frame().arguments.size();
+ }
+
+ Value argument(size_t index) const
+ {
+ if (m_call_stack.is_empty())
+ return {};
+ auto& arguments = call_frame().arguments;
+ return index < arguments.size() ? arguments[index] : js_undefined();
+ }
+
+ Value this_value(Object& global_object) const
+ {
+ if (m_call_stack.is_empty())
+ return &global_object;
+ return call_frame().this_value;
+ }
+
+ Value last_value() const { return m_last_value; }
+ void set_last_value(Badge<Interpreter>, Value value) { m_last_value = value; }
+
+ const StackInfo& stack_info() const { return m_stack_info; };
+
+ bool underscore_is_last_value() const { return m_underscore_is_last_value; }
+ void set_underscore_is_last_value(bool b) { m_underscore_is_last_value = b; }
+
+ void unwind(ScopeType type, FlyString label = {})
+ {
+ m_unwind_until = type;
+ m_unwind_until_label = label;
+ }
+ void stop_unwind() { m_unwind_until = ScopeType::None; }
+ bool should_unwind_until(ScopeType type, FlyString label = {}) const
+ {
+ if (m_unwind_until_label.is_null())
+ return m_unwind_until == type;
+ return m_unwind_until == type && m_unwind_until_label == label;
+ }
+ bool should_unwind() const { return m_unwind_until != ScopeType::None; }
+
+ ScopeType unwind_until() const { return m_unwind_until; }
+
+ Value get_variable(const FlyString& name, GlobalObject&);
+ void set_variable(const FlyString& name, Value, GlobalObject&, bool first_assignment = false);
+
+ Reference get_reference(const FlyString& name);
+
+ template<typename T, typename... Args>
+ void throw_exception(GlobalObject& global_object, Args&&... args)
+ {
+ return throw_exception(global_object, T::create(global_object, forward<Args>(args)...));
+ }
+
+ void throw_exception(Exception*);
+ void throw_exception(GlobalObject& global_object, Value value)
+ {
+ return throw_exception(heap().allocate<Exception>(global_object, value));
+ }
+
+ template<typename T, typename... Args>
+ void throw_exception(GlobalObject& global_object, ErrorType type, Args&&... args)
+ {
+ return throw_exception(global_object, T::create(global_object, String::formatted(type.message(), forward<Args>(args)...)));
+ }
+
+ Value construct(Function&, Function& new_target, Optional<MarkedValueList> arguments, GlobalObject&);
+
+ String join_arguments() const;
+
+ Value resolve_this_binding(GlobalObject&) const;
+ const ScopeObject* find_this_scope() const;
+ Value get_new_target() const;
+
+ template<typename... Args>
+ [[nodiscard]] ALWAYS_INLINE Value call(Function& function, Value this_value, Args... args)
+ {
+ if constexpr (sizeof...(Args) > 0) {
+ MarkedValueList arglist { heap() };
+ (..., arglist.append(move(args)));
+ return call(function, this_value, move(arglist));
+ }
+
+ return call(function, this_value);
+ }
+
+ CommonPropertyNames names;
+
+ Shape& scope_object_shape() { return *m_scope_object_shape; }
+
+private:
+ VM();
+
+ [[nodiscard]] Value call_internal(Function&, Value this_value, Optional<MarkedValueList> arguments);
+
+ Exception* m_exception { nullptr };
+
+ Heap m_heap;
+ Vector<Interpreter*> m_interpreters;
+
+ Vector<CallFrame*> m_call_stack;
+ Vector<const ASTNode*> m_ast_nodes;
+
+ Value m_last_value;
+ ScopeType m_unwind_until { ScopeType::None };
+ FlyString m_unwind_until_label;
+
+ StackInfo m_stack_info;
+
+ bool m_underscore_is_last_value { false };
+
+ HashMap<String, Symbol*> m_global_symbol_map;
+
+ PrimitiveString* m_empty_string { nullptr };
+ PrimitiveString* m_single_ascii_character_strings[128] {};
+
+#define __JS_ENUMERATE(SymbolName, snake_name) \
+ Symbol* m_well_known_symbol_##snake_name { nullptr };
+ JS_ENUMERATE_WELL_KNOWN_SYMBOLS
+#undef __JS_ENUMERATE
+
+ Shape* m_scope_object_shape { nullptr };
+
+ bool m_should_log_exceptions { false };
+};
+
+template<>
+[[nodiscard]] ALWAYS_INLINE Value VM::call(Function& function, Value this_value, MarkedValueList arguments) { return call_internal(function, this_value, move(arguments)); }
+
+template<>
+[[nodiscard]] ALWAYS_INLINE Value VM::call(Function& function, Value this_value, Optional<MarkedValueList> arguments) { return call_internal(function, this_value, move(arguments)); }
+
+template<>
+[[nodiscard]] ALWAYS_INLINE Value VM::call(Function& function, Value this_value) { return call(function, this_value, Optional<MarkedValueList> {}); }
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/Value.cpp b/Userland/Libraries/LibJS/Runtime/Value.cpp
new file mode 100644
index 0000000000..1d397c9536
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/Value.cpp
@@ -0,0 +1,1221 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * Copyright (c) 2020, Linus Groh <mail@linusgroh.de>
+ * 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 <AK/FlyString.h>
+#include <AK/String.h>
+#include <AK/StringBuilder.h>
+#include <AK/Utf8View.h>
+#include <LibCrypto/BigInt/SignedBigInteger.h>
+#include <LibCrypto/NumberTheory/ModularFunctions.h>
+#include <LibJS/Heap/Heap.h>
+#include <LibJS/Runtime/Accessor.h>
+#include <LibJS/Runtime/Array.h>
+#include <LibJS/Runtime/BigInt.h>
+#include <LibJS/Runtime/BigIntObject.h>
+#include <LibJS/Runtime/BooleanObject.h>
+#include <LibJS/Runtime/BoundFunction.h>
+#include <LibJS/Runtime/Error.h>
+#include <LibJS/Runtime/Function.h>
+#include <LibJS/Runtime/GlobalObject.h>
+#include <LibJS/Runtime/NumberObject.h>
+#include <LibJS/Runtime/Object.h>
+#include <LibJS/Runtime/PrimitiveString.h>
+#include <LibJS/Runtime/RegExpObject.h>
+#include <LibJS/Runtime/StringObject.h>
+#include <LibJS/Runtime/Symbol.h>
+#include <LibJS/Runtime/SymbolObject.h>
+#include <LibJS/Runtime/Value.h>
+#include <ctype.h>
+#include <math.h>
+
+namespace JS {
+
+// Used in various abstract operations to make it obvious when a non-optional return value must be discarded.
+static const double INVALID { 0 };
+
+static const Crypto::SignedBigInteger BIGINT_ZERO { 0 };
+
+static bool is_valid_bigint_value(String string)
+{
+ string = string.trim_whitespace();
+ if (string.length() > 1 && (string[0] == '-' || string[0] == '+'))
+ string = string.substring_view(1, string.length() - 1);
+ for (auto& ch : string) {
+ if (!isdigit(ch))
+ return false;
+ }
+ return true;
+}
+
+ALWAYS_INLINE bool both_number(const Value& lhs, const Value& rhs)
+{
+ return lhs.is_number() && rhs.is_number();
+}
+
+ALWAYS_INLINE bool both_bigint(const Value& lhs, const Value& rhs)
+{
+ return lhs.is_bigint() && rhs.is_bigint();
+}
+
+static String double_to_string(double d)
+{
+ // https://tc39.es/ecma262/#sec-numeric-types-number-tostring
+ if (isnan(d))
+ return "NaN";
+ if (d == +0.0 || d == -0.0)
+ return "0";
+ if (d < +0.0) {
+ StringBuilder builder;
+ builder.append('-');
+ builder.append(double_to_string(-d));
+ return builder.to_string();
+ }
+ if (d == INFINITY)
+ return "Infinity";
+
+ StringBuilder number_string_builder;
+
+ size_t start_index = 0;
+ size_t end_index = 0;
+ size_t intpart_end = 0;
+
+ // generate integer part (reversed)
+ double intPart;
+ double frac_part;
+ frac_part = modf(d, &intPart);
+ while (intPart > 0) {
+ number_string_builder.append('0' + (int)fmod(intPart, 10));
+ end_index++;
+ intPart = floor(intPart / 10);
+ }
+
+ auto reversed_integer_part = number_string_builder.to_string().reverse();
+ number_string_builder.clear();
+ number_string_builder.append(reversed_integer_part);
+
+ intpart_end = end_index;
+
+ int exponent = 0;
+
+ // generate fractional part
+ while (frac_part > 0) {
+ double old_frac_part = frac_part;
+ frac_part *= 10;
+ frac_part = modf(frac_part, &intPart);
+ if (old_frac_part == frac_part)
+ break;
+ number_string_builder.append('0' + (int)intPart);
+ end_index++;
+ exponent--;
+ }
+
+ auto number_string = number_string_builder.to_string();
+
+ // FIXME: Remove this hack.
+ // FIXME: Instead find the shortest round-trippable representation.
+ // Remove decimals after the 15th position
+ if (end_index > intpart_end + 15) {
+ exponent += end_index - intpart_end - 15;
+ end_index = intpart_end + 15;
+ }
+
+ // remove leading zeroes
+ while (start_index < end_index && number_string[start_index] == '0') {
+ start_index++;
+ }
+
+ // remove trailing zeroes
+ while (end_index > 0 && number_string[end_index - 1] == '0') {
+ end_index--;
+ exponent++;
+ }
+
+ if (end_index <= start_index)
+ return "0";
+
+ auto digits = number_string.substring_view(start_index, end_index - start_index);
+
+ int number_of_digits = end_index - start_index;
+
+ exponent += number_of_digits;
+
+ StringBuilder builder;
+
+ if (number_of_digits <= exponent && exponent <= 21) {
+ builder.append(digits);
+ builder.append(String::repeated('0', exponent - number_of_digits));
+ return builder.to_string();
+ }
+ if (0 < exponent && exponent <= 21) {
+ builder.append(digits.substring_view(0, exponent));
+ builder.append('.');
+ builder.append(digits.substring_view(exponent));
+ return builder.to_string();
+ }
+ if (-6 < exponent && exponent <= 0) {
+ builder.append("0.");
+ builder.append(String::repeated('0', -exponent));
+ builder.append(digits);
+ return builder.to_string();
+ }
+ if (number_of_digits == 1) {
+ builder.append(digits);
+ builder.append('e');
+
+ if (exponent - 1 > 0)
+ builder.append('+');
+ else
+ builder.append('-');
+
+ builder.append(String::format("%d", abs(exponent - 1)));
+ return builder.to_string();
+ }
+
+ builder.append(digits[0]);
+ builder.append('.');
+ builder.append(digits.substring_view(1));
+ builder.append('e');
+
+ if (exponent - 1 > 0)
+ builder.append('+');
+ else
+ builder.append('-');
+
+ builder.append(String::format("%d", abs(exponent - 1)));
+ return builder.to_string();
+}
+
+bool Value::is_array() const
+{
+ return is_object() && as_object().is_array();
+}
+
+Array& Value::as_array()
+{
+ ASSERT(is_array());
+ return static_cast<Array&>(*m_value.as_object);
+}
+
+bool Value::is_function() const
+{
+ return is_object() && as_object().is_function();
+}
+
+Function& Value::as_function()
+{
+ ASSERT(is_function());
+ return static_cast<Function&>(as_object());
+}
+
+bool Value::is_regexp(GlobalObject& global_object) const
+{
+ // 7.2.8 IsRegExp, https://tc39.es/ecma262/#sec-isregexp
+
+ if (!is_object())
+ return false;
+
+ auto matcher = as_object().get(global_object.vm().well_known_symbol_match());
+ if (global_object.vm().exception())
+ return false;
+ if (!matcher.is_empty() && !matcher.is_undefined())
+ return matcher.to_boolean();
+
+ return is<RegExpObject>(as_object());
+}
+
+String Value::to_string_without_side_effects() const
+{
+ switch (m_type) {
+ case Type::Undefined:
+ return "undefined";
+ case Type::Null:
+ return "null";
+ case Type::Boolean:
+ return m_value.as_bool ? "true" : "false";
+ case Type::Number:
+ return double_to_string(m_value.as_double);
+ case Type::String:
+ return m_value.as_string->string();
+ case Type::Symbol:
+ return m_value.as_symbol->to_string();
+ case Type::BigInt:
+ return m_value.as_bigint->to_string();
+ case Type::Object:
+ return String::formatted("[object {}]", as_object().class_name());
+ case Type::Accessor:
+ return "<accessor>";
+ case Type::NativeProperty:
+ return "<native-property>";
+ default:
+ ASSERT_NOT_REACHED();
+ }
+}
+
+PrimitiveString* Value::to_primitive_string(GlobalObject& global_object)
+{
+ if (is_string())
+ return &as_string();
+ auto string = to_string(global_object);
+ if (global_object.vm().exception())
+ return nullptr;
+ return js_string(global_object.heap(), string);
+}
+
+String Value::to_string(GlobalObject& global_object, bool legacy_null_to_empty_string) const
+{
+ switch (m_type) {
+ case Type::Undefined:
+ return "undefined";
+ case Type::Null:
+ return !legacy_null_to_empty_string ? "null" : String::empty();
+ case Type::Boolean:
+ return m_value.as_bool ? "true" : "false";
+ case Type::Number:
+ return double_to_string(m_value.as_double);
+ case Type::String:
+ return m_value.as_string->string();
+ case Type::Symbol:
+ global_object.vm().throw_exception<TypeError>(global_object, ErrorType::Convert, "symbol", "string");
+ return {};
+ case Type::BigInt:
+ return m_value.as_bigint->big_integer().to_base10();
+ case Type::Object: {
+ auto primitive_value = to_primitive(PreferredType::String);
+ if (global_object.vm().exception())
+ return {};
+ return primitive_value.to_string(global_object);
+ }
+ default:
+ ASSERT_NOT_REACHED();
+ }
+}
+
+bool Value::to_boolean() const
+{
+ switch (m_type) {
+ case Type::Undefined:
+ case Type::Null:
+ return false;
+ case Type::Boolean:
+ return m_value.as_bool;
+ case Type::Number:
+ if (is_nan())
+ return false;
+ return m_value.as_double != 0;
+ case Type::String:
+ return !m_value.as_string->string().is_empty();
+ case Type::Symbol:
+ return true;
+ case Type::BigInt:
+ return m_value.as_bigint->big_integer() != BIGINT_ZERO;
+ case Type::Object:
+ return true;
+ default:
+ ASSERT_NOT_REACHED();
+ }
+}
+
+Value Value::to_primitive(PreferredType preferred_type) const
+{
+ if (is_object()) {
+ // FIXME: Also support @@toPrimitive
+ if (preferred_type == PreferredType::Default)
+ preferred_type = PreferredType::Number;
+ return as_object().ordinary_to_primitive(preferred_type);
+ }
+ return *this;
+}
+
+Object* Value::to_object(GlobalObject& global_object) const
+{
+ switch (m_type) {
+ case Type::Undefined:
+ case Type::Null:
+ global_object.vm().throw_exception<TypeError>(global_object, ErrorType::ToObjectNullOrUndef);
+ return nullptr;
+ case Type::Boolean:
+ return BooleanObject::create(global_object, m_value.as_bool);
+ case Type::Number:
+ return NumberObject::create(global_object, m_value.as_double);
+ case Type::String:
+ return StringObject::create(global_object, *m_value.as_string);
+ case Type::Symbol:
+ return SymbolObject::create(global_object, *m_value.as_symbol);
+ case Type::BigInt:
+ return BigIntObject::create(global_object, *m_value.as_bigint);
+ case Type::Object:
+ return &const_cast<Object&>(as_object());
+ default:
+ dbgln("Dying because I can't to_object() on {}", *this);
+ ASSERT_NOT_REACHED();
+ }
+}
+
+Value Value::to_numeric(GlobalObject& global_object) const
+{
+ auto primitive = to_primitive(Value::PreferredType::Number);
+ if (global_object.vm().exception())
+ return {};
+ if (primitive.is_bigint())
+ return primitive;
+ return primitive.to_number(global_object);
+}
+
+Value Value::to_number(GlobalObject& global_object) const
+{
+ switch (m_type) {
+ case Type::Undefined:
+ return js_nan();
+ case Type::Null:
+ return Value(0);
+ case Type::Boolean:
+ return Value(m_value.as_bool ? 1 : 0);
+ case Type::Number:
+ return Value(m_value.as_double);
+ case Type::String: {
+ auto string = as_string().string().trim_whitespace();
+ if (string.is_empty())
+ return Value(0);
+ if (string == "Infinity" || string == "+Infinity")
+ return js_infinity();
+ if (string == "-Infinity")
+ return js_negative_infinity();
+ char* endptr;
+ auto parsed_double = strtod(string.characters(), &endptr);
+ if (*endptr)
+ return js_nan();
+ return Value(parsed_double);
+ }
+ case Type::Symbol:
+ global_object.vm().throw_exception<TypeError>(global_object, ErrorType::Convert, "symbol", "number");
+ return {};
+ case Type::BigInt:
+ global_object.vm().throw_exception<TypeError>(global_object, ErrorType::Convert, "BigInt", "number");
+ return {};
+ case Type::Object: {
+ auto primitive = to_primitive(PreferredType::Number);
+ if (global_object.vm().exception())
+ return {};
+ return primitive.to_number(global_object);
+ }
+ default:
+ ASSERT_NOT_REACHED();
+ }
+}
+
+BigInt* Value::to_bigint(GlobalObject& global_object) const
+{
+ auto& vm = global_object.vm();
+ auto primitive = to_primitive(PreferredType::Number);
+ if (vm.exception())
+ return nullptr;
+ switch (primitive.type()) {
+ case Type::Undefined:
+ vm.throw_exception<TypeError>(global_object, ErrorType::Convert, "undefined", "BigInt");
+ return nullptr;
+ case Type::Null:
+ vm.throw_exception<TypeError>(global_object, ErrorType::Convert, "null", "BigInt");
+ return nullptr;
+ case Type::Boolean: {
+ auto value = primitive.as_bool() ? 1 : 0;
+ return js_bigint(vm.heap(), Crypto::SignedBigInteger { value });
+ }
+ case Type::BigInt:
+ return &primitive.as_bigint();
+ case Type::Number:
+ vm.throw_exception<TypeError>(global_object, ErrorType::Convert, "number", "BigInt");
+ return {};
+ case Type::String: {
+ auto& string = primitive.as_string().string();
+ if (!is_valid_bigint_value(string)) {
+ vm.throw_exception<SyntaxError>(global_object, ErrorType::BigIntInvalidValue, string);
+ return {};
+ }
+ return js_bigint(vm.heap(), Crypto::SignedBigInteger::from_base10(string.trim_whitespace()));
+ }
+ case Type::Symbol:
+ vm.throw_exception<TypeError>(global_object, ErrorType::Convert, "symbol", "BigInt");
+ return {};
+ default:
+ ASSERT_NOT_REACHED();
+ }
+}
+
+i32 Value::as_i32() const
+{
+ return static_cast<i32>(as_double());
+}
+
+u32 Value::as_u32() const
+{
+ ASSERT(as_double() >= 0);
+ return min((double)as_i32(), MAX_U32);
+}
+
+size_t Value::as_size_t() const
+{
+ ASSERT(as_double() >= 0);
+ return min((double)as_i32(), MAX_ARRAY_LIKE_INDEX);
+}
+
+double Value::to_double(GlobalObject& global_object) const
+{
+ auto number = to_number(global_object);
+ if (global_object.vm().exception())
+ return INVALID;
+ return number.as_double();
+}
+
+i32 Value::to_i32(GlobalObject& global_object) const
+{
+ auto number = to_number(global_object);
+ if (global_object.vm().exception())
+ return INVALID;
+ if (number.is_nan() || number.is_infinity())
+ return 0;
+ return number.as_i32();
+}
+
+u32 Value::to_u32(GlobalObject& global_object) const
+{
+ // 7.1.7 ToUint32, https://tc39.es/ecma262/#sec-touint32
+ auto number = to_number(global_object);
+ if (global_object.vm().exception())
+ return INVALID;
+ if (number.is_nan() || number.is_infinity())
+ return 0;
+ if (number.as_double() <= 0)
+ return 0;
+ return number.as_u32();
+}
+
+size_t Value::to_length(GlobalObject& global_object) const
+{
+ // 7.1.20 ToLength, https://tc39.es/ecma262/#sec-tolength
+
+ auto& vm = global_object.vm();
+
+ auto len = to_integer_or_infinity(global_object);
+ if (vm.exception())
+ return INVALID;
+ if (len <= 0)
+ return 0;
+ return min(len, MAX_ARRAY_LIKE_INDEX);
+}
+
+size_t Value::to_index(GlobalObject& global_object) const
+{
+ // 7.1.22 ToIndex, https://tc39.es/ecma262/#sec-toindex
+
+ auto& vm = global_object.vm();
+
+ if (is_undefined())
+ return 0;
+ auto integer_index = to_integer_or_infinity(global_object);
+ if (vm.exception())
+ return INVALID;
+ if (integer_index < 0) {
+ vm.throw_exception<RangeError>(global_object, ErrorType::InvalidIndex);
+ return INVALID;
+ }
+ auto index = Value(integer_index).to_length(global_object);
+ ASSERT(!vm.exception());
+ if (integer_index != index) {
+ vm.throw_exception<RangeError>(global_object, ErrorType::InvalidIndex);
+ return INVALID;
+ }
+ return index;
+}
+
+double Value::to_integer_or_infinity(GlobalObject& global_object) const
+{
+ // 7.1.5 ToIntegerOrInfinity, https://tc39.es/ecma262/#sec-tointegerorinfinity
+
+ auto& vm = global_object.vm();
+
+ auto number = to_number(global_object);
+ if (vm.exception())
+ return INVALID;
+ if (number.is_nan() || number.as_double() == 0)
+ return 0;
+ if (number.is_infinity())
+ return number.as_double();
+ auto integer = floor(abs(number.as_double()));
+ if (number.as_double() < 0)
+ integer = -integer;
+ return integer;
+}
+
+Value greater_than(GlobalObject& global_object, Value lhs, Value rhs)
+{
+ TriState relation = abstract_relation(global_object, false, lhs, rhs);
+ if (relation == TriState::Unknown)
+ return Value(false);
+ return Value(relation == TriState::True);
+}
+
+Value greater_than_equals(GlobalObject& global_object, Value lhs, Value rhs)
+{
+ TriState relation = abstract_relation(global_object, true, lhs, rhs);
+ if (relation == TriState::Unknown || relation == TriState::True)
+ return Value(false);
+ return Value(true);
+}
+
+Value less_than(GlobalObject& global_object, Value lhs, Value rhs)
+{
+ TriState relation = abstract_relation(global_object, true, lhs, rhs);
+ if (relation == TriState::Unknown)
+ return Value(false);
+ return Value(relation == TriState::True);
+}
+
+Value less_than_equals(GlobalObject& global_object, Value lhs, Value rhs)
+{
+ TriState relation = abstract_relation(global_object, false, lhs, rhs);
+ if (relation == TriState::Unknown || relation == TriState::True)
+ return Value(false);
+ return Value(true);
+}
+
+Value bitwise_and(GlobalObject& global_object, Value lhs, Value rhs)
+{
+ auto lhs_numeric = lhs.to_numeric(global_object.global_object());
+ if (global_object.vm().exception())
+ return {};
+ auto rhs_numeric = rhs.to_numeric(global_object.global_object());
+ if (global_object.vm().exception())
+ return {};
+ if (both_number(lhs_numeric, rhs_numeric)) {
+ if (!lhs_numeric.is_finite_number() || !rhs_numeric.is_finite_number())
+ return Value(0);
+ return Value((i32)lhs_numeric.as_double() & (i32)rhs_numeric.as_double());
+ }
+ if (both_bigint(lhs_numeric, rhs_numeric))
+ return js_bigint(global_object.heap(), lhs_numeric.as_bigint().big_integer().bitwise_and(rhs_numeric.as_bigint().big_integer()));
+ global_object.vm().throw_exception<TypeError>(global_object.global_object(), ErrorType::BigIntBadOperatorOtherType, "bitwise AND");
+ return {};
+}
+
+Value bitwise_or(GlobalObject& global_object, Value lhs, Value rhs)
+{
+ auto lhs_numeric = lhs.to_numeric(global_object.global_object());
+ if (global_object.vm().exception())
+ return {};
+ auto rhs_numeric = rhs.to_numeric(global_object.global_object());
+ if (global_object.vm().exception())
+ return {};
+ if (both_number(lhs_numeric, rhs_numeric)) {
+ if (!lhs_numeric.is_finite_number() && !rhs_numeric.is_finite_number())
+ return Value(0);
+ if (!lhs_numeric.is_finite_number())
+ return rhs_numeric;
+ if (!rhs_numeric.is_finite_number())
+ return lhs_numeric;
+ return Value((i32)lhs_numeric.as_double() | (i32)rhs_numeric.as_double());
+ }
+ if (both_bigint(lhs_numeric, rhs_numeric))
+ return js_bigint(global_object.heap(), lhs_numeric.as_bigint().big_integer().bitwise_or(rhs_numeric.as_bigint().big_integer()));
+ global_object.vm().throw_exception<TypeError>(global_object.global_object(), ErrorType::BigIntBadOperatorOtherType, "bitwise OR");
+ return {};
+}
+
+Value bitwise_xor(GlobalObject& global_object, Value lhs, Value rhs)
+{
+ auto lhs_numeric = lhs.to_numeric(global_object.global_object());
+ if (global_object.vm().exception())
+ return {};
+ auto rhs_numeric = rhs.to_numeric(global_object.global_object());
+ if (global_object.vm().exception())
+ return {};
+ if (both_number(lhs_numeric, rhs_numeric)) {
+ if (!lhs_numeric.is_finite_number() && !rhs_numeric.is_finite_number())
+ return Value(0);
+ if (!lhs_numeric.is_finite_number())
+ return rhs_numeric;
+ if (!rhs_numeric.is_finite_number())
+ return lhs_numeric;
+ return Value((i32)lhs_numeric.as_double() ^ (i32)rhs_numeric.as_double());
+ }
+ if (both_bigint(lhs_numeric, rhs_numeric))
+ return js_bigint(global_object.heap(), lhs_numeric.as_bigint().big_integer().bitwise_xor(rhs_numeric.as_bigint().big_integer()));
+ global_object.vm().throw_exception<TypeError>(global_object.global_object(), ErrorType::BigIntBadOperatorOtherType, "bitwise XOR");
+ return {};
+}
+
+Value bitwise_not(GlobalObject& global_object, Value lhs)
+{
+ auto lhs_numeric = lhs.to_numeric(global_object.global_object());
+ if (global_object.vm().exception())
+ return {};
+ if (lhs_numeric.is_number())
+ return Value(~lhs_numeric.to_i32(global_object));
+ auto big_integer_bitwise_not = lhs_numeric.as_bigint().big_integer();
+ big_integer_bitwise_not = big_integer_bitwise_not.plus(Crypto::SignedBigInteger { 1 });
+ big_integer_bitwise_not.negate();
+ return js_bigint(global_object.heap(), big_integer_bitwise_not);
+}
+
+Value unary_plus(GlobalObject& global_object, Value lhs)
+{
+ return lhs.to_number(global_object.global_object());
+}
+
+Value unary_minus(GlobalObject& global_object, Value lhs)
+{
+ auto lhs_numeric = lhs.to_numeric(global_object.global_object());
+ if (global_object.vm().exception())
+ return {};
+ if (lhs_numeric.is_number()) {
+ if (lhs_numeric.is_nan())
+ return js_nan();
+ return Value(-lhs_numeric.as_double());
+ }
+ if (lhs_numeric.as_bigint().big_integer() == BIGINT_ZERO)
+ return js_bigint(global_object.heap(), BIGINT_ZERO);
+ auto big_integer_negated = lhs_numeric.as_bigint().big_integer();
+ big_integer_negated.negate();
+ return js_bigint(global_object.heap(), big_integer_negated);
+}
+
+Value left_shift(GlobalObject& global_object, Value lhs, Value rhs)
+{
+ auto lhs_numeric = lhs.to_numeric(global_object.global_object());
+ if (global_object.vm().exception())
+ return {};
+ auto rhs_numeric = rhs.to_numeric(global_object.global_object());
+ if (global_object.vm().exception())
+ return {};
+ if (both_number(lhs_numeric, rhs_numeric)) {
+ if (!lhs_numeric.is_finite_number())
+ return Value(0);
+ if (!rhs_numeric.is_finite_number())
+ return lhs_numeric;
+ return Value((i32)lhs_numeric.as_double() << (i32)rhs_numeric.as_double());
+ }
+ if (both_bigint(lhs_numeric, rhs_numeric))
+ TODO();
+ global_object.vm().throw_exception<TypeError>(global_object.global_object(), ErrorType::BigIntBadOperatorOtherType, "left-shift");
+ return {};
+}
+
+Value right_shift(GlobalObject& global_object, Value lhs, Value rhs)
+{
+ auto lhs_numeric = lhs.to_numeric(global_object.global_object());
+ if (global_object.vm().exception())
+ return {};
+ auto rhs_numeric = rhs.to_numeric(global_object.global_object());
+ if (global_object.vm().exception())
+ return {};
+ if (both_number(lhs_numeric, rhs_numeric)) {
+ if (!lhs_numeric.is_finite_number())
+ return Value(0);
+ if (!rhs_numeric.is_finite_number())
+ return lhs_numeric;
+ return Value((i32)lhs_numeric.as_double() >> (i32)rhs_numeric.as_double());
+ }
+ if (both_bigint(lhs_numeric, rhs_numeric))
+ TODO();
+ global_object.vm().throw_exception<TypeError>(global_object.global_object(), ErrorType::BigIntBadOperatorOtherType, "right-shift");
+ return {};
+}
+
+Value unsigned_right_shift(GlobalObject& global_object, Value lhs, Value rhs)
+{
+ auto lhs_numeric = lhs.to_numeric(global_object.global_object());
+ if (global_object.vm().exception())
+ return {};
+ auto rhs_numeric = rhs.to_numeric(global_object.global_object());
+ if (global_object.vm().exception())
+ return {};
+ if (both_number(lhs_numeric, rhs_numeric)) {
+ if (!lhs_numeric.is_finite_number())
+ return Value(0);
+ if (!rhs_numeric.is_finite_number())
+ return lhs_numeric;
+ return Value((unsigned)lhs_numeric.as_double() >> (i32)rhs_numeric.as_double());
+ }
+ global_object.vm().throw_exception<TypeError>(global_object.global_object(), ErrorType::BigIntBadOperator, "unsigned right-shift");
+ return {};
+}
+
+Value add(GlobalObject& global_object, Value lhs, Value rhs)
+{
+ auto lhs_primitive = lhs.to_primitive();
+ if (global_object.vm().exception())
+ return {};
+ auto rhs_primitive = rhs.to_primitive();
+ if (global_object.vm().exception())
+ return {};
+
+ if (lhs_primitive.is_string() || rhs_primitive.is_string()) {
+ auto lhs_string = lhs_primitive.to_string(global_object.global_object());
+ if (global_object.vm().exception())
+ return {};
+ auto rhs_string = rhs_primitive.to_string(global_object.global_object());
+ if (global_object.vm().exception())
+ return {};
+ StringBuilder builder(lhs_string.length() + rhs_string.length());
+ builder.append(lhs_string);
+ builder.append(rhs_string);
+ return js_string(global_object.heap(), builder.to_string());
+ }
+
+ auto lhs_numeric = lhs_primitive.to_numeric(global_object.global_object());
+ if (global_object.vm().exception())
+ return {};
+ auto rhs_numeric = rhs_primitive.to_numeric(global_object.global_object());
+ if (global_object.vm().exception())
+ return {};
+ if (both_number(lhs_numeric, rhs_numeric))
+ return Value(lhs_numeric.as_double() + rhs_numeric.as_double());
+ if (both_bigint(lhs_numeric, rhs_numeric))
+ return js_bigint(global_object.heap(), lhs_numeric.as_bigint().big_integer().plus(rhs_numeric.as_bigint().big_integer()));
+ global_object.vm().throw_exception<TypeError>(global_object.global_object(), ErrorType::BigIntBadOperatorOtherType, "addition");
+ return {};
+}
+
+Value sub(GlobalObject& global_object, Value lhs, Value rhs)
+{
+ auto lhs_numeric = lhs.to_numeric(global_object.global_object());
+ if (global_object.vm().exception())
+ return {};
+ auto rhs_numeric = rhs.to_numeric(global_object.global_object());
+ if (global_object.vm().exception())
+ return {};
+ if (both_number(lhs_numeric, rhs_numeric))
+ return Value(lhs_numeric.as_double() - rhs_numeric.as_double());
+ if (both_bigint(lhs_numeric, rhs_numeric))
+ return js_bigint(global_object.heap(), lhs_numeric.as_bigint().big_integer().minus(rhs_numeric.as_bigint().big_integer()));
+ global_object.vm().throw_exception<TypeError>(global_object.global_object(), ErrorType::BigIntBadOperatorOtherType, "subtraction");
+ return {};
+}
+
+Value mul(GlobalObject& global_object, Value lhs, Value rhs)
+{
+ auto lhs_numeric = lhs.to_numeric(global_object.global_object());
+ if (global_object.vm().exception())
+ return {};
+ auto rhs_numeric = rhs.to_numeric(global_object.global_object());
+ if (global_object.vm().exception())
+ return {};
+ if (both_number(lhs_numeric, rhs_numeric))
+ return Value(lhs_numeric.as_double() * rhs_numeric.as_double());
+ if (both_bigint(lhs_numeric, rhs_numeric))
+ return js_bigint(global_object.heap(), lhs_numeric.as_bigint().big_integer().multiplied_by(rhs_numeric.as_bigint().big_integer()));
+ global_object.vm().throw_exception<TypeError>(global_object.global_object(), ErrorType::BigIntBadOperatorOtherType, "multiplication");
+ return {};
+}
+
+Value div(GlobalObject& global_object, Value lhs, Value rhs)
+{
+ auto lhs_numeric = lhs.to_numeric(global_object.global_object());
+ if (global_object.vm().exception())
+ return {};
+ auto rhs_numeric = rhs.to_numeric(global_object.global_object());
+ if (global_object.vm().exception())
+ return {};
+ if (both_number(lhs_numeric, rhs_numeric))
+ return Value(lhs_numeric.as_double() / rhs_numeric.as_double());
+ if (both_bigint(lhs_numeric, rhs_numeric))
+ return js_bigint(global_object.heap(), lhs_numeric.as_bigint().big_integer().divided_by(rhs_numeric.as_bigint().big_integer()).quotient);
+ global_object.vm().throw_exception<TypeError>(global_object.global_object(), ErrorType::BigIntBadOperatorOtherType, "division");
+ return {};
+}
+
+Value mod(GlobalObject& global_object, Value lhs, Value rhs)
+{
+ auto lhs_numeric = lhs.to_numeric(global_object.global_object());
+ if (global_object.vm().exception())
+ return {};
+ auto rhs_numeric = rhs.to_numeric(global_object.global_object());
+ if (global_object.vm().exception())
+ return {};
+ if (both_number(lhs_numeric, rhs_numeric)) {
+ if (lhs_numeric.is_nan() || rhs_numeric.is_nan())
+ return js_nan();
+ auto index = lhs_numeric.as_double();
+ auto period = rhs_numeric.as_double();
+ auto trunc = (double)(i32)(index / period);
+ return Value(index - trunc * period);
+ }
+ if (both_bigint(lhs_numeric, rhs_numeric))
+ return js_bigint(global_object.heap(), lhs_numeric.as_bigint().big_integer().divided_by(rhs_numeric.as_bigint().big_integer()).remainder);
+ global_object.vm().throw_exception<TypeError>(global_object.global_object(), ErrorType::BigIntBadOperatorOtherType, "modulo");
+ return {};
+}
+
+Value exp(GlobalObject& global_object, Value lhs, Value rhs)
+{
+ auto& vm = global_object.vm();
+ auto lhs_numeric = lhs.to_numeric(global_object);
+ if (vm.exception())
+ return {};
+ auto rhs_numeric = rhs.to_numeric(global_object);
+ if (vm.exception())
+ return {};
+ if (both_number(lhs_numeric, rhs_numeric))
+ return Value(pow(lhs_numeric.as_double(), rhs_numeric.as_double()));
+ if (both_bigint(lhs_numeric, rhs_numeric))
+ return js_bigint(vm.heap(), Crypto::NumberTheory::Power(lhs_numeric.as_bigint().big_integer(), rhs_numeric.as_bigint().big_integer()));
+ vm.throw_exception<TypeError>(global_object, ErrorType::BigIntBadOperatorOtherType, "exponentiation");
+ return {};
+}
+
+Value in(GlobalObject& global_object, Value lhs, Value rhs)
+{
+ if (!rhs.is_object()) {
+ global_object.vm().throw_exception<TypeError>(global_object.global_object(), ErrorType::InOperatorWithObject);
+ return {};
+ }
+ auto lhs_string = lhs.to_string(global_object.global_object());
+ if (global_object.vm().exception())
+ return {};
+ return Value(rhs.as_object().has_property(lhs_string));
+}
+
+Value instance_of(GlobalObject& global_object, Value lhs, Value rhs)
+{
+ auto& vm = global_object.vm();
+ if (!rhs.is_object()) {
+ vm.throw_exception<TypeError>(global_object, ErrorType::NotAnObject, rhs.to_string_without_side_effects());
+ return {};
+ }
+ auto has_instance_method = rhs.as_object().get(vm.well_known_symbol_has_instance());
+ if (!has_instance_method.is_empty()) {
+ if (!has_instance_method.is_function()) {
+ vm.throw_exception<TypeError>(global_object, ErrorType::NotAFunction, has_instance_method.to_string_without_side_effects());
+ return {};
+ }
+ auto has_instance_result = vm.call(has_instance_method.as_function(), rhs, lhs);
+ if (vm.exception())
+ return {};
+ return Value(has_instance_result.to_boolean());
+ }
+
+ if (!rhs.is_function()) {
+ vm.throw_exception<TypeError>(global_object, ErrorType::NotAFunction, rhs.to_string_without_side_effects());
+ return {};
+ }
+ return ordinary_has_instance(global_object, lhs, rhs);
+}
+
+Value ordinary_has_instance(GlobalObject& global_object, Value lhs, Value rhs)
+{
+ auto& vm = global_object.vm();
+ if (!rhs.is_function())
+ return Value(false);
+ auto& rhs_function = rhs.as_function();
+
+ if (is<BoundFunction>(rhs_function)) {
+ auto& bound_target = static_cast<const BoundFunction&>(rhs_function);
+ return instance_of(global_object, lhs, Value(&bound_target.target_function()));
+ }
+
+ if (!lhs.is_object())
+ return Value(false);
+
+ Object* lhs_object = &lhs.as_object();
+ auto rhs_prototype = rhs_function.get(vm.names.prototype);
+ if (vm.exception())
+ return {};
+
+ if (!rhs_prototype.is_object()) {
+ vm.throw_exception<TypeError>(global_object, ErrorType::InstanceOfOperatorBadPrototype, rhs.to_string_without_side_effects());
+ return {};
+ }
+ while (true) {
+ lhs_object = lhs_object->prototype();
+ if (vm.exception())
+ return {};
+ if (!lhs_object)
+ return Value(false);
+ if (same_value(rhs_prototype, lhs_object))
+ return Value(true);
+ }
+}
+
+bool same_value(Value lhs, Value rhs)
+{
+ if (lhs.type() != rhs.type())
+ return false;
+
+ if (lhs.is_number()) {
+ if (lhs.is_nan() && rhs.is_nan())
+ return true;
+ if (lhs.is_positive_zero() && rhs.is_negative_zero())
+ return false;
+ if (lhs.is_negative_zero() && rhs.is_positive_zero())
+ return false;
+ return lhs.as_double() == rhs.as_double();
+ }
+
+ if (lhs.is_bigint()) {
+ auto lhs_big_integer = lhs.as_bigint().big_integer();
+ auto rhs_big_integer = rhs.as_bigint().big_integer();
+ if (lhs_big_integer == BIGINT_ZERO && rhs_big_integer == BIGINT_ZERO && lhs_big_integer.is_negative() != rhs_big_integer.is_negative())
+ return false;
+ return lhs_big_integer == rhs_big_integer;
+ }
+
+ return same_value_non_numeric(lhs, rhs);
+}
+
+bool same_value_zero(Value lhs, Value rhs)
+{
+ if (lhs.type() != rhs.type())
+ return false;
+
+ if (lhs.is_number()) {
+ if (lhs.is_nan() && rhs.is_nan())
+ return true;
+ return lhs.as_double() == rhs.as_double();
+ }
+
+ if (lhs.is_bigint())
+ return lhs.as_bigint().big_integer() == rhs.as_bigint().big_integer();
+
+ return same_value_non_numeric(lhs, rhs);
+}
+
+bool same_value_non_numeric(Value lhs, Value rhs)
+{
+ ASSERT(!lhs.is_number() && !lhs.is_bigint());
+ ASSERT(lhs.type() == rhs.type());
+
+ switch (lhs.type()) {
+ case Value::Type::Undefined:
+ case Value::Type::Null:
+ return true;
+ case Value::Type::String:
+ return lhs.as_string().string() == rhs.as_string().string();
+ case Value::Type::Symbol:
+ return &lhs.as_symbol() == &rhs.as_symbol();
+ case Value::Type::Boolean:
+ return lhs.as_bool() == rhs.as_bool();
+ case Value::Type::Object:
+ return &lhs.as_object() == &rhs.as_object();
+ default:
+ ASSERT_NOT_REACHED();
+ }
+}
+
+bool strict_eq(Value lhs, Value rhs)
+{
+ if (lhs.type() != rhs.type())
+ return false;
+
+ if (lhs.is_number()) {
+ if (lhs.is_nan() || rhs.is_nan())
+ return false;
+ if (lhs.as_double() == rhs.as_double())
+ return true;
+ return false;
+ }
+
+ if (lhs.is_bigint())
+ return lhs.as_bigint().big_integer() == rhs.as_bigint().big_integer();
+
+ return same_value_non_numeric(lhs, rhs);
+}
+
+bool abstract_eq(GlobalObject& global_object, Value lhs, Value rhs)
+{
+ if (lhs.type() == rhs.type())
+ return strict_eq(lhs, rhs);
+
+ if (lhs.is_nullish() && rhs.is_nullish())
+ return true;
+
+ if (lhs.is_number() && rhs.is_string())
+ return abstract_eq(global_object, lhs, rhs.to_number(global_object.global_object()));
+
+ if (lhs.is_string() && rhs.is_number())
+ return abstract_eq(global_object, lhs.to_number(global_object.global_object()), rhs);
+
+ if (lhs.is_bigint() && rhs.is_string()) {
+ auto& rhs_string = rhs.as_string().string();
+ if (!is_valid_bigint_value(rhs_string))
+ return false;
+ return abstract_eq(global_object, lhs, js_bigint(global_object.heap(), Crypto::SignedBigInteger::from_base10(rhs_string)));
+ }
+
+ if (lhs.is_string() && rhs.is_bigint())
+ return abstract_eq(global_object, rhs, lhs);
+
+ if (lhs.is_boolean())
+ return abstract_eq(global_object, lhs.to_number(global_object.global_object()), rhs);
+
+ if (rhs.is_boolean())
+ return abstract_eq(global_object, lhs, rhs.to_number(global_object.global_object()));
+
+ if ((lhs.is_string() || lhs.is_number() || lhs.is_bigint() || lhs.is_symbol()) && rhs.is_object())
+ return abstract_eq(global_object, lhs, rhs.to_primitive());
+
+ if (lhs.is_object() && (rhs.is_string() || rhs.is_number() || lhs.is_bigint() || rhs.is_symbol()))
+ return abstract_eq(global_object, lhs.to_primitive(), rhs);
+
+ if ((lhs.is_bigint() && rhs.is_number()) || (lhs.is_number() && rhs.is_bigint())) {
+ if (lhs.is_nan() || lhs.is_infinity() || rhs.is_nan() || rhs.is_infinity())
+ return false;
+ if ((lhs.is_number() && !lhs.is_integer()) || (rhs.is_number() && !rhs.is_integer()))
+ return false;
+ if (lhs.is_number())
+ return Crypto::SignedBigInteger { lhs.as_i32() } == rhs.as_bigint().big_integer();
+ else
+ return Crypto::SignedBigInteger { rhs.as_i32() } == lhs.as_bigint().big_integer();
+ }
+
+ return false;
+}
+
+TriState abstract_relation(GlobalObject& global_object, bool left_first, Value lhs, Value rhs)
+{
+ Value x_primitive;
+ Value y_primitive;
+
+ if (left_first) {
+ x_primitive = lhs.to_primitive(Value::PreferredType::Number);
+ if (global_object.vm().exception())
+ return {};
+ y_primitive = rhs.to_primitive(Value::PreferredType::Number);
+ if (global_object.vm().exception())
+ return {};
+ } else {
+ y_primitive = lhs.to_primitive(Value::PreferredType::Number);
+ if (global_object.vm().exception())
+ return {};
+ x_primitive = rhs.to_primitive(Value::PreferredType::Number);
+ if (global_object.vm().exception())
+ return {};
+ }
+
+ if (x_primitive.is_string() && y_primitive.is_string()) {
+ auto x_string = x_primitive.as_string().string();
+ auto y_string = y_primitive.as_string().string();
+
+ if (x_string.starts_with(y_string))
+ return TriState::False;
+ if (y_string.starts_with(x_string))
+ return TriState::True;
+
+ Utf8View x_code_points { x_string };
+ Utf8View y_code_points { y_string };
+ for (auto k = x_code_points.begin(), l = y_code_points.begin();
+ k != x_code_points.end() && l != y_code_points.end();
+ ++k, ++l) {
+ if (*k != *l) {
+ if (*k < *l) {
+ return TriState::True;
+ } else {
+ return TriState::False;
+ }
+ }
+ }
+ ASSERT_NOT_REACHED();
+ }
+
+ if (x_primitive.is_bigint() && y_primitive.is_string()) {
+ auto& y_string = y_primitive.as_string().string();
+ if (!is_valid_bigint_value(y_string))
+ return TriState::Unknown;
+ if (x_primitive.as_bigint().big_integer() < Crypto::SignedBigInteger::from_base10(y_string))
+ return TriState::True;
+ else
+ return TriState::False;
+ }
+
+ if (x_primitive.is_string() && y_primitive.is_bigint()) {
+ auto& x_string = x_primitive.as_string().string();
+ if (!is_valid_bigint_value(x_string))
+ return TriState::Unknown;
+ if (Crypto::SignedBigInteger::from_base10(x_string) < y_primitive.as_bigint().big_integer())
+ return TriState::True;
+ else
+ return TriState::False;
+ }
+
+ auto x_numeric = x_primitive.to_numeric(global_object.global_object());
+ if (global_object.vm().exception())
+ return {};
+ auto y_numeric = y_primitive.to_numeric(global_object.global_object());
+ if (global_object.vm().exception())
+ return {};
+
+ if (x_numeric.is_nan() || y_numeric.is_nan())
+ return TriState::Unknown;
+
+ if (x_numeric.is_positive_infinity() || y_numeric.is_negative_infinity())
+ return TriState::False;
+
+ if (x_numeric.is_negative_infinity() || y_numeric.is_positive_infinity())
+ return TriState::True;
+
+ if (x_numeric.is_number() && y_numeric.is_number()) {
+ if (x_numeric.as_double() < y_numeric.as_double())
+ return TriState::True;
+ else
+ return TriState::False;
+ }
+
+ if (x_numeric.is_bigint() && y_numeric.is_bigint()) {
+ if (x_numeric.as_bigint().big_integer() < y_numeric.as_bigint().big_integer())
+ return TriState::True;
+ else
+ return TriState::False;
+ }
+
+ ASSERT((x_numeric.is_number() && y_numeric.is_bigint()) || (x_numeric.is_bigint() && y_numeric.is_number()));
+
+ bool x_lower_than_y;
+ if (x_numeric.is_number()) {
+ x_lower_than_y = x_numeric.is_integer()
+ ? Crypto::SignedBigInteger { x_numeric.as_i32() } < y_numeric.as_bigint().big_integer()
+ : (Crypto::SignedBigInteger { x_numeric.as_i32() } < y_numeric.as_bigint().big_integer() || Crypto::SignedBigInteger { x_numeric.as_i32() + 1 } < y_numeric.as_bigint().big_integer());
+ } else {
+ x_lower_than_y = y_numeric.is_integer()
+ ? x_numeric.as_bigint().big_integer() < Crypto::SignedBigInteger { y_numeric.as_i32() }
+ : (x_numeric.as_bigint().big_integer() < Crypto::SignedBigInteger { y_numeric.as_i32() } || x_numeric.as_bigint().big_integer() < Crypto::SignedBigInteger { y_numeric.as_i32() + 1 });
+ }
+ if (x_lower_than_y)
+ return TriState::True;
+ else
+ return TriState::False;
+}
+
+size_t length_of_array_like(GlobalObject& global_object, const Object& object)
+{
+ // 7.3.18 LengthOfArrayLike, https://tc39.es/ecma262/#sec-lengthofarraylike
+
+ auto& vm = global_object.vm();
+ auto result = object.get(vm.names.length).value_or(js_undefined());
+ if (vm.exception())
+ return INVALID;
+ return result.to_length(global_object);
+}
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/Value.h b/Userland/Libraries/LibJS/Runtime/Value.h
new file mode 100644
index 0000000000..a24db80057
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/Value.h
@@ -0,0 +1,357 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * 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 <AK/Assertions.h>
+#include <AK/Format.h>
+#include <AK/Forward.h>
+#include <AK/String.h>
+#include <AK/Types.h>
+#include <LibJS/Forward.h>
+#include <math.h>
+
+// 2 ** 53 - 1
+static constexpr double MAX_ARRAY_LIKE_INDEX = 9007199254740991.0;
+// 2 ** 32 - 1
+static constexpr double MAX_U32 = 4294967295.0;
+
+namespace JS {
+
+class Value {
+public:
+ enum class Type {
+ Empty,
+ Undefined,
+ Null,
+ Number,
+ String,
+ Object,
+ Boolean,
+ Symbol,
+ Accessor,
+ BigInt,
+ NativeProperty,
+ };
+
+ enum class PreferredType {
+ Default,
+ String,
+ Number,
+ };
+
+ bool is_empty() const { return m_type == Type::Empty; }
+ bool is_undefined() const { return m_type == Type::Undefined; }
+ bool is_null() const { return m_type == Type::Null; }
+ bool is_number() const { return m_type == Type::Number; }
+ bool is_string() const { return m_type == Type::String; }
+ bool is_object() const { return m_type == Type::Object; }
+ bool is_boolean() const { return m_type == Type::Boolean; }
+ bool is_symbol() const { return m_type == Type::Symbol; }
+ bool is_accessor() const { return m_type == Type::Accessor; };
+ bool is_bigint() const { return m_type == Type::BigInt; };
+ bool is_native_property() const { return m_type == Type::NativeProperty; }
+ bool is_nullish() const { return is_null() || is_undefined(); }
+ bool is_cell() const { return is_string() || is_accessor() || is_object() || is_bigint() || is_symbol() || is_native_property(); }
+ bool is_array() const;
+ bool is_function() const;
+ bool is_regexp(GlobalObject& global_object) const;
+
+ bool is_nan() const { return is_number() && __builtin_isnan(as_double()); }
+ bool is_infinity() const { return is_number() && __builtin_isinf(as_double()); }
+ bool is_positive_infinity() const { return is_number() && __builtin_isinf_sign(as_double()) > 0; }
+ bool is_negative_infinity() const { return is_number() && __builtin_isinf_sign(as_double()) < 0; }
+ bool is_positive_zero() const { return is_number() && 1.0 / as_double() == INFINITY; }
+ bool is_negative_zero() const { return is_number() && 1.0 / as_double() == -INFINITY; }
+ bool is_integer() const { return is_finite_number() && (i32)as_double() == as_double(); }
+ bool is_finite_number() const
+ {
+ if (!is_number())
+ return false;
+ auto number = as_double();
+ return !__builtin_isnan(number) && !__builtin_isinf(number);
+ }
+
+ Value()
+ : m_type(Type::Empty)
+ {
+ }
+
+ explicit Value(bool value)
+ : m_type(Type::Boolean)
+ {
+ m_value.as_bool = value;
+ }
+
+ explicit Value(double value)
+ : m_type(Type::Number)
+ {
+ m_value.as_double = value;
+ }
+
+ explicit Value(unsigned value)
+ : m_type(Type::Number)
+ {
+ m_value.as_double = static_cast<double>(value);
+ }
+
+ explicit Value(i32 value)
+ : m_type(Type::Number)
+ {
+ m_value.as_double = value;
+ }
+
+ Value(const Object* object)
+ : m_type(object ? Type::Object : Type::Null)
+ {
+ m_value.as_object = const_cast<Object*>(object);
+ }
+
+ Value(const PrimitiveString* string)
+ : m_type(Type::String)
+ {
+ m_value.as_string = const_cast<PrimitiveString*>(string);
+ }
+
+ Value(const Symbol* symbol)
+ : m_type(Type::Symbol)
+ {
+ m_value.as_symbol = const_cast<Symbol*>(symbol);
+ }
+
+ Value(const Accessor* accessor)
+ : m_type(Type::Accessor)
+ {
+ m_value.as_accessor = const_cast<Accessor*>(accessor);
+ }
+
+ Value(const BigInt* bigint)
+ : m_type(Type::BigInt)
+ {
+ m_value.as_bigint = const_cast<BigInt*>(bigint);
+ }
+
+ Value(const NativeProperty* native_property)
+ : m_type(Type::NativeProperty)
+ {
+ m_value.as_native_property = const_cast<NativeProperty*>(native_property);
+ }
+
+ explicit Value(Type type)
+ : m_type(type)
+ {
+ }
+
+ Type type() const { return m_type; }
+
+ double as_double() const
+ {
+ ASSERT(type() == Type::Number);
+ return m_value.as_double;
+ }
+
+ bool as_bool() const
+ {
+ ASSERT(type() == Type::Boolean);
+ return m_value.as_bool;
+ }
+
+ Object& as_object()
+ {
+ ASSERT(type() == Type::Object);
+ return *m_value.as_object;
+ }
+
+ const Object& as_object() const
+ {
+ ASSERT(type() == Type::Object);
+ return *m_value.as_object;
+ }
+
+ PrimitiveString& as_string()
+ {
+ ASSERT(is_string());
+ return *m_value.as_string;
+ }
+
+ const PrimitiveString& as_string() const
+ {
+ ASSERT(is_string());
+ return *m_value.as_string;
+ }
+
+ Symbol& as_symbol()
+ {
+ ASSERT(is_symbol());
+ return *m_value.as_symbol;
+ }
+
+ const Symbol& as_symbol() const
+ {
+ ASSERT(is_symbol());
+ return *m_value.as_symbol;
+ }
+
+ Cell* as_cell()
+ {
+ ASSERT(is_cell());
+ return m_value.as_cell;
+ }
+
+ Accessor& as_accessor()
+ {
+ ASSERT(is_accessor());
+ return *m_value.as_accessor;
+ }
+
+ BigInt& as_bigint()
+ {
+ ASSERT(is_bigint());
+ return *m_value.as_bigint;
+ }
+
+ NativeProperty& as_native_property()
+ {
+ ASSERT(is_native_property());
+ return *m_value.as_native_property;
+ }
+
+ Array& as_array();
+ Function& as_function();
+
+ i32 as_i32() const;
+ u32 as_u32() const;
+ size_t as_size_t() const;
+
+ String to_string(GlobalObject&, bool legacy_null_to_empty_string = false) const;
+ PrimitiveString* to_primitive_string(GlobalObject&);
+ Value to_primitive(PreferredType preferred_type = PreferredType::Default) const;
+ Object* to_object(GlobalObject&) const;
+ Value to_numeric(GlobalObject&) const;
+ Value to_number(GlobalObject&) const;
+ BigInt* to_bigint(GlobalObject&) const;
+ double to_double(GlobalObject&) const;
+ i32 to_i32(GlobalObject&) const;
+ u32 to_u32(GlobalObject&) const;
+ size_t to_length(GlobalObject&) const;
+ size_t to_index(GlobalObject&) const;
+ double to_integer_or_infinity(GlobalObject&) const;
+ bool to_boolean() const;
+
+ String to_string_without_side_effects() const;
+
+ Value value_or(Value fallback) const
+ {
+ if (is_empty())
+ return fallback;
+ return *this;
+ }
+
+private:
+ Type m_type { Type::Empty };
+
+ union {
+ bool as_bool;
+ double as_double;
+ PrimitiveString* as_string;
+ Symbol* as_symbol;
+ Object* as_object;
+ Cell* as_cell;
+ Accessor* as_accessor;
+ BigInt* as_bigint;
+ NativeProperty* as_native_property;
+ } m_value;
+};
+
+inline Value js_undefined()
+{
+ return Value(Value::Type::Undefined);
+}
+
+inline Value js_null()
+{
+ return Value(Value::Type::Null);
+}
+
+inline Value js_nan()
+{
+ return Value(NAN);
+}
+
+inline Value js_infinity()
+{
+ return Value(INFINITY);
+}
+
+inline Value js_negative_infinity()
+{
+ return Value(-INFINITY);
+}
+
+Value greater_than(GlobalObject&, Value lhs, Value rhs);
+Value greater_than_equals(GlobalObject&, Value lhs, Value rhs);
+Value less_than(GlobalObject&, Value lhs, Value rhs);
+Value less_than_equals(GlobalObject&, Value lhs, Value rhs);
+Value bitwise_and(GlobalObject&, Value lhs, Value rhs);
+Value bitwise_or(GlobalObject&, Value lhs, Value rhs);
+Value bitwise_xor(GlobalObject&, Value lhs, Value rhs);
+Value bitwise_not(GlobalObject&, Value);
+Value unary_plus(GlobalObject&, Value);
+Value unary_minus(GlobalObject&, Value);
+Value left_shift(GlobalObject&, Value lhs, Value rhs);
+Value right_shift(GlobalObject&, Value lhs, Value rhs);
+Value unsigned_right_shift(GlobalObject&, Value lhs, Value rhs);
+Value add(GlobalObject&, Value lhs, Value rhs);
+Value sub(GlobalObject&, Value lhs, Value rhs);
+Value mul(GlobalObject&, Value lhs, Value rhs);
+Value div(GlobalObject&, Value lhs, Value rhs);
+Value mod(GlobalObject&, Value lhs, Value rhs);
+Value exp(GlobalObject&, Value lhs, Value rhs);
+Value in(GlobalObject&, Value lhs, Value rhs);
+Value instance_of(GlobalObject&, Value lhs, Value rhs);
+Value ordinary_has_instance(GlobalObject&, Value lhs, Value rhs);
+
+bool abstract_eq(GlobalObject&, Value lhs, Value rhs);
+bool strict_eq(Value lhs, Value rhs);
+bool same_value(Value lhs, Value rhs);
+bool same_value_zero(Value lhs, Value rhs);
+bool same_value_non_numeric(Value lhs, Value rhs);
+TriState abstract_relation(GlobalObject&, bool left_first, Value lhs, Value rhs);
+size_t length_of_array_like(GlobalObject&, const Object&);
+
+}
+
+namespace AK {
+
+template<>
+struct Formatter<JS::Value> : Formatter<StringView> {
+ void format(FormatBuilder& builder, const JS::Value& value)
+ {
+ Formatter<StringView>::format(builder, value.is_empty() ? "<empty>" : value.to_string_without_side_effects());
+ }
+};
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/WithScope.cpp b/Userland/Libraries/LibJS/Runtime/WithScope.cpp
new file mode 100644
index 0000000000..8afb608bf3
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/WithScope.cpp
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * 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/AST.h>
+#include <LibJS/Runtime/WithScope.h>
+
+namespace JS {
+
+WithScope::WithScope(Object& object, ScopeObject* parent_scope)
+ : ScopeObject(parent_scope)
+ , m_object(object)
+{
+}
+
+void WithScope::visit_edges(Cell::Visitor& visitor)
+{
+ Base::visit_edges(visitor);
+ visitor.visit(&m_object);
+}
+
+Optional<Variable> WithScope::get_from_scope(const FlyString& name) const
+{
+ auto value = m_object.get(name);
+ if (value.is_empty())
+ return {};
+ return Variable { value, DeclarationKind::Var };
+}
+
+void WithScope::put_to_scope(const FlyString& name, Variable variable)
+{
+ m_object.put(name, variable.value);
+}
+
+bool WithScope::has_this_binding() const
+{
+ return parent()->has_this_binding();
+}
+
+Value WithScope::get_this_binding(GlobalObject& global_object) const
+{
+ return parent()->get_this_binding(global_object);
+}
+
+}
diff --git a/Userland/Libraries/LibJS/Runtime/WithScope.h b/Userland/Libraries/LibJS/Runtime/WithScope.h
new file mode 100644
index 0000000000..b74542cb64
--- /dev/null
+++ b/Userland/Libraries/LibJS/Runtime/WithScope.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
+ * 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/ScopeObject.h>
+
+namespace JS {
+
+class WithScope : public ScopeObject {
+ JS_OBJECT(WithScope, ScopeObject);
+
+public:
+ WithScope(Object&, ScopeObject* parent_scope);
+
+ virtual Optional<Variable> get_from_scope(const FlyString&) const override;
+ virtual void put_to_scope(const FlyString&, Variable) override;
+ virtual bool has_this_binding() const override;
+ virtual Value get_this_binding(GlobalObject&) const override;
+
+private:
+ virtual void visit_edges(Visitor&) override;
+
+ Object& m_object;
+};
+
+}