[njs] Introduced the ArrayBuffer object.

Dmitry Volyntsev xeioex at nginx.com
Mon Dec 9 13:09:45 UTC 2019


details:   https://hg.nginx.org/njs/rev/3bef40125db2
branches:  
changeset: 1281:3bef40125db2
user:      Tiago Natel de Moura <t.nateldemoura at f5.com>
date:      Wed Nov 27 14:02:04 2019 +0000
description:
Introduced the ArrayBuffer object.

diffstat:

 auto/sources             |    1 +
 src/njs_array_buffer.c   |  268 +++++++++++++++++++++++++++++++++++++++++++++++
 src/njs_array_buffer.h   |   46 ++++++++
 src/njs_builtin.c        |   11 +
 src/njs_json.c           |    1 +
 src/njs_main.h           |    1 +
 src/njs_object.c         |    3 +
 src/njs_object_hash.h    |   15 ++
 src/njs_value.c          |    4 +
 src/njs_value.h          |   40 +++++++
 src/njs_vm.h             |    1 +
 src/test/njs_unit_test.c |   49 ++++++++
 12 files changed, 440 insertions(+), 0 deletions(-)

diffs (609 lines):

diff -r 242395b814bb -r 3bef40125db2 auto/sources
--- a/auto/sources	Fri Dec 06 14:59:48 2019 +0300
+++ b/auto/sources	Wed Nov 27 14:02:04 2019 +0000
@@ -51,6 +51,7 @@ NJS_LIB_SRCS=" \
    src/njs_parser_expression.c \
    src/njs_generator.c \
    src/njs_disassembler.c \
+   src/njs_array_buffer.c \
 "
 
 NJS_LIB_TEST_SRCS=" \
diff -r 242395b814bb -r 3bef40125db2 src/njs_array_buffer.c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/njs_array_buffer.c	Wed Nov 27 14:02:04 2019 +0000
@@ -0,0 +1,268 @@
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) NGINX, Inc.
+ */
+
+
+#include <njs_main.h>
+
+
+njs_array_buffer_t *
+njs_array_buffer_alloc(njs_vm_t *vm, uint64_t size)
+{
+    njs_object_t        *proto;
+    njs_array_buffer_t  *array;
+
+    if (njs_slow_path(size > UINT32_MAX)) {
+        goto overflow;
+    }
+
+    array = njs_mp_alloc(vm->mem_pool, sizeof(njs_array_buffer_t));
+    if (njs_slow_path(array == NULL)) {
+        goto memory_error;
+    }
+
+    if (size > 0) {
+        array->u.data = njs_mp_zalloc(vm->mem_pool, size);
+        if (njs_slow_path(array->u.data == NULL)) {
+            goto memory_error;
+        }
+    }
+
+    proto = &vm->prototypes[NJS_OBJ_TYPE_ARRAY_BUFFER].object;
+
+    njs_lvlhsh_init(&array->object.hash);
+    njs_lvlhsh_init(&array->object.shared_hash);
+    array->object.__proto__ = proto;
+    array->object.type = NJS_ARRAY_BUFFER;
+    array->object.shared = 0;
+    array->object.extensible = 1;
+    array->size = size;
+
+    return array;
+
+memory_error:
+
+    njs_memory_error(vm);
+
+    return NULL;
+
+overflow:
+
+    njs_range_error(vm, "Invalid array length");
+
+    return NULL;
+}
+
+
+static njs_int_t
+njs_array_buffer_constructor(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
+    njs_index_t unused)
+{
+    uint32_t            size;
+    njs_int_t           ret;
+    njs_value_t         *value;
+    njs_array_buffer_t  *array;
+
+    if (!vm->top_frame->ctor) {
+        njs_type_error(vm, "Constructor ArrayBuffer requires 'new'");
+        return NJS_ERROR;
+    }
+
+    size = 0;
+    value = njs_arg(args, nargs, 1);
+
+    ret = njs_value_to_index(vm, value, &size);
+    if (njs_slow_path(ret != NJS_OK)) {
+        return NJS_ERROR;
+    }
+
+    array = njs_array_buffer_alloc(vm, size);
+    if (njs_slow_path(array == NULL)) {
+        return NJS_ERROR;
+    }
+
+    njs_set_array_buffer(&vm->retval, array);
+
+    return NJS_OK;
+}
+
+
+static njs_int_t
+njs_array_buffer_get_this(njs_vm_t *vm, njs_value_t *args,
+    njs_uint_t nargs, njs_index_t unused)
+{
+    vm->retval = args[0];
+
+    return NJS_OK;
+}
+
+
+static const njs_object_prop_t  njs_array_buffer_constructor_properties[] =
+{
+    /* ArrayBuffer.name == "ArrayBuffer". */
+    {
+        .type = NJS_PROPERTY,
+        .name = njs_string("name"),
+        .value = njs_string("ArrayBuffer"),
+        .configurable = 1,
+    },
+
+    /* ArrayBuffer.length == 1. */
+    {
+        .type = NJS_PROPERTY,
+        .name = njs_string("length"),
+        .value = njs_value(NJS_NUMBER, 1, 1.0),
+        .configurable = 1,
+    },
+
+    /* ArrayBuffer.prototype. */
+    {
+        .type = NJS_PROPERTY_HANDLER,
+        .name = njs_string("prototype"),
+        .value = njs_prop_handler(njs_object_prototype_create),
+    },
+
+    /* ArrayBuffer[Symbol.species] */
+    {
+        .type = NJS_PROPERTY,
+        .name = njs_wellknown_symbol(NJS_SYMBOL_SPECIES),
+        .value = njs_value(NJS_INVALID, 1, NAN),
+        .getter = njs_native_function(njs_array_buffer_get_this, 0),
+        .setter = njs_value(NJS_UNDEFINED, 0, NAN),
+        .writable = NJS_ATTRIBUTE_UNSET,
+        .configurable = 1,
+        .enumerable = 0,
+    },
+};
+
+
+const njs_object_init_t  njs_array_buffer_constructor_init = {
+    njs_array_buffer_constructor_properties,
+    njs_nitems(njs_array_buffer_constructor_properties),
+};
+
+
+static njs_int_t
+njs_array_buffer_prototype_byte_length(njs_vm_t *vm, njs_value_t *args,
+    njs_uint_t nargs, njs_index_t unused)
+{
+    njs_value_t         *value;
+    njs_array_buffer_t  *array;
+
+    value = njs_arg(args, nargs, 0);
+
+    if (!njs_is_array_buffer(value)) {
+        njs_type_error(vm, "Method ArrayBuffer.prototype.byteLength called "
+                       "on incompatible receiver");
+        return NJS_ERROR;
+    }
+
+    array = njs_array_buffer(value);
+
+    njs_set_number(&vm->retval, array->size);
+
+    return NJS_OK;
+}
+
+
+static njs_int_t
+njs_array_buffer_prototype_slice(njs_vm_t *vm, njs_value_t *args,
+    njs_uint_t nargs, njs_index_t unused)
+{
+    int64_t             len, start, end;
+    njs_int_t           ret;
+    njs_value_t         *value;
+    njs_array_buffer_t  *this, *buffer;
+
+    value = njs_arg(args, nargs, 0);
+
+    if (!njs_is_array_buffer(value)) {
+        njs_type_error(vm, "Method ArrayBuffer.prototype.slice called "
+                       "on incompatible receiver");
+        return NJS_ERROR;
+    }
+
+    this = njs_array_buffer(value);
+    len  = njs_array_buffer_size(this);
+    end  = len;
+
+    value = njs_arg(args, nargs, 1);
+
+    ret = njs_value_to_integer(vm, value, &start);
+    if (njs_slow_path(ret != NJS_OK)) {
+        return ret;
+    }
+
+    value = njs_arg(args, nargs, 2);
+
+    if (!njs_is_undefined(value)) {
+        ret = njs_value_to_integer(vm, value, &end);
+        if (njs_slow_path(ret != NJS_OK)) {
+            return ret;
+        }
+    }
+
+    buffer = njs_array_buffer_slice(vm, this, start, end);
+    if (njs_slow_path(buffer == NULL)) {
+        return NJS_ERROR;
+    }
+
+    njs_set_array_buffer(&vm->retval, buffer);
+
+    return NJS_OK;
+}
+
+
+static const njs_object_prop_t  njs_array_buffer_prototype_properties[] =
+{
+    {
+        .type = NJS_PROPERTY_HANDLER,
+        .name = njs_string("constructor"),
+        .value = njs_prop_handler(njs_object_prototype_create_constructor),
+        .writable = 1,
+        .configurable = 1,
+    },
+
+    {
+        .type = NJS_PROPERTY,
+        .name = njs_string("byteLength"),
+        .value = njs_value(NJS_INVALID, 1, NAN),
+        .getter = njs_native_function(njs_array_buffer_prototype_byte_length,
+                                      0),
+        .setter = njs_value(NJS_UNDEFINED, 0, NAN),
+        .writable = NJS_ATTRIBUTE_UNSET,
+        .configurable = 1,
+        .enumerable = 0,
+    },
+
+    {
+        .type = NJS_PROPERTY,
+        .name = njs_string("slice"),
+        .value = njs_native_function(njs_array_buffer_prototype_slice, 2),
+        .writable = 1,
+        .configurable = 1,
+        .enumerable = 0,
+    },
+
+    {
+        .type = NJS_PROPERTY,
+        .name = njs_wellknown_symbol(NJS_SYMBOL_TO_STRING_TAG),
+        .value = njs_string("ArrayBuffer"),
+        .configurable = 1,
+    },
+};
+
+
+const njs_object_init_t  njs_array_buffer_prototype_init = {
+    njs_array_buffer_prototype_properties,
+    njs_nitems(njs_array_buffer_prototype_properties),
+};
+
+
+const njs_object_type_init_t  njs_array_buffer_type_init = {
+    .constructor = njs_native_ctor(njs_array_buffer_constructor, 1, 0),
+    .prototype_props = &njs_array_buffer_prototype_init,
+    .constructor_props = &njs_array_buffer_constructor_init,
+    .prototype_value = { .object = { .type = NJS_OBJECT } },
+};
diff -r 242395b814bb -r 3bef40125db2 src/njs_array_buffer.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/njs_array_buffer.h	Wed Nov 27 14:02:04 2019 +0000
@@ -0,0 +1,46 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ * Copyright (C) NGINX, Inc.
+ */
+
+#ifndef _NJS_ARRAY_BUFFER_H_INCLUDED_
+#define _NJS_ARRAY_BUFFER_H_INCLUDED_
+
+
+#define njs_array_buffer_size(buffer)                                        \
+    ((buffer)->size)
+
+
+njs_array_buffer_t *njs_array_buffer_alloc(njs_vm_t *vm, uint64_t size);
+
+
+njs_inline njs_array_buffer_t *
+njs_array_buffer_slice(njs_vm_t *vm, njs_array_buffer_t *this, int64_t start,
+    int64_t end)
+{
+    int64_t             len, new_len, first, final;
+    njs_array_buffer_t  *new_buffer;
+
+    len = njs_array_buffer_size(this);
+
+    first = (start < 0) ? njs_max(len + start, 0) : njs_min(start, len);
+    final = (end < 0) ? njs_max(len + end, 0) : njs_min(end, len);
+
+    new_len = njs_max(final - first, 0);
+
+    new_buffer = njs_array_buffer_alloc(vm, new_len);
+    if (new_buffer == NULL) {
+        return NULL;
+    }
+
+    memcpy(new_buffer->u.u8, &this->u.u8[first], new_len);
+
+    return new_buffer;
+}
+
+
+extern const njs_object_type_init_t  njs_array_buffer_type_init;
+
+
+#endif /* _NJS_ARRAY_BUFFER_H_INCLUDED_ */
diff -r 242395b814bb -r 3bef40125db2 src/njs_builtin.c
--- a/src/njs_builtin.c	Fri Dec 06 14:59:48 2019 +0300
+++ b/src/njs_builtin.c	Wed Nov 27 14:02:04 2019 +0000
@@ -58,6 +58,7 @@ static const njs_object_type_init_t *con
 
     &njs_obj_type_init,
     &njs_array_type_init,
