/*
* Copyright (C) Igor Sysoev
+ * Copyright (C) Dmitry Volyntsev
* Copyright (C) NGINX, Inc.
*/
njs_typed_array_t *array;
this = njs_argument(args, 0);
- if (!njs_is_typed_array(this)) {
+ if (!njs_is_typed_array(this) && !njs_is_data_view(this)) {
njs_type_error(vm, "Method TypedArray.prototype.buffer called "
"on incompatible receiver");
return NJS_ERROR;
njs_typed_array_t *array;
this = njs_argument(args, 0);
- if (!njs_is_typed_array(this)) {
+ if (!njs_is_typed_array(this) && !njs_is_data_view(this)) {
njs_type_error(vm, "Method TypedArray.prototype.byteLength called "
"on incompatible receiver");
return NJS_ERROR;
njs_typed_array_t *array;
this = njs_argument(args, 0);
- if (!njs_is_typed_array(this)) {
+ if (!njs_is_typed_array(this) && !njs_is_data_view(this)) {
njs_type_error(vm, "Method TypedArray.prototype.byteOffset called "
"on incompatible receiver");
return NJS_ERROR;
};
+static njs_int_t
+njs_data_view_constructor(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
+ njs_index_t unused)
+{
+ uint64_t size, offset;
+ njs_int_t ret;
+ njs_data_view_t *view;
+ njs_array_buffer_t *buffer;
+
+ if (!vm->top_frame->ctor) {
+ njs_type_error(vm, "Constructor of DataView requires 'new'");
+ return NJS_ERROR;
+ }
+
+ if (!njs_is_array_buffer(njs_arg(args, nargs, 1))) {
+ njs_type_error(vm, "buffer is not an ArrayBuffer");
+ return NJS_ERROR;
+ }
+
+ size = 0;
+ offset = 0;
+
+ buffer = njs_array_buffer(njs_argument(args, 1));
+
+ ret = njs_value_to_index(vm, njs_arg(args, nargs, 2), &offset);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return NJS_ERROR;
+ }
+
+ if (!njs_is_undefined(njs_arg(args, nargs, 3))) {
+ ret = njs_value_to_index(vm, njs_argument(args, 3), &size);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return NJS_ERROR;
+ }
+
+ if (njs_slow_path((offset + size) > buffer->size)) {
+ njs_range_error(vm, "Invalid DataView length: %uL", size);
+ return NJS_ERROR;
+ }
+
+ } else {
+ if (offset > buffer->size) {
+ njs_range_error(vm, "byteOffset %uL is outside the bound of "
+ "the buffer", offset);
+ return NJS_ERROR;
+ }
+
+ size = buffer->size - offset;
+ }
+
+ view = njs_mp_zalloc(vm->mem_pool, sizeof(njs_data_view_t));
+ if (njs_slow_path(view == NULL)) {
+ goto memory_error;
+ }
+
+ view->buffer = buffer;
+ view->offset = offset;
+ view->byte_length = size;
+ view->type = NJS_OBJ_TYPE_DATA_VIEW;
+
+ njs_lvlhsh_init(&view->object.hash);
+ njs_lvlhsh_init(&view->object.shared_hash);
+ view->object.__proto__ = &vm->prototypes[view->type].object;
+ view->object.type = NJS_DATA_VIEW;
+ view->object.extensible = 1;
+
+ njs_set_data_view(&vm->retval, view);
+
+ return NJS_OK;
+
+memory_error:
+
+ njs_memory_error(vm);
+
+ return NJS_ERROR;
+}
+
+
+static const njs_object_prop_t njs_data_view_constructor_props[] =
+{
+ {
+ .type = NJS_PROPERTY,
+ .name = njs_string("name"),
+ .value = njs_string("DataView"),
+ .configurable = 1,
+ },
+
+ {
+ .type = NJS_PROPERTY,
+ .name = njs_string("length"),
+ .value = njs_value(NJS_NUMBER, 1, 1.0),
+ .configurable = 1,
+ },
+
+ {
+ .type = NJS_PROPERTY_HANDLER,
+ .name = njs_string("prototype"),
+ .value = njs_prop_handler(njs_object_prototype_create),
+ },
+};
+
+
+static const njs_object_init_t njs_data_view_constructor_init = {
+ njs_data_view_constructor_props,
+ njs_nitems(njs_data_view_constructor_props),
+};
+
+
+typedef union {
+ float f;
+ uint32_t u;
+} njs_conv_f32_t;
+
+
+typedef union {
+ double f;
+ uint64_t u;
+} njs_conv_f64_t;
+
+
+static njs_int_t
+njs_data_view_prototype_get(njs_vm_t *vm, njs_value_t *args,
+ njs_uint_t nargs, njs_index_t type)
+{
+ double v;
+ uint32_t u32;
+ uint64_t index;
+ njs_int_t ret;
+ njs_bool_t swap;
+ njs_value_t *this;
+ const uint8_t *u8;
+ njs_conv_f32_t conv_f32;
+ njs_conv_f64_t conv_f64;
+ njs_data_view_t *view;
+ njs_array_buffer_t *buffer;
+
+ this = njs_argument(args, 0);
+ if (njs_slow_path(!njs_is_data_view(this))) {
+ njs_type_error(vm, "this is not a DataView");
+ return NJS_ERROR;
+ }
+
+ ret = njs_value_to_index(vm, njs_arg(args, nargs, 1), &index);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return NJS_ERROR;
+ }
+
+ swap = njs_bool(njs_arg(args, nargs, 2));
+
+#if NJS_HAVE_LITTLE_ENDIAN
+ swap = !swap;
+#endif
+
+ view = njs_data_view(this);
+
+ if (njs_typed_array_element_size(type) + index > view->byte_length) {
+ njs_range_error(vm, "index %uL is outside the bound of the buffer",
+ index);
+ return NJS_ERROR;
+ }
+
+ buffer = view->buffer;
+ u8 = &buffer->u.u8[index + view->offset];
+
+ switch (type) {
+ case NJS_OBJ_TYPE_UINT8_ARRAY:
+ v = *u8;
+ break;
+
+ case NJS_OBJ_TYPE_INT8_ARRAY:
+ v = (int8_t) *u8;
+ break;
+
+ case NJS_OBJ_TYPE_UINT16_ARRAY:
+ u32 = *((uint16_t *) u8);
+
+ if (swap) {
+ u32 = njs_bswap_u16(u32);
+ }
+
+ v = u32;
+ break;
+
+ case NJS_OBJ_TYPE_INT16_ARRAY:
+ u32 = *((uint16_t *) u8);
+
+ if (swap) {
+ u32 = njs_bswap_u16(u32);
+ }
+
+ v = (int16_t) u32;
+ break;
+
+ case NJS_OBJ_TYPE_UINT32_ARRAY:
+ case NJS_OBJ_TYPE_INT32_ARRAY:
+ case NJS_OBJ_TYPE_FLOAT32_ARRAY:
+ u32 = *((uint32_t *) u8);
+
+ if (swap) {
+ u32 = njs_bswap_u32(u32);
+ }
+
+ switch (type) {
+ case NJS_OBJ_TYPE_UINT32_ARRAY:
+ v = u32;
+ break;
+
+ case NJS_OBJ_TYPE_INT32_ARRAY:
+ v = (int32_t) u32;
+ break;
+
+ default:
+ conv_f32.u = u32;
+ v = conv_f32.f;
+ }
+
+ break;
+
+ default:
+ /* NJS_OBJ_TYPE_FLOAT64_ARRAY. */
+
+ conv_f64.u = *((uint64_t *) u8);
+
+ if (swap) {
+ conv_f64.u = njs_bswap_u64(conv_f64.u);
+ }
+
+ v = conv_f64.f;
+ }
+
+ njs_set_number(&vm->retval, v);
+
+ return NJS_OK;
+}
+
+
+static njs_int_t
+njs_data_view_prototype_set(njs_vm_t *vm, njs_value_t *args,
+ njs_uint_t nargs, njs_index_t type)
+{
+ double v;
+ uint8_t *u8;
+ uint32_t u32;
+ uint64_t index;
+ njs_int_t ret;
+ njs_bool_t swap;
+ njs_value_t *this;
+ njs_conv_f32_t conv_f32;
+ njs_conv_f64_t conv_f64;
+ njs_data_view_t *view;
+ njs_array_buffer_t *buffer;
+
+ this = njs_argument(args, 0);
+ if (njs_slow_path(!njs_is_data_view(this))) {
+ njs_type_error(vm, "this is not a DataView");
+ return NJS_ERROR;
+ }
+
+ ret = njs_value_to_index(vm, njs_arg(args, nargs, 1), &index);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return NJS_ERROR;
+ }
+
+ ret = njs_value_to_number(vm, njs_arg(args, nargs, 2), &v);
+ if (njs_slow_path(ret != NJS_OK)) {
+ return NJS_ERROR;
+ }
+
+ swap = njs_bool(njs_arg(args, nargs, 3));
+
+#if NJS_HAVE_LITTLE_ENDIAN
+ swap = !swap;
+#endif
+
+ view = njs_data_view(this);
+
+ if (njs_typed_array_element_size(type) + index > view->byte_length) {
+ njs_range_error(vm, "index %uL is outside the bound of the buffer",
+ index);
+ return NJS_ERROR;
+ }
+
+ buffer = view->buffer;
+ u8 = &buffer->u.u8[index + view->offset];
+
+ switch (type) {
+ case NJS_OBJ_TYPE_UINT8_ARRAY:
+ case NJS_OBJ_TYPE_INT8_ARRAY:
+ *u8 = njs_number_to_int32(v);
+ break;
+
+ case NJS_OBJ_TYPE_UINT16_ARRAY:
+ case NJS_OBJ_TYPE_INT16_ARRAY:
+ u32 = (uint16_t) njs_number_to_int32(v);
+
+ if (swap) {
+ u32 = njs_bswap_u16(u32);
+ }
+
+ *((uint16_t *) u8) = u32;
+ break;
+
+ case NJS_OBJ_TYPE_UINT32_ARRAY:
+ case NJS_OBJ_TYPE_INT32_ARRAY:
+ u32 = njs_number_to_int32(v);
+
+ if (swap) {
+ u32 = njs_bswap_u32(u32);
+ }
+
+ *((uint32_t *) u8) = u32;
+ break;
+
+ case NJS_OBJ_TYPE_FLOAT32_ARRAY:
+ conv_f32.f = (float) v;
+
+ if (swap) {
+ conv_f32.u = njs_bswap_u32(conv_f32.u);
+ }
+
+ *((uint32_t *) u8) = conv_f32.u;
+ break;
+
+ default:
+ /* NJS_OBJ_TYPE_FLOAT64_ARRAY. */
+
+ conv_f64.f = v;
+
+ if (swap) {
+ conv_f64.u = njs_bswap_u64(conv_f64.u);
+ }
+
+ *((uint64_t *) u8) = conv_f64.u;
+ }
+
+ njs_set_undefined(&vm->retval);
+
+ return NJS_OK;
+}
+
+
+static const njs_object_prop_t njs_data_view_prototype_properties[] =
+{
+ {
+ .type = NJS_PROPERTY,
+ .name = njs_wellknown_symbol(NJS_SYMBOL_TO_STRING_TAG),
+ .value = njs_string("DataView"),
+ .configurable = 1,
+ },
+
+ {
+ .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("buffer"),
+ .value = njs_value(NJS_INVALID, 1, NAN),
+ .getter = njs_native_function(njs_typed_array_prototype_buffer, 0),
+ .setter = njs_value(NJS_UNDEFINED, 0, NAN),
+ .writable = NJS_ATTRIBUTE_UNSET,
+ .configurable = 1,
+ .enumerable = 0,
+ },
+
+ {
+ .type = NJS_PROPERTY,
+ .name = njs_string("byteLength"),
+ .value = njs_value(NJS_INVALID, 1, NAN),
+ .getter = njs_native_function(njs_typed_array_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("byteOffset"),
+ .value = njs_value(NJS_INVALID, 1, NAN),
+ .getter = njs_native_function(njs_typed_array_prototype_byte_offset, 0),
+ .setter = njs_value(NJS_UNDEFINED, 0, NAN),
+ .writable = NJS_ATTRIBUTE_UNSET,
+ .configurable = 1,
+ .enumerable = 0,
+ },
+
+ {
+ .type = NJS_PROPERTY,
+ .name = njs_string("getUint8"),
+ .value = njs_native_function2(njs_data_view_prototype_get, 1,
+ NJS_OBJ_TYPE_UINT8_ARRAY),
+ .writable = 1,
+ .configurable = 1,
+ },
+
+ {
+ .type = NJS_PROPERTY,
+ .name = njs_string("getInt8"),
+ .value = njs_native_function2(njs_data_view_prototype_get, 1,
+ NJS_OBJ_TYPE_INT8_ARRAY),
+ .writable = 1,
+ .configurable = 1,
+ },
+
+ {
+ .type = NJS_PROPERTY,
+ .name = njs_string("getUint16"),
+ .value = njs_native_function2(njs_data_view_prototype_get, 1,
+ NJS_OBJ_TYPE_UINT16_ARRAY),
+ .writable = 1,
+ .configurable = 1,
+ },
+
+ {
+ .type = NJS_PROPERTY,
+ .name = njs_string("getInt16"),
+ .value = njs_native_function2(njs_data_view_prototype_get, 1,
+ NJS_OBJ_TYPE_INT16_ARRAY),
+ .writable = 1,
+ .configurable = 1,
+ },
+
+ {
+ .type = NJS_PROPERTY,
+ .name = njs_string("getUint32"),
+ .value = njs_native_function2(njs_data_view_prototype_get, 1,
+ NJS_OBJ_TYPE_UINT32_ARRAY),
+ .writable = 1,
+ .configurable = 1,
+ },
+
+ {
+ .type = NJS_PROPERTY,
+ .name = njs_string("getInt32"),
+ .value = njs_native_function2(njs_data_view_prototype_get, 1,
+ NJS_OBJ_TYPE_INT32_ARRAY),
+ .writable = 1,
+ .configurable = 1,
+ },
+
+ {
+ .type = NJS_PROPERTY,
+ .name = njs_string("getFloat32"),
+ .value = njs_native_function2(njs_data_view_prototype_get, 1,
+ NJS_OBJ_TYPE_FLOAT32_ARRAY),
+ .writable = 1,
+ .configurable = 1,
+ },
+
+ {
+ .type = NJS_PROPERTY,
+ .name = njs_string("getFloat64"),
+ .value = njs_native_function2(njs_data_view_prototype_get, 1,
+ NJS_OBJ_TYPE_FLOAT64_ARRAY),
+ .writable = 1,
+ .configurable = 1,
+ },
+
+ {
+ .type = NJS_PROPERTY,
+ .name = njs_string("setUint8"),
+ .value = njs_native_function2(njs_data_view_prototype_set, 2,
+ NJS_OBJ_TYPE_UINT8_ARRAY),
+ .writable = 1,
+ .configurable = 1,
+ },
+
+ {
+ .type = NJS_PROPERTY,
+ .name = njs_string("setInt8"),
+ .value = njs_native_function2(njs_data_view_prototype_set, 2,
+ NJS_OBJ_TYPE_INT8_ARRAY),
+ .writable = 1,
+ .configurable = 1,
+ },
+
+ {
+ .type = NJS_PROPERTY,
+ .name = njs_string("setUint16"),
+ .value = njs_native_function2(njs_data_view_prototype_set, 2,
+ NJS_OBJ_TYPE_UINT16_ARRAY),
+ .writable = 1,
+ .configurable = 1,
+ },
+
+ {
+ .type = NJS_PROPERTY,
+ .name = njs_string("setInt16"),
+ .value = njs_native_function2(njs_data_view_prototype_set, 2,
+ NJS_OBJ_TYPE_INT16_ARRAY),
+ .writable = 1,
+ .configurable = 1,
+ },
+
+ {
+ .type = NJS_PROPERTY,
+ .name = njs_string("setUint32"),
+ .value = njs_native_function2(njs_data_view_prototype_set, 2,
+ NJS_OBJ_TYPE_UINT32_ARRAY),
+ .writable = 1,
+ .configurable = 1,
+ },
+
+ {
+ .type = NJS_PROPERTY,
+ .name = njs_string("setInt32"),
+ .value = njs_native_function2(njs_data_view_prototype_set, 2,
+ NJS_OBJ_TYPE_INT32_ARRAY),
+ .writable = 1,
+ .configurable = 1,
+ },
+
+ {
+ .type = NJS_PROPERTY,
+ .name = njs_string("setFloat32"),
+ .value = njs_native_function2(njs_data_view_prototype_set, 2,
+ NJS_OBJ_TYPE_FLOAT32_ARRAY),
+ .writable = 1,
+ .configurable = 1,
+ },
+
+ {
+ .type = NJS_PROPERTY,
+ .name = njs_string("setFloat64"),
+ .value = njs_native_function2(njs_data_view_prototype_set, 2,
+ NJS_OBJ_TYPE_FLOAT64_ARRAY),
+ .writable = 1,
+ .configurable = 1,
+ },
+};
+
+
+static const njs_object_init_t njs_data_view_prototype_init = {
+ njs_data_view_prototype_properties,
+ njs_nitems(njs_data_view_prototype_properties),
+};
+
+
+const njs_object_type_init_t njs_data_view_type_init = {
+ .constructor = njs_native_ctor(njs_data_view_constructor, 1, 0),
+ .prototype_props = &njs_data_view_prototype_init,
+ .constructor_props = &njs_data_view_constructor_init,
+ .prototype_value = { .object = { .type = NJS_OBJECT } },
+};
+
+
static const njs_object_prop_t njs_typed_array_u8_constructor_props[] =
{
{
{ njs_str("(new Float64Array([255,255,NaN,3,NaN,Infinity,3,-Infinity,0,-0,2,1,-5])).slice(2).sort()"),
njs_str("-Infinity,-5,0,0,1,2,3,3,Infinity,NaN,NaN") },
+ { njs_str("(new DataView(new ArrayBuffer(3)))"),
+ njs_str("[object DataView]") },
+
+ { njs_str("(new DataView(new ArrayBuffer(3))).buffer"),
+ njs_str("[object ArrayBuffer]") },
+
+ { njs_str("(new DataView(new ArrayBuffer(3))).byteLength"),
+ njs_str("3") },
+
+ { njs_str("(new DataView(new ArrayBuffer(3), 1)).byteLength"),
+ njs_str("2") },
+
+ { njs_str("(new DataView(new ArrayBuffer(3), 3)).byteLength"),
+ njs_str("0") },
+
+ { njs_str("(new DataView(new ArrayBuffer(3), 1)).byteOffset"),
+ njs_str("1") },
+
+ { njs_str("(new DataView(new ArrayBuffer(3), 1, 1)).byteLength"),
+ njs_str("1") },
+
+ { njs_str("(new DataView(new ArrayBuffer(3), 4))"),
+ njs_str("RangeError: byteOffset 4 is outside the bound of the buffer") },
+
+ { njs_str("(new DataView(new ArrayBuffer(3), 1,3))"),
+ njs_str("RangeError: Invalid DataView length: 3") },
+
+ { njs_str("var u8 = new Uint8Array([255, 129, 130, 131, 4, 5, 6, 7, 8, 9, 255]); "
+ "var dv = new DataView(u8.buffer, 1); "
+ "['getUint8', 'getInt8',"
+ " 'getUint16', 'getInt16',"
+ " 'getUint32', 'getInt32',"
+ " 'getFloat32','getFloat64'"
+ "]"
+ ".map(fn => [dv[fn](0), dv[fn](0,1), dv[fn](1), dv[fn](1,1)])"),
+ njs_str("129,129,130,130,"
+ "-127,-127,-126,-126,"
+ "33154,33409,33411,33666,"
+ "-32382,-32127,-32125,-31870,"
+ "2172814084,75727489,2189624325,84181890,"
+ "-2122153212,75727489,-2105342971,84181890,"
+ "-4.794245620412925e-38,3.091780090135418e-36,-1.9251027092506622e-37,6.230764342760857e-36,"
+ "-2.159546358334202e-301,5.447603729090798e-270,-1.4538065947240604e-296,3.72581468952343e-265") },
+
+ { njs_str("var u8 = new Uint8Array(10);"
+ "var dv = new DataView(u8.buffer, 1);"
+ "var u8view = new Uint8Array(u8.buffer, 1);"
+ "function run(test) {"
+ " var fn = test[0];"
+ " var val = test[1];"
+ " var size = parseInt(fn.match(/\\d+/)) / 8;"
+ " "
+ " return [[0], [0,1],[1], [1,1]].map(args => {"
+ " var offset = args[0];"
+ " var le = args[1];"
+ " u8.fill(0); "
+ " dv[fn].apply(dv, [offset, val, le]);"
+ " return `[${u8view.subarray(0, offset + size)}]`;"
+ " })"
+ "};"
+ "["
+ " ['setUint8', 129],"
+ " ['setInt8', -127],"
+ " ['setUint16', 33154],"
+ " ['setInt16', -32382],"
+ " ['setUint32', 2172814084],"
+ " ['setInt32', -2122153212],"
+ " ['setFloat32', -4.794245620412925e-38],"
+ " ['setFloat64', -2.159546358334202e-301],"
+ "]"
+ ".map(t => run(t))"),
+ njs_str("[129],[129],[0,129],[0,129],"
+ "[129],[129],[0,129],[0,129],"
+ "[129,130],[130,129],[0,129,130],[0,130,129],"
+ "[129,130],[130,129],[0,129,130],[0,130,129],"
+ "[129,130,131,4],[4,131,130,129],[0,129,130,131,4],[0,4,131,130,129],"
+ "[129,130,131,4],[4,131,130,129],[0,129,130,131,4],[0,4,131,130,129],"
+ "[129,130,131,4],[4,131,130,129],[0,129,130,131,4],[0,4,131,130,129],"
+ "[129,130,131,4,5,6,7,8],[8,7,6,5,4,131,130,129],[0,129,130,131,4,5,6,7,8],[0,8,7,6,5,4,131,130,129]"
+ ) },
+
+ { njs_str("var u8 = new Uint8Array([1,2,3]); "
+ "var dv = new DataView(u8.buffer); "
+ "dv.getUint16(2)"),
+ njs_str("RangeError: index 2 is outside the bound of the buffer") },
+
#if NJS_HAVE_LARGE_STACK
{ njs_str("var o = Object({length: 3});"
"Object.defineProperty(o, '0', {set: function(v){this[0] = 2 * v}});"