summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndreas Kling <kling@serenityos.org>2020-12-01 21:05:25 +0100
committerAndreas Kling <kling@serenityos.org>2020-12-01 21:05:25 +0100
commit3565d3c60c43d29f35f309063de6b6a5dc70929a (patch)
tree85ca7e136026d6b0a885ca701bb87ad655b9b20b
parent93feb7a81fe7c1ca9603d94d24eeb5822c0c56f7 (diff)
downloadserenity-3565d3c60c43d29f35f309063de6b6a5dc70929a.zip
LibJS: Add six typed arrays (signed and unsigned 8/16/32-bit)
This patch adds six of the standard type arrays and tries to share as much code as possible: - Uint8Array - Uint16Array - Uint32Array - Int8Array - Int16Array - Int32Array
-rw-r--r--Libraries/LibJS/CMakeLists.txt1
-rw-r--r--Libraries/LibJS/Forward.h61
-rw-r--r--Libraries/LibJS/Runtime/CommonPropertyNames.h2
-rw-r--r--Libraries/LibJS/Runtime/Error.cpp2
-rw-r--r--Libraries/LibJS/Runtime/Error.h2
-rw-r--r--Libraries/LibJS/Runtime/ErrorConstructor.cpp2
-rw-r--r--Libraries/LibJS/Runtime/ErrorConstructor.h2
-rw-r--r--Libraries/LibJS/Runtime/ErrorPrototype.cpp10
-rw-r--r--Libraries/LibJS/Runtime/ErrorPrototype.h2
-rw-r--r--Libraries/LibJS/Runtime/GlobalObject.cpp10
-rw-r--r--Libraries/LibJS/Runtime/GlobalObject.h6
-rw-r--r--Libraries/LibJS/Runtime/Object.h1
-rw-r--r--Libraries/LibJS/Runtime/TypedArray.cpp95
-rw-r--r--Libraries/LibJS/Runtime/TypedArray.h165
-rw-r--r--Libraries/LibJS/Tests/builtins/TypedArray/typed-array-basic.js86
15 files changed, 403 insertions, 44 deletions
diff --git a/Libraries/LibJS/CMakeLists.txt b/Libraries/LibJS/CMakeLists.txt
index 6517907288..778d2d71f9 100644
--- a/Libraries/LibJS/CMakeLists.txt
+++ b/Libraries/LibJS/CMakeLists.txt
@@ -72,6 +72,7 @@ set(SOURCES
Runtime/SymbolConstructor.cpp
Runtime/SymbolObject.cpp
Runtime/SymbolPrototype.cpp
+ Runtime/TypedArray.cpp
Runtime/Uint8ClampedArray.cpp
Runtime/VM.cpp
Runtime/Value.cpp
diff --git a/Libraries/LibJS/Forward.h b/Libraries/LibJS/Forward.h
index a6f482f825..a351738cf6 100644
--- a/Libraries/LibJS/Forward.h
+++ b/Libraries/LibJS/Forward.h
@@ -45,28 +45,36 @@
void name([[maybe_unused]] JS::VM& vm, [[maybe_unused]] JS::GlobalObject& global_object, JS::Value value)
// NOTE: Proxy is not included here as it doesn't have a prototype - m_proxy_constructor is initialized separately.
-#define JS_ENUMERATE_NATIVE_OBJECTS \
- __JS_ENUMERATE(Array, array, ArrayPrototype, ArrayConstructor) \
- __JS_ENUMERATE(BigIntObject, bigint, BigIntPrototype, BigIntConstructor) \
- __JS_ENUMERATE(BooleanObject, boolean, BooleanPrototype, BooleanConstructor) \
- __JS_ENUMERATE(Date, date, DatePrototype, DateConstructor) \
- __JS_ENUMERATE(Error, error, ErrorPrototype, ErrorConstructor) \
- __JS_ENUMERATE(Function, function, FunctionPrototype, FunctionConstructor) \
- __JS_ENUMERATE(NumberObject, number, NumberPrototype, NumberConstructor) \
- __JS_ENUMERATE(Object, object, ObjectPrototype, ObjectConstructor) \
- __JS_ENUMERATE(RegExpObject, regexp, RegExpPrototype, RegExpConstructor) \
- __JS_ENUMERATE(StringObject, string, StringPrototype, StringConstructor) \
- __JS_ENUMERATE(SymbolObject, symbol, SymbolPrototype, SymbolConstructor)
-
-#define JS_ENUMERATE_ERROR_SUBCLASSES \
- __JS_ENUMERATE(EvalError, eval_error, EvalErrorPrototype, EvalErrorConstructor) \
- __JS_ENUMERATE(InternalError, internal_error, InternalErrorPrototype, InternalErrorConstructor) \
- __JS_ENUMERATE(InvalidCharacterError, invalid_character_error, InvalidCharacterErrorPrototype, InvalidCharacterErrorConstructor) \
- __JS_ENUMERATE(RangeError, range_error, RangeErrorPrototype, RangeErrorConstructor) \
- __JS_ENUMERATE(ReferenceError, reference_error, ReferenceErrorPrototype, ReferenceErrorConstructor) \
- __JS_ENUMERATE(SyntaxError, syntax_error, SyntaxErrorPrototype, SyntaxErrorConstructor) \
- __JS_ENUMERATE(TypeError, type_error, TypeErrorPrototype, TypeErrorConstructor) \
- __JS_ENUMERATE(URIError, uri_error, URIErrorPrototype, URIErrorConstructor)
+#define JS_ENUMERATE_NATIVE_OBJECTS \
+ __JS_ENUMERATE(Array, array, ArrayPrototype, ArrayConstructor, void) \
+ __JS_ENUMERATE(BigIntObject, bigint, BigIntPrototype, BigIntConstructor, void) \
+ __JS_ENUMERATE(BooleanObject, boolean, BooleanPrototype, BooleanConstructor, void) \
+ __JS_ENUMERATE(Date, date, DatePrototype, DateConstructor, void) \
+ __JS_ENUMERATE(Error, error, ErrorPrototype, ErrorConstructor, void) \
+ __JS_ENUMERATE(Function, function, FunctionPrototype, FunctionConstructor, void) \
+ __JS_ENUMERATE(NumberObject, number, NumberPrototype, NumberConstructor, void) \
+ __JS_ENUMERATE(Object, object, ObjectPrototype, ObjectConstructor, void) \
+ __JS_ENUMERATE(RegExpObject, regexp, RegExpPrototype, RegExpConstructor, void) \
+ __JS_ENUMERATE(StringObject, string, StringPrototype, StringConstructor, void) \
+ __JS_ENUMERATE(SymbolObject, symbol, SymbolPrototype, SymbolConstructor, void)
+
+#define JS_ENUMERATE_ERROR_SUBCLASSES \
+ __JS_ENUMERATE(EvalError, eval_error, EvalErrorPrototype, EvalErrorConstructor, void) \
+ __JS_ENUMERATE(InternalError, internal_error, InternalErrorPrototype, InternalErrorConstructor, void) \
+ __JS_ENUMERATE(InvalidCharacterError, invalid_character_error, InvalidCharacterErrorPrototype, InvalidCharacterErrorConstructor, void) \
+ __JS_ENUMERATE(RangeError, range_error, RangeErrorPrototype, RangeErrorConstructor, void) \
+ __JS_ENUMERATE(ReferenceError, reference_error, ReferenceErrorPrototype, ReferenceErrorConstructor, void) \
+ __JS_ENUMERATE(SyntaxError, syntax_error, SyntaxErrorPrototype, SyntaxErrorConstructor, void) \
+ __JS_ENUMERATE(TypeError, type_error, TypeErrorPrototype, TypeErrorConstructor, void) \
+ __JS_ENUMERATE(URIError, uri_error, URIErrorPrototype, URIErrorConstructor, void)
+
+#define JS_ENUMERATE_TYPED_ARRAYS \
+ __JS_ENUMERATE(Uint8Array, uint8_array, Uint8ArrayPrototype, Uint8ArrayConstructor, u8) \
+ __JS_ENUMERATE(Uint16Array, uint16_array, Uint16ArrayPrototype, Uint16ArrayConstructor, u16) \
+ __JS_ENUMERATE(Uint32Array, uint32_array, Uint32ArrayPrototype, Uint32ArrayConstructor, u32) \
+ __JS_ENUMERATE(Int8Array, int8_array, Int8ArrayPrototype, Int8ArrayConstructor, i8) \
+ __JS_ENUMERATE(Int16Array, int16_array, Int16ArrayPrototype, Int16ArrayConstructor, i16) \
+ __JS_ENUMERATE(Int32Array, int32_array, Int32ArrayPrototype, Int32ArrayConstructor, i32)
#define JS_ENUMERATE_ITERATOR_PROTOTYPES \
__JS_ENUMERATE(Iterator, iterator) \
@@ -75,7 +83,8 @@
#define JS_ENUMERATE_BUILTIN_TYPES \
JS_ENUMERATE_NATIVE_OBJECTS \
- JS_ENUMERATE_ERROR_SUBCLASSES
+ JS_ENUMERATE_ERROR_SUBCLASSES \
+ JS_ENUMERATE_TYPED_ARRAYS
#define JS_ENUMERATE_WELL_KNOWN_SYMBOLS \
__JS_ENUMERATE(iterator, iterator) \
@@ -138,9 +147,9 @@ enum class DeclarationKind;
class ProxyObject;
class ProxyConstructor;
-#define __JS_ENUMERATE(ClassName, snake_name, ConstructorName, PrototypeName) \
- class ClassName; \
- class ConstructorName; \
+#define __JS_ENUMERATE(ClassName, snake_name, ConstructorName, PrototypeName, ArrayType) \
+ class ClassName; \
+ class ConstructorName; \
class PrototypeName;
JS_ENUMERATE_BUILTIN_TYPES
#undef __JS_ENUMERATE
diff --git a/Libraries/LibJS/Runtime/CommonPropertyNames.h b/Libraries/LibJS/Runtime/CommonPropertyNames.h
index f2803f3162..46d0eab30a 100644
--- a/Libraries/LibJS/Runtime/CommonPropertyNames.h
+++ b/Libraries/LibJS/Runtime/CommonPropertyNames.h
@@ -223,7 +223,7 @@ struct CommonPropertyNames {
#define __ENUMERATE(x) FlyString x { #x };
ENUMERATE_STANDARD_PROPERTY_NAMES(__ENUMERATE)
#undef __ENUMERATE
-#define __JS_ENUMERATE(x, a, b, c) FlyString x { #x };
+#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 };
diff --git a/Libraries/LibJS/Runtime/Error.cpp b/Libraries/LibJS/Runtime/Error.cpp
index f0d8c891b4..7f30ab872c 100644
--- a/Libraries/LibJS/Runtime/Error.cpp
+++ b/Libraries/LibJS/Runtime/Error.cpp
@@ -45,7 +45,7 @@ Error::~Error()
{
}
-#define __JS_ENUMERATE(ClassName, snake_name, PrototypeName, ConstructorName) \
+#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()); \
diff --git a/Libraries/LibJS/Runtime/Error.h b/Libraries/LibJS/Runtime/Error.h
index 7ec5c67ac2..e186b13e17 100644
--- a/Libraries/LibJS/Runtime/Error.h
+++ b/Libraries/LibJS/Runtime/Error.h
@@ -63,7 +63,7 @@ private:
virtual ~ClassName() override; \
};
-#define __JS_ENUMERATE(ClassName, snake_name, PrototypeName, ConstructorName) \
+#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/Libraries/LibJS/Runtime/ErrorConstructor.cpp b/Libraries/LibJS/Runtime/ErrorConstructor.cpp
index 28b01bac33..549c74855c 100644
--- a/Libraries/LibJS/Runtime/ErrorConstructor.cpp
+++ b/Libraries/LibJS/Runtime/ErrorConstructor.cpp
@@ -64,7 +64,7 @@ Value ErrorConstructor::construct(Function&)
return Error::create(global_object(), vm.names.Error, message);
}
-#define __JS_ENUMERATE(ClassName, snake_name, PrototypeName, ConstructorName) \
+#define __JS_ENUMERATE(ClassName, snake_name, PrototypeName, ConstructorName, ArrayType) \
ConstructorName::ConstructorName(GlobalObject& global_object) \
: NativeFunction(*global_object.function_prototype()) \
{ \
diff --git a/Libraries/LibJS/Runtime/ErrorConstructor.h b/Libraries/LibJS/Runtime/ErrorConstructor.h
index 5f7d69830e..2626623aaa 100644
--- a/Libraries/LibJS/Runtime/ErrorConstructor.h
+++ b/Libraries/LibJS/Runtime/ErrorConstructor.h
@@ -61,7 +61,7 @@ private:
virtual bool has_constructor() const override { return true; } \
};
-#define __JS_ENUMERATE(ClassName, snake_name, PrototypeName, ConstructorName) \
+#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/Libraries/LibJS/Runtime/ErrorPrototype.cpp b/Libraries/LibJS/Runtime/ErrorPrototype.cpp
index 0a74ee8173..151924b76a 100644
--- a/Libraries/LibJS/Runtime/ErrorPrototype.cpp
+++ b/Libraries/LibJS/Runtime/ErrorPrototype.cpp
@@ -127,11 +127,11 @@ JS_DEFINE_NATIVE_FUNCTION(ErrorPrototype::to_string)
return js_string(vm, String::formatted("{}: {}", name, message));
}
-#define __JS_ENUMERATE(ClassName, snake_name, PrototypeName, ConstructorName) \
- PrototypeName::PrototypeName(GlobalObject& global_object) \
- : Object(*global_object.error_prototype()) \
- { \
- } \
+#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
diff --git a/Libraries/LibJS/Runtime/ErrorPrototype.h b/Libraries/LibJS/Runtime/ErrorPrototype.h
index 0e3423b26d..3b461942ab 100644
--- a/Libraries/LibJS/Runtime/ErrorPrototype.h
+++ b/Libraries/LibJS/Runtime/ErrorPrototype.h
@@ -57,7 +57,7 @@ private:
virtual ~PrototypeName() override; \
};
-#define __JS_ENUMERATE(ClassName, snake_name, PrototypeName, ConstructorName) \
+#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/Libraries/LibJS/Runtime/GlobalObject.cpp b/Libraries/LibJS/Runtime/GlobalObject.cpp
index 7d8b0992df..7a0b939a02 100644
--- a/Libraries/LibJS/Runtime/GlobalObject.cpp
+++ b/Libraries/LibJS/Runtime/GlobalObject.cpp
@@ -62,6 +62,7 @@
#include <LibJS/Runtime/StringPrototype.h>
#include <LibJS/Runtime/SymbolConstructor.h>
#include <LibJS/Runtime/SymbolPrototype.h>
+#include <LibJS/Runtime/TypedArray.h>
#include <LibJS/Runtime/Value.h>
namespace JS {
@@ -95,8 +96,8 @@ void GlobalObject::initialize()
set_prototype(m_object_prototype);
-#define __JS_ENUMERATE(ClassName, snake_name, PrototypeName, ConstructorName) \
- if (!m_##snake_name##_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
@@ -136,9 +137,10 @@ void GlobalObject::initialize()
add_constructor(vm.names.String, m_string_constructor, m_string_prototype);
add_constructor(vm.names.Symbol, m_symbol_constructor, m_symbol_prototype);
-#define __JS_ENUMERATE(ClassName, snake_name, PrototypeName, ConstructorName) \
+#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
}
@@ -154,7 +156,7 @@ void GlobalObject::visit_edges(Visitor& visitor)
visitor.visit(m_new_object_shape);
visitor.visit(m_new_script_function_prototype_object_shape);
-#define __JS_ENUMERATE(ClassName, snake_name, PrototypeName, ConstructorName) \
+#define __JS_ENUMERATE(ClassName, snake_name, PrototypeName, ConstructorName, ArrayType) \
visitor.visit(m_##snake_name##_constructor);
JS_ENUMERATE_ERROR_SUBCLASSES
#undef __JS_ENUMERATE
diff --git a/Libraries/LibJS/Runtime/GlobalObject.h b/Libraries/LibJS/Runtime/GlobalObject.h
index 6ac0eaaa47..154aa5b2aa 100644
--- a/Libraries/LibJS/Runtime/GlobalObject.h
+++ b/Libraries/LibJS/Runtime/GlobalObject.h
@@ -56,7 +56,7 @@ public:
// 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) \
+#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
@@ -90,8 +90,8 @@ private:
// 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) \
- ConstructorName* m_##snake_name##_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
diff --git a/Libraries/LibJS/Runtime/Object.h b/Libraries/LibJS/Runtime/Object.h
index 5caa6780c4..54705576f6 100644
--- a/Libraries/LibJS/Runtime/Object.h
+++ b/Libraries/LibJS/Runtime/Object.h
@@ -129,6 +129,7 @@ public:
virtual bool is_array_iterator_object() const { return false; }
virtual bool is_lexical_environment() const { return false; }
virtual bool is_global_object() 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;
diff --git a/Libraries/LibJS/Runtime/TypedArray.cpp b/Libraries/LibJS/Runtime/TypedArray.cpp
new file mode 100644
index 0000000000..99d0487ab3
--- /dev/null
+++ b/Libraries/LibJS/Runtime/TypedArray.cpp
@@ -0,0 +1,95 @@
+/*
+ * 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/TypedArray.h>
+
+namespace JS {
+
+#define JS_DEFINE_TYPED_ARRAY(ClassName, snake_name, PrototypeName, ConstructorName, Type) \
+ ClassName::~ClassName() { } \
+ 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) \
+ { \
+ } \
+ \
+ PrototypeName::PrototypeName(GlobalObject& global_object) \
+ : Object(*global_object.object_prototype()) \
+ { \
+ } \
+ void PrototypeName::initialize(GlobalObject& global_object) \
+ { \
+ auto& vm = this->vm(); \
+ Object::initialize(global_object); \
+ define_property(vm.names.length, Value(0), Attribute::Configurable); \
+ } \
+ PrototypeName::~PrototypeName() { } \
+ \
+ ConstructorName::~ConstructorName() { } \
+ Value ConstructorName::construct(Function&) { return call(); } \
+ ConstructorName::ConstructorName(GlobalObject& global_object) \
+ : NativeFunction(vm().names.ClassName, *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); \
+ } \
+ Value ConstructorName::call() \
+ { \
+ if (vm().argument_count() <= 0) \
+ return ClassName::create(global_object(), 0); \
+ \
+ 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<TypeError>(global_object(), ErrorType::ArrayInvalidLength); \
+ return {}; \
+ } \
+ auto* array = ClassName::create(global_object(), array_length_value.as_i32()); \
+ return array; \
+ } \
+ auto* array = ClassName::create(global_object(), vm().argument_count()); \
+ for (size_t i = 0; i < vm().argument_count(); ++i) \
+ array->put_by_index(i, vm().argument(i)); \
+ return array; \
+ }
+
+#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/Libraries/LibJS/Runtime/TypedArray.h b/Libraries/LibJS/Runtime/TypedArray.h
new file mode 100644
index 0000000000..8f8c928704
--- /dev/null
+++ b/Libraries/LibJS/Runtime/TypedArray.h
@@ -0,0 +1,165 @@
+/*
+ * 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/GlobalObject.h>
+#include <LibJS/Runtime/NativeFunction.h>
+#include <LibJS/Runtime/VM.h>
+
+namespace JS {
+
+template<typename T>
+class TypedArray : public Object {
+ JS_OBJECT(TypedArray, Object);
+
+public:
+ virtual ~TypedArray() override
+ {
+ ASSERT(m_data);
+ free(m_data);
+ m_data = nullptr;
+ }
+
+ i32 length() const { return m_length; }
+
+ virtual bool put_by_index(u32 property_index, Value value) override
+ {
+ if (property_index >= m_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 {};
+ m_data[property_index] = number;
+ } else if constexpr (sizeof(T) == 4) {
+ auto number = value.to_double(global_object());
+ if (vm().exception())
+ return {};
+ m_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
+ {
+ if (property_index >= m_length)
+ return Base::get_by_index(property_index);
+
+ if constexpr (sizeof(T) < 4) {
+ return Value((i32)m_data[property_index]);
+ } else if constexpr (sizeof(T) == 4) {
+ auto value = m_data[property_index];
+ 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() { return m_data; }
+ const T* data() const { return m_data; }
+
+protected:
+ TypedArray(u32 length, Object& prototype)
+ : Object(prototype)
+ , m_length(length)
+ {
+ auto& vm = this->vm();
+ define_native_property(vm.names.length, length_getter, nullptr);
+ m_data = (T*)calloc(m_length, sizeof(T));
+ }
+
+private:
+ virtual bool is_typed_array() const final { return true; }
+
+ JS_DECLARE_NATIVE_GETTER(length_getter);
+
+ T* m_data { nullptr };
+ u32 m_length { 0 };
+};
+
+template<typename T>
+inline JS_DEFINE_NATIVE_GETTER(TypedArray<T>::length_getter)
+{
+ auto* this_object = vm.this_value(global_object).to_object(global_object);
+ if (!this_object)
+ return {};
+ if (!this_object->is_typed_array()) {
+ vm.throw_exception<TypeError>(global_object, ErrorType::NotA, "TypedArray");
+ return {};
+ }
+ return Value(static_cast<const TypedArray*>(this_object)->length());
+}
+
+#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 void initialize(GlobalObject&) override; \
+ virtual ~PrototypeName() override; \
+ }; \
+ 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; } \
+ };
+
+#undef __JS_ENUMERATE
+#define __JS_ENUMERATE(ClassName, snake_name, PrototypeName, ConstructorName, ArrayType) \
+ JS_DECLARE_TYPED_ARRAY(ClassName, snake_name, PrototypeName, ConstructorName, ArrayType);
+JS_ENUMERATE_TYPED_ARRAYS
+#undef __JS_ENUMERATE
+
+}
diff --git a/Libraries/LibJS/Tests/builtins/TypedArray/typed-array-basic.js b/Libraries/LibJS/Tests/builtins/TypedArray/typed-array-basic.js
new file mode 100644
index 0000000000..aeefb5bb54
--- /dev/null
+++ b/Libraries/LibJS/Tests/builtins/TypedArray/typed-array-basic.js
@@ -0,0 +1,86 @@
+test("basic Uint8Array", () => {
+ var a = new Uint8Array(1);
+ expect(typeof a).toBe("object");
+ expect(a instanceof Uint8Array).toBe(true);
+ expect(a.length).toBe(1);
+ a[0] = 1;
+ expect(a[0]).toBe(1);
+ a[0] -= 2;
+ expect(a[0]).toBe(0xff);
+ ++a[0];
+ expect(a[0]).toBe(0);
+});
+
+test("basic Uint16Array", () => {
+ var a = new Uint16Array(1);
+ expect(typeof a).toBe("object");
+ expect(a instanceof Uint16Array).toBe(true);
+ expect(a.length).toBe(1);
+ a[0] = 1;
+ expect(a[0]).toBe(1);
+ a[0] -= 2;
+ expect(a[0]).toBe(0xffff);
+ ++a[0];
+ expect(a[0]).toBe(0);
+});
+
+test("basic Uint32Array", () => {
+ var a = new Uint32Array(1);
+ expect(typeof a).toBe("object");
+ expect(a instanceof Uint32Array).toBe(true);
+ expect(a.length).toBe(1);
+ a[0] = 1;
+ expect(a[0]).toBe(1);
+ a[0] -= 2;
+ expect(a[0]).toBe(0xffffffff);
+ ++a[0];
+ expect(a[0]).toBe(0);
+});
+
+test("basic Int8Array", () => {
+ var a = new Int8Array(1);
+ expect(typeof a).toBe("object");
+ expect(a instanceof Int8Array).toBe(true);
+ expect(a.length).toBe(1);
+ a[0] = 1;
+ expect(a[0]).toBe(1);
+ a[0] -= 2;
+ expect(a[0]).toBe(-1);
+ ++a[0];
+ expect(a[0]).toBe(0);
+ a[0] = 127;
+ a[0]++;
+ expect(a[0]).toBe(-128);
+});
+
+test("basic Int16Array", () => {
+ var a = new Int16Array(1);
+ expect(typeof a).toBe("object");
+ expect(a instanceof Int16Array).toBe(true);
+ expect(a.length).toBe(1);
+ a[0] = 1;
+ expect(a[0]).toBe(1);
+ a[0] -= 2;
+ expect(a[0]).toBe(-1);
+ ++a[0];
+ expect(a[0]).toBe(0);
+ a[0] = 32767;
+ a[0]++;
+ expect(a[0]).toBe(-32768);
+});
+
+test("basic Int32Array", () => {
+ var a = new Int32Array(1);
+ expect(typeof a).toBe("object");
+ expect(a instanceof Int32Array).toBe(true);
+ expect(a.length).toBe(1);
+ a[0] = 1;
+ expect(a[0]).toBe(1);
+ a[0] -= 2;
+ expect(a[0]).toBe(-1);
+ ++a[0];
+ expect(a[0]).toBe(0);
+ a[0] = 0x7fffffff;
+ a[0]++;
+ expect(a[0]).toBe(-0x80000000);
+});