+    &njs_array_buffer_type_init,
     &njs_boolean_type_init,
     &njs_number_type_init,
     &njs_symbol_type_init,
@@ -1099,6 +1100,16 @@ static const njs_object_prop_t  njs_glob
 
     {
         .type = NJS_PROPERTY_HANDLER,
+        .name = njs_string("ArrayBuffer"),
+        .value = njs_prop_handler2(njs_top_level_constructor,
+                                   NJS_OBJ_TYPE_ARRAY_BUFFER,
+                                   NJS_ARRAY_BUFFER_HASH),
+        .writable = 1,
+        .configurable = 1,
+    },
+
+    {
+        .type = NJS_PROPERTY_HANDLER,
         .name = njs_string("Boolean"),
         .value = njs_prop_handler2(njs_top_level_constructor,
                                    NJS_OBJ_TYPE_BOOLEAN, NJS_BOOLEAN_HASH),
diff -r 242395b814bb -r 3bef40125db2 src/njs_json.c
--- a/src/njs_json.c	Fri Dec 06 14:59:48 2019 +0300
+++ b/src/njs_json.c	Wed Nov 27 14:02:04 2019 +0000
@@ -2109,6 +2109,7 @@ njs_dump_is_object(const njs_value_t *va
 {
     return (value->type == NJS_OBJECT && !njs_object(value)->error_data)
            || (value->type == NJS_ARRAY)
+           || (value->type == NJS_ARRAY_BUFFER)
            || (value->type == NJS_OBJECT_VALUE)
            || njs_dump_is_external_object(value);
 }
diff -r 242395b814bb -r 3bef40125db2 src/njs_main.h
--- a/src/njs_main.h	Fri Dec 06 14:59:48 2019 +0300
+++ b/src/njs_main.h	Wed Nov 27 14:02:04 2019 +0000
@@ -59,6 +59,7 @@
 #include <njs_object.h>
 #include <njs_object_hash.h>
 #include <njs_array.h>
+#include <njs_array_buffer.h>
 #include <njs_function.h>
 #include <njs_regexp.h>
 #include <njs_regexp_pattern.h>
diff -r 242395b814bb -r 3bef40125db2 src/njs_object.c
--- a/src/njs_object.c	Fri Dec 06 14:59:48 2019 +0300
+++ b/src/njs_object.c	Wed Nov 27 14:02:04 2019 +0000
@@ -2356,6 +2356,8 @@ static const njs_value_t  njs_object_obj
                                      njs_long_string("[object Object]");
 static const njs_value_t  njs_object_array_string =
                                      njs_string("[object Array]");
+static const njs_value_t  njs_object_array_buffer_string =
+                                     njs_long_string("[object ArrayBuffer]");
 static const njs_value_t  njs_object_function_string =
                                      njs_long_string("[object Function]");
 static const njs_value_t  njs_object_regexp_string =
@@ -2408,6 +2410,7 @@ njs_object_prototype_to_string(njs_vm_t 
         &njs_object_regexp_string,
         &njs_object_date_string,
         &njs_object_object_string,
+        &njs_object_array_buffer_string,
     };
 
     value = njs_argument(args, 0);
diff -r 242395b814bb -r 3bef40125db2 src/njs_object_hash.h
--- a/src/njs_object_hash.h	Fri Dec 06 14:59:48 2019 +0300
+++ b/src/njs_object_hash.h	Wed Nov 27 14:02:04 2019 +0000
@@ -561,4 +561,19 @@
         'U'), 'R'), 'I'), 'E'), 'r'), 'r'), 'o'), 'r')
 
 
