Browse Source

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
Andreas Kling 4 năm trước cách đây
mục cha
commit
3565d3c60c

+ 1 - 0
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

+ 35 - 26
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

+ 1 - 1
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 };

+ 1 - 1
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()); \

+ 1 - 1
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

+ 1 - 1
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())                                            \
     {                                                                                                    \

+ 1 - 1
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

+ 5 - 5
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

+ 1 - 1
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

+ 6 - 4
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

+ 3 - 3
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

+ 1 - 0
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;

+ 95 - 0
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
+
+}

+ 165 - 0
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
+
+}

+ 86 - 0
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);
+});