[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