+#define NJS_ARRAY_BUFFER_HASH                                                 \
+    njs_djb_hash_add(                                                         \
+    njs_djb_hash_add(                                                         \
+    njs_djb_hash_add(                                                         \
+    njs_djb_hash_add(                                                         \
+    njs_djb_hash_add(                                                         \
+    njs_djb_hash_add(                                                         \
+    njs_djb_hash_add(                                                         \
+    njs_djb_hash_add(                                                         \
+    njs_djb_hash_add(                                                         \
+    njs_djb_hash_add(                                                         \
+    njs_djb_hash_add(NJS_DJB_HASH_INIT,                                       \
+        'A'), 'r'), 'r'), 'a'), 'y'), 'B'), 'u'), 'f'), 'f'), 'e'), 'r')
+
+
 #endif /* _NJS_OBJECT_HASH_H_INCLUDED_ */
diff -r 242395b814bb -r 3bef40125db2 src/njs_value.c
--- a/src/njs_value.c	Fri Dec 06 14:59:48 2019 +0300
+++ b/src/njs_value.c	Wed Nov 27 14:02:04 2019 +0000
@@ -338,6 +338,9 @@ njs_type_string(njs_value_type_t type)
     case NJS_ARRAY:
         return "array";
 
+    case NJS_ARRAY_BUFFER:
+        return "object arraybuffer";
+
     case NJS_OBJECT_BOOLEAN:
         return "object boolean";
 
@@ -547,6 +550,7 @@ njs_property_query(njs_vm_t *vm, njs_pro
 
     case NJS_OBJECT:
     case NJS_ARRAY:
+    case NJS_ARRAY_BUFFER:
     case NJS_OBJECT_BOOLEAN:
     case NJS_OBJECT_NUMBER:
     case NJS_OBJECT_SYMBOL:
diff -r 242395b814bb -r 3bef40125db2 src/njs_value.h
--- a/src/njs_value.h	Fri Dec 06 14:59:48 2019 +0300
+++ b/src/njs_value.h	Wed Nov 27 14:02:04 2019 +0000
@@ -68,6 +68,7 @@ typedef enum {
     NJS_REGEXP                = 0x17,
     NJS_DATE                  = 0x18,
     NJS_OBJECT_VALUE          = 0x19,
+    NJS_ARRAY_BUFFER          = 0x1A,
     NJS_VALUE_TYPE_MAX
 } njs_value_type_t;
 
@@ -79,6 +80,7 @@ typedef struct njs_object_value_s     nj
 typedef struct njs_function_lambda_s  njs_function_lambda_t;
 typedef struct njs_regexp_pattern_s   njs_regexp_pattern_t;
 typedef struct njs_array_s            njs_array_t;
+typedef struct njs_array_buffer_s     njs_array_buffer_t;
 typedef struct njs_regexp_s           njs_regexp_t;
 typedef struct njs_date_s             njs_date_t;
 typedef struct njs_property_next_s    njs_property_next_t;
@@ -138,6 +140,7 @@ union njs_value_s {
             double                    number;
             njs_object_t              *object;
             njs_array_t               *array;
+            njs_array_buffer_t        *array_buffer;
             njs_object_value_t        *object_value;
             njs_function_t            *function;
             njs_function_lambda_t     *lambda;
@@ -223,6 +226,26 @@ struct njs_array_s {
 };
 
 
+struct njs_array_buffer_s {
+    njs_object_t                      object;
+    size_t                            size;
+    union {
+        uint8_t                       *u8;
+        uint16_t                      *u16;
+        uint32_t                      *u32;
+        uint64_t                      *u64;
+        int8_t                        *i8;
+        int16_t                       *i16;
+        int32_t                       *i32;
+        int64_t                       *i64;
+        float                         *f32;
+        double                        *f64;
+
+        void                          *data;
+    } u;
+};
+
+
 typedef struct {
     union {
         uint32_t                      count;
@@ -601,6 +624,10 @@ typedef struct {
     ((value)->type == NJS_ARRAY)
 
 
+#define njs_is_array_buffer(value)                                            \
+    ((value)->type == NJS_ARRAY_BUFFER)
+
+
 #define njs_is_function(value)                                                \
     ((value)->type == NJS_FUNCTION)
 
@@ -669,6 +696,10 @@ typedef struct {
     ((value)->data.u.array->length)
 
 
+#define njs_array_buffer(value)                                               \
+    ((value)->data.u.array_buffer)
+
+
 #define njs_array_start(value)                                                \
     ((value)->data.u.array->start)
 
@@ -817,6 +848,15 @@ njs_set_array(njs_value_t *value, njs_ar
 
 
 njs_inline void
+njs_set_array_buffer(njs_value_t *value, njs_array_buffer_t *array)
+{
+    value->data.u.array_buffer = array;
+    value->type = NJS_ARRAY_BUFFER;
+    value->data.truth = 1;
+}
+
+
+njs_inline void
 njs_set_function(njs_value_t *value, njs_function_t *function)
 {
     value->data.u.function = function;
diff -r 242395b814bb -r 3bef40125db2 src/njs_vm.h
--- a/src/njs_vm.h	Fri Dec 06 14:59:48 2019 +0300
+++ b/src/njs_vm.h	Wed Nov 27 14:02:04 2019 +0000
@@ -82,6 +82,7 @@ typedef enum {
 typedef enum {
     NJS_OBJ_TYPE_OBJECT = 0,
     NJS_OBJ_TYPE_ARRAY,
+    NJS_OBJ_TYPE_ARRAY_BUFFER,
     NJS_OBJ_TYPE_BOOLEAN,
     NJS_OBJ_TYPE_NUMBER,
     NJS_OBJ_TYPE_SYMBOL,
diff -r 242395b814bb -r 3bef40125db2 src/test/njs_unit_test.c
--- a/src/test/njs_unit_test.c	Fri Dec 06 14:59:48 2019 +0300
+++ b/src/test/njs_unit_test.c	Wed Nov 27 14:02:04 2019 +0000
@@ -4733,6 +4733,55 @@ static njs_unit_test_t  njs_test[] =
                  "Array.prototype.fill.call(o, 2).a"),
       njs_str("4") },
 
+    { njs_str("ArrayBuffer()"),
+      njs_str("TypeError: Constructor ArrayBuffer requires 'new'") },
+
+    { njs_str("new ArrayBuffer()"),
+      njs_str("[object ArrayBuffer]") },
+
+    { njs_str("ArrayBuffer.prototype.constructor.name === 'ArrayBuffer'"),
+      njs_str("true") },
+
+    { njs_str("ArrayBuffer.prototype.constructor()"),
+      njs_str("TypeError: Constructor ArrayBuffer requires 'new'") },
+
+    { njs_str("ArrayBuffer.name"),
+      njs_str("ArrayBuffer") },
+
+    { njs_str("ArrayBuffer[Symbol.species]"),
+      njs_str("[object Function]") },
+
+    { njs_str("ArrayBuffer.prototype[Symbol.toStringTag]"),
+      njs_str("ArrayBuffer") },
+
+    { njs_str("var desc = Object.getOwnPropertyDescriptor(ArrayBuffer,"
+              "Symbol.species); desc.get"),
+      njs_str("[object Function]") },
+
+    { njs_str("var ctor = ArrayBuffer[Symbol.species]; var a = new ctor(100);"
+              "a.byteLength;"),
+      njs_str("100") },
+
+    { njs_str("var a = new ArrayBuffer(); a.byteLength"),
+      njs_str("0") },
+
+    { njs_str("var a = new ArrayBuffer.prototype.constructor(10); a.byteLength"),
+      njs_str("10") },
+
+    { njs_str("var get = Object.getOwnPropertyDescriptor(ArrayBuffer.prototype, 'byteLength').get;"
+              "get.call([])"),
+      njs_str("TypeError: Method ArrayBuffer.prototype.byteLength called on incompatible receiver") },
+
+    { njs_str("[undefined, 1, 10, 1000, null, NaN, false, {}, [1,2,3], Object(1),'10',"
+              " -1, -Infinity, Infinity, 2**50]"
+              ".map(v=>{ var a; try { a = new ArrayBuffer(v) } catch (e) {return e.name} return a.byteLength})"),
+      njs_str("0,1,10,1000,0,0,0,0,0,1,10,RangeError,RangeError,RangeError,RangeError") },
+
+    { njs_str("var buffer = new ArrayBuffer(16);"
+              "[[4,12], [-1,-1], [-1,10], [0, -1], [0, -16], [0,-17]]"
+              ".map(pr=>buffer.slice(pr[0], pr[1]).byteLength)"),
+      njs_str("8,0,0,15,0,0") },
+
 #if NJS_HAVE_LARGE_STACK
     { njs_str("var o = Object({length: 3});"
                  "Object.defineProperty(o, '0', {set: function(v){this[0] = 2 * v}});"


More information about the nginx-devel mailing list