| |
| /* |
| * Copyright (C) Igor Sysoev |
| * Copyright (C) Dmitry Volyntsev |
| * Copyright (C) NGINX, Inc. |
| */ |
| |
| |
| #include <njs_main.h> |
| |
| |
| typedef enum { |
| NJS_ARRAY_EVERY = 0, |
| NJS_ARRAY_FOR_EACH, |
| NJS_ARRAY_SOME, |
| NJS_ARRAY_FIND, |
| NJS_ARRAY_FIND_INDEX, |
| NJS_ARRAY_FILTER, |
| NJS_ARRAY_MAP, |
| } njs_array_iterator_fun_t; |
| |
| |
| static void njs_typed_array_prop_set(njs_vm_t *vm, njs_typed_array_t *array, |
| uint32_t index, double v); |
| |
| |
| njs_typed_array_t * |
| njs_typed_array_alloc(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, |
| njs_bool_t zeroing, njs_object_type_t type) |
| { |
| double num; |
| int64_t i, length; |
| uint32_t element_size; |
| uint64_t size, offset; |
| njs_int_t ret; |
| njs_value_t *value, prop; |
| njs_array_t *src_array; |
| njs_typed_array_t *array, *src_tarray; |
| njs_array_buffer_t *buffer; |
| |
| size = 0; |
| length = 0; |
| offset = 0; |
| |
| buffer = NULL; |
| src_array = NULL; |
| src_tarray = NULL; |
| |
| element_size = njs_typed_array_element_size(type); |
| |
| value = njs_arg(args, nargs, 0); |
| |
| if (njs_is_array_buffer(value)) { |
| buffer = njs_array_buffer(value); |
| |
| ret = njs_value_to_index(vm, njs_arg(args, nargs, 1), &offset); |
| if (njs_slow_path(ret != NJS_OK)) { |
| return NULL; |
| } |
| |
| if (njs_slow_path((offset % element_size) != 0)) { |
| njs_range_error(vm, "start offset must be multiple of %uD", |
| element_size); |
| return NULL; |
| } |
| |
| if (njs_is_defined(njs_arg(args, nargs, 2))) { |
| ret = njs_value_to_index(vm, njs_argument(args, 2), &size); |
| if (njs_slow_path(ret != NJS_OK)) { |
| return NULL; |
| } |
| } |
| |
| if (njs_slow_path(njs_is_detached_buffer(buffer))) { |
| njs_type_error(vm, "detached buffer"); |
| return NULL; |
| } |
| |
| if (njs_is_defined(njs_arg(args, nargs, 2))) { |
| ret = njs_value_to_index(vm, njs_argument(args, 2), &size); |
| if (njs_slow_path(ret != NJS_OK)) { |
| return NULL; |
| } |
| |
| size *= element_size; |
| |
| if (njs_slow_path((offset + size) > buffer->size)) { |
| njs_range_error(vm, "Invalid typed array length: %uL", size); |
| return NULL; |
| } |
| |
| } else { |
| if (njs_slow_path((buffer->size % element_size) != 0)) { |
| njs_range_error(vm, "byteLength of buffer must be " |
| "multiple of %uD", element_size); |
| return NULL; |
| } |
| |
| if (offset > buffer->size) { |
| njs_range_error(vm, "byteOffset %uL is outside the bound of " |
| "the buffer", offset); |
| return NULL; |
| } |
| |
| size = buffer->size - offset; |
| } |
| |
| } else if (njs_is_typed_array(value)) { |
| src_tarray = njs_typed_array(value); |
| if (njs_slow_path(njs_is_detached_buffer(src_tarray->buffer))) { |
| njs_type_error(vm, "detached buffer"); |
| return NULL; |
| } |
| |
| size = (uint64_t) njs_typed_array_length(src_tarray) * element_size; |
| |
| } else if (njs_is_object(value)) { |
| if (njs_is_fast_array(value)) { |
| src_array = njs_array(value); |
| length = src_array->length; |
| |
| } else { |
| ret = njs_object_length(vm, value, &length); |
| if (njs_slow_path(ret == NJS_ERROR)) { |
| return NULL; |
| } |
| } |
| |
| size = length * element_size; |
| |
| } else { |
| ret = njs_value_to_index(vm, value, &size); |
| if (njs_slow_path(ret != NJS_OK)) { |
| return NULL; |
| } |
| |
| size *= element_size; |
| } |
| |
| if (buffer == NULL) { |
| buffer = njs_array_buffer_alloc(vm, size, zeroing); |
| if (njs_slow_path(buffer == NULL)) { |
| return NULL; |
| } |
| } |
| |
| array = njs_mp_zalloc(vm->mem_pool, sizeof(njs_typed_array_t)); |
| if (njs_slow_path(array == NULL)) { |
| goto memory_error; |
| } |
| |
| array->buffer = buffer; |
| array->offset = offset / element_size; |
| array->byte_length = size; |
| array->type = type; |
| |
| if (src_tarray != NULL) { |
| if (type != src_tarray->type) { |
| length = njs_typed_array_length(src_tarray); |
| for (i = 0; i < length; i++) { |
| njs_typed_array_prop_set(vm, array, i, |
| njs_typed_array_prop(src_tarray, i)); |
| } |
| |
| } else { |
| memcpy(&buffer->u.u8[0], &src_tarray->buffer->u.u8[0], size); |
| } |
| |
| } else if (src_array != NULL) { |
| for (i = 0; i < length; i++) { |
| ret = njs_value_to_number(vm, &src_array->start[i], &num); |
| if (njs_slow_path(ret == NJS_ERROR)) { |
| return NULL; |
| } |
| |
| if (ret == NJS_OK) { |
| njs_typed_array_prop_set(vm, array, i, num); |
| } |
| } |
| |
| } else if (!njs_is_array_buffer(value) && njs_is_object(value)) { |
| for (i = 0; i < length; i++) { |
| ret = njs_value_property_i64(vm, value, i, &prop); |
| if (njs_slow_path(ret == NJS_ERROR)) { |
| return NULL; |
| } |
| |
| num = NAN; |
| |
| if (ret == NJS_OK) { |
| ret = njs_value_to_number(vm, &prop, &num); |
| if (njs_slow_path(ret == NJS_ERROR)) { |
| return NULL; |
| } |
| } |
| |
| njs_typed_array_prop_set(vm, array, i, num); |
| } |
| } |
| |
| njs_lvlhsh_init(&array->object.hash); |
| njs_lvlhsh_init(&array->object.shared_hash); |
| array->object.__proto__ = &vm->prototypes[type].object; |
| array->object.type = NJS_TYPED_ARRAY; |
| array->object.extensible = 1; |
| array->object.fast_array = 1; |
| |
| return array; |
| |
| memory_error: |
| |
| njs_memory_error(vm); |
| |
| return NULL; |
| } |
| |
| |
| static njs_int_t |
| njs_typed_array_constructor(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, |
| njs_index_t magic) |
| { |
| njs_typed_array_t *array; |
| |
| if (!vm->top_frame->ctor) { |
| njs_type_error(vm, "Constructor of TypedArray requires 'new'"); |
| return NJS_ERROR; |
| } |
| |
| array = njs_typed_array_alloc(vm, &args[1], nargs - 1, 1, magic); |
| if (njs_slow_path(array == NULL)) { |
| return NJS_ERROR; |
| } |
| |
| njs_set_typed_array(&vm->retval, array); |
| |
| return NJS_OK; |
| } |
| |
| |
| static njs_int_t |
| njs_typed_array_create(njs_vm_t *vm, njs_value_t *constructor, |
| njs_value_t *args, njs_uint_t nargs, njs_value_t *retval) |
| { |
| njs_int_t ret; |
| njs_value_t this; |
| njs_object_t *object; |
| njs_typed_array_t *array; |
| |
| object = njs_function_new_object(vm, constructor); |
| if (njs_slow_path(object == NULL)) { |
| return NJS_ERROR; |
| } |
| |
| njs_set_object(&this, object); |
| |
| ret = njs_function_call2(vm, njs_function(constructor), &this, args, |
| nargs, retval, 1); |
| if (njs_slow_path(ret != NJS_OK)) { |
| return NJS_ERROR; |
| } |
| |
| if (njs_slow_path(!njs_is_typed_array(retval))) { |
| njs_type_error(vm, "Derived TypedArray constructor " |
| "returned not a typed array"); |
| return NJS_ERROR; |
| } |
| |
| array = njs_typed_array(retval); |
| if (njs_slow_path(njs_is_detached_buffer(array->buffer))) { |
| njs_type_error(vm, "detached buffer"); |
| return NJS_ERROR; |
| } |
| |
| if (njs_slow_path(nargs == 1 && njs_is_number(&args[0]) |
| && njs_typed_array_length(array) |
| < njs_number(&args[0]))) |
| { |
| njs_type_error(vm, "Derived TypedArray constructor " |
| "returned too short array"); |
| return NJS_ERROR; |
| } |
| |
| return NJS_OK; |
| } |
| |
| |
| static njs_int_t |
| njs_typed_array_species_create(njs_vm_t *vm, njs_value_t *exemplar, |
| njs_value_t *args, njs_uint_t nargs, njs_value_t *retval) |
| { |
| njs_int_t ret; |
| njs_value_t constructor; |
| njs_typed_array_t *array; |
| |
| array = njs_typed_array(exemplar); |
| |
| njs_set_function(&constructor, &vm->constructors[array->type]); |
| |
| ret = njs_value_species_constructor(vm, exemplar, &constructor, |
| &constructor); |
| if (njs_slow_path(ret != NJS_OK)) { |
| return ret; |
| } |
| |
| return njs_typed_array_create(vm, &constructor, args, nargs, retval); |
| } |
| |
| |
| static njs_int_t |
| njs_typed_array_of(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, |
| njs_index_t unused) |
| { |
| double num; |
| uint32_t length, i; |
| njs_int_t ret; |
| njs_value_t *this; |
| njs_value_t argument; |
| njs_typed_array_t *array; |
| |
| this = njs_argument(args, 0); |
| |
| if (njs_slow_path(!njs_is_constructor(this))) { |
| njs_type_error(vm, "%s is not a constructor", |
| njs_type_string(this->type)); |
| return NJS_ERROR; |
| } |
| |
| length = nargs - 1; |
| |
| njs_set_number(&argument, length); |
| ret = njs_typed_array_create(vm, this, &argument, 1, &vm->retval); |
| if (njs_slow_path(ret != NJS_OK)) { |
| return NJS_ERROR; |
| } |
| |
| array = njs_typed_array(&vm->retval); |
| |
| for (i = 0; i < length; i++) { |
| ret = njs_value_to_number(vm, njs_argument(args, i + 1), &num); |
| if (njs_slow_path(ret != NJS_OK)) { |
| return NJS_ERROR; |
| } |
| |
| njs_typed_array_prop_set(vm, array, i, num); |
| } |
| |
| njs_set_typed_array(&vm->retval, array); |
| |
| return NJS_OK; |
| } |
| |
| |
| static njs_int_t |
| njs_typed_array_from(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs, |
| njs_index_t unused) |
| { |
| double num; |
| int64_t length, i; |
| njs_int_t ret; |
| njs_value_t *this, *source, *mapfn; |
| njs_value_t arguments[3], retval; |
| njs_function_t *function; |
| njs_typed_array_t *array; |
| |
| this = njs_argument(args, 0); |
| |
| if (njs_slow_path(!njs_is_constructor(this))) { |
| njs_type_error(vm, "%s is not a constructor", |
| njs_type_string(this->type)); |
| return NJS_ERROR; |
| } |
| |
| mapfn = njs_arg(args, nargs, 2); |
| |
| if (njs_slow_path(!njs_is_function_or_undefined(mapfn))) { |
| njs_type_error(vm, "\"mapfn\" argument is not callable"); |
| return NJS_ERROR; |
| } |
| |
| function = NULL; |
| if (njs_is_function(mapfn)) { |
| function = njs_function(mapfn); |
| } |
| |
| source = njs_arg(args, nargs, 1); |
| |
| ret = njs_value_to_object(vm, source); |
| if (njs_slow_path(ret != NJS_OK)) { |
| return ret; |
| } |
| |
| ret = njs_object_length(vm, source, &length); |
| if (njs_slow_path(ret == NJS_ERROR)) { |
| return ret; |
| } |
| |
| njs_set_number(&arguments[0], length); |
| ret = njs_typed_array_create(vm, this, arguments, 1, &vm->retval); |
| if (njs_slow_path(ret != NJS_OK)) { |
| return NJS_ERROR; |
| } |
| |
| array = njs_typed_array(&vm->retval); |
| arguments[0] = *njs_arg(args, nargs, 3); |
| |
| for (i = 0; i < length; i++) { |
| ret = njs_value_property_i64(vm, source, i, &retval); |
| if (njs_slow_path(ret == NJS_ERROR)) { |
| return NJS_ERROR; |
| } |
| |
| if (function != NULL) { |
| arguments[1] = retval; |
| njs_set_number(&arguments[2], i); |
| ret = njs_function_apply(vm, function, arguments, 3, &retval); |
| if (njs_slow_path(ret != NJS_OK)) { |
| return NJS_ERROR; |
| } |
| } |
| |
| ret = njs_value_to_number(vm, &retval, &num); |
| if (njs_slow_path(ret != NJS_OK)) { |
| return NJS_ERROR; |
| } |
| |
| njs_typed_array_prop_set(vm, array, i, num); |
| } |
| |
| njs_set_typed_array(&vm->retval, array); |
| |
| return NJS_OK; |
| } |
| |
| |
| static njs_int_t |
| njs_typed_array_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; |
| } |
| |
| |
| njs_array_buffer_t * |
| njs_typed_array_writable(njs_vm_t *vm, njs_typed_array_t *array) |
| { |
| njs_int_t ret; |
| njs_array_buffer_t *buffer; |
| |
| buffer = array->buffer; |
| if (njs_slow_path(njs_is_detached_buffer(buffer))) { |
| njs_type_error(vm, "detached buffer"); |
| return NULL; |
| } |
| |
| ret = njs_array_buffer_writable(vm, buffer); |
| if (njs_slow_path(ret != NJS_OK)) { |
| return NULL; |
| } |
| |
| return buffer; |
| } |
| |
| |
| static const njs_value_t njs_typed_array_uint8_tag = njs_string("Uint8Array"); |
| static const njs_value_t njs_typed_array_uint8_clamped_tag = |
| njs_long_string("Uint8ClampedArray"); |
| static const njs_value_t njs_typed_array_int8_tag = njs_string("Int8Array"); |
| static const njs_value_t njs_typed_array_uint16_tag = |
| njs_string("Uint16Array"); |
| static const njs_value_t njs_typed_array_int16_tag = njs_string("Int16Array"); |
| static const njs_value_t njs_typed_array_uint32_tag = |
| njs_string("Uint32Array"); |
| static const njs_value_t njs_typed_array_int32_tag = njs_string("Int32Array"); |
| static const njs_value_t njs_typed_array_float32_tag = |
| njs_string("Float32Array"); |
| static const njs_value_t njs_typed_array_float64_tag = |
| njs_string("Float64Array"); |
| |
| static njs_int_t |
| njs_typed_array_get_string_tag(njs_vm_t *vm, njs_value_t *args, |
| njs_uint_t nargs, njs_index_t unused) |
| { |
| njs_value_t *this; |
| |
| static const njs_value_t *tags[NJS_OBJ_TYPE_TYPED_ARRAY_SIZE] = { |
| &njs_typed_array_uint8_tag, |
| &njs_typed_array_uint8_clamped_tag, |
| &njs_typed_array_int8_tag, |
| &njs_typed_array_uint16_tag, |
| &njs_typed_array_int16_tag, |
| &njs_typed_array_uint32_tag, |
| &njs_typed_array_int32_tag, |
| &njs_typed_array_float32_tag, |
| &njs_typed_array_float64_tag, |
| }; |
| |
| this = njs_argument(args, 0); |
| |
| if (!njs_is_typed_array(this)) { |
| njs_set_undefined(&vm->retval); |
| return NJS_OK; |
| } |
| |
| vm->retval = *tags[njs_typed_array_index(njs_typed_array(this)->type)]; |
| |
| return NJS_OK; |
| } |
| |
| |
| static njs_int_t |
| njs_typed_array_prototype_length(njs_vm_t *vm, njs_value_t *args, |
| njs_uint_t nargs, njs_index_t unused) |
| { |
| uint32_t length; |
| njs_value_t *this; |
| njs_typed_array_t *array; |
| |
| this = njs_argument(args, 0); |
| if (!njs_is_typed_array(this)) { |
| njs_type_error(vm, "Method TypedArray.prototype.length called " |
| "on incompatible receiver"); |
| return NJS_ERROR; |
| } |
| |
| array = njs_typed_array(this); |
| length = njs_typed_array_length(array); |
| |
| if (njs_slow_path(njs_is_detached_buffer(array->buffer))) { |
| length = 0; |
| } |
| |
| njs_set_number(&vm->retval, length); |
| |
| return NJS_OK; |
| } |
| |
| |
| static njs_int_t |
| njs_typed_array_prototype_buffer(njs_vm_t *vm, njs_value_t *args, |
| njs_uint_t nargs, njs_index_t unused) |
| { |
| njs_value_t *this; |
| njs_typed_array_t *array; |
| |
| this = njs_argument(args, 0); |
| 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; |
| } |
| |
| array = njs_typed_array(this); |
| |
| njs_set_array_buffer(&vm->retval, njs_typed_array_buffer(array)); |
| |
| return NJS_OK; |
| } |
| |
| |
| static njs_int_t |
| njs_typed_array_prototype_byte_length(njs_vm_t *vm, njs_value_t *args, |
| njs_uint_t nargs, njs_index_t unused) |
| { |
| size_t byte_length; |
| njs_value_t *this; |
| njs_typed_array_t *array; |
| |
| this = njs_argument(args, 0); |
| 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; |
| } |
| |
| array = njs_typed_array(this); |
| byte_length = array->byte_length; |
| |
| if (njs_slow_path(njs_is_detached_buffer(array->buffer))) { |
| if (njs_is_data_view(this)) { |
| njs_type_error(vm, "detached buffer"); |
| return NJS_ERROR; |
| } |
| |
| byte_length = 0; |
| } |
| |
| njs_set_number(&vm->retval, byte_length); |
| |
| return NJS_OK; |
| } |
| |
| |
| static njs_int_t |
| njs_typed_array_prototype_byte_offset(njs_vm_t *vm, njs_value_t *args, |
| njs_uint_t nargs, njs_index_t unused) |
| { |
| size_t byte_offset; |
| njs_value_t *this; |
| njs_typed_array_t *array; |
| |
| this = njs_argument(args, 0); |
| 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; |
| } |
| |
| array = njs_typed_array(this); |
| byte_offset = njs_typed_array_offset(array); |
| |
| if (njs_slow_path(njs_is_detached_buffer(array->buffer))) { |
| if (njs_is_data_view(this)) { |
| njs_type_error(vm, "detached buffer"); |
| return NJS_ERROR; |
| } |
| |
| byte_offset = 0; |
| } |
| |
| njs_set_number(&vm->retval, byte_offset); |
| |
| return NJS_OK; |
| } |
| |
| |
| static void |
| njs_typed_array_prop_set(njs_vm_t *vm, njs_typed_array_t *array, uint32_t index, |
| double v) |
| { |
| int8_t i8; |
| int16_t i16; |
| int32_t i32; |
| njs_array_buffer_t *buffer; |
| |
| buffer = array->buffer; |
| index += array->offset; |
| |
| njs_assert(!buffer->object.shared); |
| |
| switch (array->type) { |
| case NJS_OBJ_TYPE_UINT8_CLAMPED_ARRAY: |
| if (isnan(v) || v < 0) { |
| v = 0; |
| } else if (v > 255) { |
| v = 255; |
| } |
| |
| buffer->u.u8[index] = lrint(v); |
| |
| break; |
| |
| case NJS_OBJ_TYPE_UINT8_ARRAY: |
| case NJS_OBJ_TYPE_INT8_ARRAY: |
| i8 = njs_number_to_int32(v); |
| buffer->u.u8[index] = i8; |
| break; |
| |
| case NJS_OBJ_TYPE_UINT16_ARRAY: |
| case NJS_OBJ_TYPE_INT16_ARRAY: |
| i16 = njs_number_to_int32(v); |
| buffer->u.u16[index] = i16; |
| break; |
| |
| case NJS_OBJ_TYPE_UINT32_ARRAY: |
| case NJS_OBJ_TYPE_INT32_ARRAY: |
| i32 = njs_number_to_int32(v); |
| buffer->u.u32[index] = i32; |
| break; |
| |
| case NJS_OBJ_TYPE_FLOAT32_ARRAY: |
| buffer->u.f32[index] = v; |
| break; |
| |
| default: |
| |
| /* NJS_OBJ_TYPE_FLOAT64_ARRAY. */ |
| |
| buffer->u.f64[index] = v; |
| } |
| } |
| |
| |
| njs_int_t |
| njs_typed_array_set_value(njs_vm_t *vm, njs_typed_array_t *array, |
| uint32_t index, njs_value_t *setval) |
| { |
| double num; |
| njs_int_t ret; |
| njs_array_buffer_t *buffer; |
| |
| ret = njs_value_to_number(vm, setval, &num); |
| if (njs_slow_path(ret != NJS_OK)) { |
| return ret; |
| } |
| |
| buffer = njs_typed_array_writable(vm, array); |
| if (njs_slow_path(buffer == NULL)) { |
| return NJS_ERROR; |
| } |
| |
| njs_typed_array_prop_set(vm, array, index, num); |
| |
| njs_set_number(setval, num); |
| |
| return NJS_OK; |
| } |
| |
| |
| static njs_int_t |
| njs_typed_array_prototype_set(njs_vm_t *vm, njs_value_t *args, |
| njs_uint_t nargs, njs_index_t unused) |
| { |
| double num; |
| int64_t i, length, src_length, offset; |
| njs_int_t ret; |
| njs_value_t *this, *src, *value, prop; |
| njs_array_t *array; |
| njs_typed_array_t *self, *src_tarray; |
| njs_array_buffer_t *buffer; |
| |
| this = njs_argument(args, 0); |
| if (njs_slow_path(!njs_is_typed_array(this))) { |
| njs_type_error(vm, "this is not a typed array"); |
| return NJS_ERROR; |
| } |
| |
| self = njs_typed_array(this); |
| src = njs_arg(args, nargs, 1); |
| value = njs_arg(args, nargs, 2); |
| |
| ret = njs_value_to_integer(vm, value, &offset); |
| if (njs_slow_path(ret != NJS_OK)) { |
| return NJS_ERROR; |
| } |
| |
| if (njs_slow_path(offset < 0)) { |
| njs_range_error(vm, "offset is out of bounds"); |
| return NJS_ERROR; |
| } |
| |
| buffer = njs_typed_array_writable(vm, self); |
| if (njs_slow_path(buffer == NULL)) { |
| return NJS_ERROR; |
| } |
| |
| length = njs_typed_array_length(self); |
| |
| if (njs_is_typed_array(src)) { |
| src_tarray = njs_typed_array(src); |
| if (njs_slow_path(njs_is_detached_buffer(src_tarray->buffer))) { |
| njs_type_error(vm, "detached buffer"); |
| return NJS_ERROR; |
| } |
| |
| src_length = njs_typed_array_length(src_tarray); |
| |
| if (njs_slow_path((src_length > length) |
| || (offset > length - src_length))) |
| { |
| njs_range_error(vm, "source is too large"); |
| return NJS_ERROR; |
| } |
| |
| length = njs_min(njs_typed_array_length(src_tarray), length - offset); |
| |
| for (i = 0; i < length; i++) { |
| njs_typed_array_prop_set(vm, self, offset + i, |
| njs_typed_array_prop(src_tarray, i)); |
| } |
| |
| } else { |
| if (njs_is_fast_array(src)) { |
| array = njs_array(src); |
| src_length = array->length; |
| |
| if (njs_slow_path((src_length > length) |
| || (offset > length - src_length))) |
| { |
| njs_range_error(vm, "source is too large"); |
| return NJS_ERROR; |
| } |
| |
| length = njs_min(array->length, length - offset); |
| |
| for (i = 0; i < length; i++) { |
| ret = njs_value_to_number(vm, &array->start[i], &num); |
| if (ret == NJS_OK) { |
| njs_typed_array_prop_set(vm, self, offset + i, num); |
| } |
| } |
| |
| goto done; |
| } |
| |
| ret = njs_value_to_object(vm, src); |
| if (njs_slow_path(ret != NJS_OK)) { |
| return ret; |
| } |
| |
| ret = njs_object_length(vm, src, &src_length); |
| if (njs_slow_path(ret == NJS_ERROR)) { |
| return ret; |
| } |
| |
| if (njs_slow_path((src_length > length) |
| || (offset > length - src_length))) |
| { |
| njs_range_error(vm, "source is too large"); |
| return NJS_ERROR; |
| } |
| |
| length = njs_min(src_length, length - offset); |
| |
| for (i = 0; i < length; i++) { |
| ret = njs_value_property_i64(vm, src, i, &prop); |
| if (njs_slow_path(ret == NJS_ERROR)) { |
| return NJS_ERROR; |
| } |
| |
| num = NAN; |
| |
| if (ret == NJS_OK) { |
| ret = njs_value_to_number(vm, &prop, &num); |
| if (njs_slow_path(ret == NJS_ERROR)) { |
| return NJS_ERROR; |
| } |
| } |
| |
| if (njs_slow_path(njs_is_detached_buffer(buffer))) { |
| njs_type_error(vm, "detached buffer"); |
| return NJS_ERROR; |
| } |
| |
| njs_typed_array_prop_set(vm, self, offset + i, num); |
| } |
| } |
| |
| done: |
| |
| njs_set_undefined(&vm->retval); |
| |
| return NJS_OK; |
| } |
| |
| |
| static njs_int_t |
| njs_typed_array_prototype_fill(njs_vm_t *vm, njs_value_t *args, |
| njs_uint_t nargs, njs_index_t unused) |
| { |
| float f32; |
| int8_t i8; |
| double num; |
| int16_t i16; |
| int32_t i32; |
| uint8_t u8; |
| int64_t start, end, offset; |
| uint32_t i, length; |
| njs_int_t ret; |
| njs_value_t *this, *setval, lvalue; |
| njs_typed_array_t *array; |
| njs_array_buffer_t *buffer; |
| |
| this = njs_argument(args, 0); |
| if (njs_slow_path(!njs_is_typed_array(this))) { |
| njs_type_error(vm, "this is not a typed array"); |
| return NJS_ERROR; |
| } |
| |
| array = njs_typed_array(this); |
| if (njs_slow_path(njs_is_detached_buffer(array->buffer))) { |
| njs_type_error(vm, "detached buffer"); |
| return NJS_ERROR; |
| } |
| |
| length = njs_typed_array_length(array); |
| |
| setval = njs_lvalue_arg(&lvalue, args, nargs, 1); |
| ret = njs_value_to_number(vm, setval, &num); |
| if (njs_slow_path(ret != NJS_OK)) { |
| return NJS_ERROR; |
| } |
| |
| ret = njs_value_to_integer(vm, njs_arg(args, nargs, 2), &start); |
| if (njs_slow_path(ret != NJS_OK)) { |
| return ret; |
| } |
| |
| start = (start < 0) ? njs_max(length + start, 0) : njs_min(start, length); |
| |
| if (njs_is_undefined(njs_arg(args, nargs, 3))) { |
| end = length; |
| |
| } else { |
| ret = njs_value_to_integer(vm, njs_arg(args, nargs, 3), &end); |
| if (njs_slow_path(ret != NJS_OK)) { |
| return ret; |
| } |
| } |
| |
| end = (end < 0) ? njs_max(length + end, 0) : njs_min(end, length); |
| |
| buffer = njs_typed_array_writable(vm, array); |
| if (njs_slow_path(buffer == NULL)) { |
| return NJS_ERROR; |
| } |
| |
| njs_set_typed_array(&vm->retval, array); |
| |
| offset = array->offset; |
| |
| switch (array->type) { |
| case NJS_OBJ_TYPE_UINT8_CLAMPED_ARRAY: |
| if (isnan(num) || num < 0) { |
| u8 = 0; |
| |
| } else if (num > 255) { |
| u8 = 255; |
| |
| } else { |
| u8 = lrint(num); |
| } |
| |
| if (start < end) { |
| memset(&buffer->u.u8[start + offset], u8, end - start); |
| } |
| |
| break; |
| |
| case NJS_OBJ_TYPE_UINT8_ARRAY: |
| case NJS_OBJ_TYPE_INT8_ARRAY: |
| i8 = njs_number_to_int32(num); |
| |
| if (start < end) { |
| memset(&buffer->u.u8[start + offset], i8, end - start); |
| } |
| |
| break; |
| |
| case NJS_OBJ_TYPE_UINT16_ARRAY: |
| case NJS_OBJ_TYPE_INT16_ARRAY: |
| i16 = njs_number_to_int32(num); |
| |
| for (i = start; i < end; i++) { |
| buffer->u.u16[i + offset] = i16; |
| } |
| |
| break; |
| |
| case NJS_OBJ_TYPE_UINT32_ARRAY: |
| case NJS_OBJ_TYPE_INT32_ARRAY: |
| i32 = njs_number_to_int32(num); |
| |
| for (i = start; i < end; i++) { |
| buffer->u.u32[i + offset] = i32; |
| } |
| |
| break; |
| |
| case NJS_OBJ_TYPE_FLOAT32_ARRAY: |
| f32 = num; |
| |
| for (i = start; i < end; i++) { |
| buffer->u.f32[i + offset] = f32; |
| } |
| |
| break; |
| |
| default: |
| |
| /* NJS_OBJ_TYPE_FLOAT64_ARRAY. */ |
| |
| for (i = start; i < end; i++) { |
| buffer->u.f64[i + offset] = num; |
| } |
| } |
| |
| return NJS_OK; |
| } |
| |
| |
| njs_int_t |
| njs_typed_array_prototype_slice(njs_vm_t *vm, njs_value_t *args, |
| njs_uint_t nargs, njs_index_t copy) |
| { |
| int64_t start, end, count, offset; |
| uint32_t i, element_size, length; |
| njs_int_t ret; |
| njs_value_t arguments[3], *this, *value; |
| njs_typed_array_t *array, *new_array; |
| njs_array_buffer_t *buffer, *new_buffer; |
| |
| this = njs_argument(args, 0); |
| if (njs_slow_path(!njs_is_typed_array(this))) { |
| njs_type_error(vm, "this is not a typed array"); |
| return NJS_ERROR; |
| } |
| |
| array = njs_typed_array(this); |
| length = njs_typed_array_length(array); |
| buffer = njs_typed_array_buffer(array); |
| if (njs_slow_path(copy && njs_is_detached_buffer(buffer))) { |
| njs_type_error(vm, "detached buffer"); |
| return NJS_ERROR; |
| } |
| |
| ret = njs_value_to_integer(vm, njs_arg(args, nargs, 1), &start); |
| if (njs_slow_path(ret != NJS_OK)) { |
| njs_range_error(vm, "invalid start"); |
| return NJS_ERROR; |
| } |
| |
| start = (start < 0) ? njs_max(length + start, 0) : njs_min(start, length); |
| |
| value = njs_arg(args, nargs, 2); |
| |
| if (njs_is_undefined(value)) { |
| end = length; |
| |
| } else { |
| ret = njs_value_to_integer(vm, value, &end); |
| if (njs_slow_path(ret != NJS_OK)) { |
| njs_range_error(vm, "invalid end"); |
| return NJS_ERROR; |
| } |
| } |
| |
| end = (end < 0) ? njs_max(length + end, 0) : njs_min(end, length); |
| |
| element_size = njs_typed_array_element_size(array->type); |
| |
| if (copy) { |
| count = njs_max(end - start, 0); |
| njs_set_number(&arguments[0], count); |
| |
| ret = njs_typed_array_species_create(vm, this, |
| njs_value_arg(arguments), 1, |
| &vm->retval); |
| if (njs_slow_path(ret != NJS_OK)) { |
| return ret; |
| } |
| |
| if (count == 0) { |
| return NJS_OK; |
| } |
| |
| if (njs_slow_path(njs_is_detached_buffer(buffer))) { |
| njs_type_error(vm, "detached buffer"); |
| return NJS_ERROR; |
| } |
| |
| new_array = njs_typed_array(&vm->retval); |
| new_buffer = njs_typed_array_buffer(new_array); |
| element_size = njs_typed_array_element_size(array->type); |
| |
| if (njs_fast_path(array->type == new_array->type)) { |
| start = start * element_size; |
| count = count * element_size; |
| |
| memcpy(&new_buffer->u.u8[0], &buffer->u.u8[start], count); |
| |
| } else { |
| for (i = 0; i < count; i++) { |
| njs_typed_array_prop_set(vm, new_array, i, |
| njs_typed_array_prop(array, i + start)); |
| } |
| } |
| |
| return NJS_OK; |
| } |
| |
| offset = array->offset * element_size; |
| offset += start * element_size; |
| |
| njs_set_array_buffer(&arguments[0], buffer); |
| njs_set_number(&arguments[1], offset); |
| njs_set_number(&arguments[2], njs_max(end - start, 0)); |
| |
| return njs_typed_array_species_create(vm, this, njs_value_arg(arguments), 3, |
| &vm->retval); |
| } |
| |
| |
| static njs_int_t |
| njs_typed_array_prototype_copy_within(njs_vm_t *vm, njs_value_t *args, |
| njs_uint_t nargs, njs_index_t unused) |
| { |
| int64_t length, to, from, final, count; |
| uint32_t element_size; |
| njs_int_t ret; |
| njs_value_t *this, *value; |
| njs_typed_array_t *array; |
| njs_array_buffer_t *buffer; |
| |
| this = njs_argument(args, 0); |
| if (njs_slow_path(!njs_is_typed_array(this))) { |
| njs_type_error(vm, "this is not a typed array"); |
| return NJS_ERROR; |
| } |
| |
| array = njs_typed_array(this); |
| if (njs_slow_path(njs_is_detached_buffer(array->buffer))) { |
| njs_type_error(vm, "detached buffer"); |
| return NJS_ERROR; |
| } |
| |
| length = njs_typed_array_length(array); |
| |
| value = njs_arg(args, nargs, 1); |
| |
| ret = njs_value_to_integer(vm, value, &to); |
| if (njs_slow_path(ret != NJS_OK)) { |
| return NJS_ERROR; |
| } |
| |
| to = to < 0 ? njs_max(to + length, 0) : njs_min(to, length); |
| |
| value = njs_arg(args, nargs, 2); |
| |
| ret = njs_value_to_integer(vm, value, &from); |
| if (njs_slow_path(ret != NJS_OK)) { |
| return ret; |
| } |
| |
| from = from < 0 ? njs_max(from + length, 0) : njs_min(from, length); |
| |
| value = njs_arg(args, nargs, 3); |
| |
| final = length; |
| |
| if (njs_is_defined(value)) { |
| ret = njs_value_to_integer(vm, value, &final); |
| if (njs_slow_path(ret != NJS_OK)) { |
| return NJS_ERROR; |
| } |
| } |
| |
| final = (final < 0) ? njs_max(final + length, 0) : njs_min(final, length); |
| |
| njs_set_typed_array(&vm->retval, array); |
| |
| count = njs_min(final - from, length - to); |
| |
| if (count <= 0) { |
| return NJS_OK; |
| } |
| |
| buffer = njs_typed_array_writable(vm, array); |
| if (njs_slow_path(buffer == NULL)) { |
| return NJS_ERROR; |
| } |
| |
| element_size = njs_typed_array_element_size(array->type); |
| |
| to = (to + array->offset) * element_size; |
| from = (from + array->offset) * element_size; |
| count = count * element_size; |
| |
| memmove(&buffer->u.u8[to], &buffer->u.u8[from], count); |
| |
| return NJS_OK; |
| } |
| |
| |
| static njs_int_t |
| njs_typed_array_prototype_iterator(njs_vm_t *vm, njs_value_t *args, |
| njs_uint_t nargs, njs_index_t type) |
| { |
| double val; |
| int64_t i, length; |
| njs_int_t ret; |
| njs_arr_t results; |
| njs_value_t *this, *this_arg, *r; |
| njs_value_t arguments[4], retval; |
| njs_function_t *function; |
| njs_typed_array_t *array, *dst; |
| njs_array_buffer_t *buffer; |
| |
| this = njs_argument(args, 0); |
| if (njs_slow_path(!njs_is_typed_array(this))) { |
| njs_type_error(vm, "this is not a typed array"); |
| return NJS_ERROR; |
| } |
| |
| dst = NULL; |
| array = njs_typed_array(this); |
| length = njs_typed_array_length(array); |
| |
| if (njs_slow_path(!njs_is_function(njs_arg(args, nargs, 1)))) { |
| njs_type_error(vm, "callback argument is not callable"); |
| return NJS_ERROR; |
| } |
| |
| function = njs_function(njs_argument(args, 1)); |
| this_arg = njs_arg(args, nargs, 2); |
| |
| buffer = array->buffer; |
| results.separate = 0; |
| results.pointer = 0; |
| |
| switch (type) { |
| case NJS_ARRAY_MAP: |
| njs_set_number(&arguments[0], length); |
| ret = njs_typed_array_species_create(vm, this, njs_value_arg(arguments), |
| 1, &retval); |
| if (njs_slow_path(ret != NJS_OK)) { |
| return NJS_ERROR; |
| } |
| |
| dst = njs_typed_array(&retval); |
| break; |
| |
| case NJS_ARRAY_FILTER: |
| default: |
| r = njs_arr_init(vm->mem_pool, &results, NULL, 4, sizeof(njs_value_t)); |
| if (njs_slow_path(r == NULL)) { |
| return NJS_ERROR; |
| } |
| } |
| |
| for (i = 0; i < length; i++) { |
| if (njs_slow_path(njs_is_detached_buffer(buffer))) { |
| njs_type_error(vm, "detached buffer"); |
| return NJS_ERROR; |
| } |
| |
| val = njs_typed_array_prop(array, i); |
| |
| arguments[0] = *this_arg; |
| njs_set_number(&arguments[1], val); |
| njs_set_number(&arguments[2], i); |
| njs_set_typed_array(&arguments[3], array); |
| |
| ret = njs_function_apply(vm, function, arguments, 4, &vm->retval); |
| if (njs_slow_path(ret != NJS_OK)) { |
| goto exception; |
| } |
| |
| switch (type) { |
| case NJS_ARRAY_EVERY: |
| if (!njs_is_true(&vm->retval)) { |
| vm->retval = njs_value_false; |
| goto done; |
| } |
| |
| break; |
| |
| case NJS_ARRAY_FOR_EACH: |
| break; |
| |
| case NJS_ARRAY_SOME: |
| case NJS_ARRAY_FIND: |
| case NJS_ARRAY_FIND_INDEX: |
| if (!njs_is_true(&vm->retval)) { |
| continue; |
| } |
| |
| switch (type) { |
| case NJS_ARRAY_SOME: |
| vm->retval = njs_value_true; |
| break; |
| |
| case NJS_ARRAY_FIND: |
| njs_set_number(&vm->retval, val); |
| break; |
| |
| default: |
| njs_set_number(&vm->retval, i); |
| break; |
| } |
| |
| goto done; |
| |
| case NJS_ARRAY_MAP: |
| ret = njs_typed_array_set_value(vm, dst, i, &vm->retval); |
| if (njs_slow_path(ret != NJS_OK)) { |
| goto exception; |
| } |
| |
| break; |
| |
| default: |
| /* NJS_ARRAY_FILTER. */ |
| |
| if (!njs_is_true(&vm->retval)) { |
| continue; |
| } |
| |
| r = njs_arr_add(&results); |
| if (njs_slow_path(r == NULL)) { |
| goto exception; |
| } |
| |
| njs_set_number(r, val); |
| } |
| } |
| |
| /* Default values. */ |
| |
| switch (type) { |
| case NJS_ARRAY_EVERY: |
| vm->retval = njs_value_true; |
| break; |
| |
| case NJS_ARRAY_SOME: |
| vm->retval = njs_value_false; |
| break; |
| |
| case NJS_ARRAY_FOR_EACH: |
| case NJS_ARRAY_FIND: |
| njs_set_undefined(&vm->retval); |
| break; |
| |
| case NJS_ARRAY_FIND_INDEX: |
| njs_set_number(&vm->retval, -1); |
| break; |
| |
| case NJS_ARRAY_MAP: |
| case NJS_ARRAY_FILTER: |
| default: |
| if (type == NJS_ARRAY_FILTER) { |
| njs_set_number(&arguments[0], results.items); |
| ret = njs_typed_array_species_create(vm, this, |
| njs_value_arg(arguments), |
| 1, &retval); |
| if (njs_slow_path(ret != NJS_OK)) { |
| goto exception; |
| } |
| |
| dst = njs_typed_array(&retval); |
| |
| i = 0; |
| |
| while (i < results.items) { |
| r = njs_arr_item(&results, i); |
| ret = njs_typed_array_set_value(vm, dst, i++, r); |
| if (njs_slow_path(ret != NJS_OK)) { |
| goto exception; |
| } |
| } |
| } |
| |
| njs_set_typed_array(&vm->retval, dst); |
| break; |
| } |
| |
| done: |
| |
| ret = NJS_OK; |
| |
| exception: |
| |
| njs_arr_destroy(&results); |
| |
| return ret; |
| } |
| |
| |
| static njs_int_t |
| njs_typed_array_prototype_index_of(njs_vm_t *vm, njs_value_t *args, |
| njs_uint_t nargs, njs_index_t type) |
| { |
| double v; |
| int64_t i, i64, from, to, index, increment, offset, length; |
| njs_int_t ret, integer; |
| njs_value_t *this; |
| const float *f32; |
| const double *f64; |
| const uint8_t *u8; |
| const uint16_t *u16; |
| const uint32_t *u32; |
| njs_typed_array_t *array; |
| njs_array_buffer_t *buffer; |
| |
| this = njs_argument(args, 0); |
| if (njs_slow_path(!njs_is_typed_array(this))) { |
| njs_type_error(vm, "this is not a typed array"); |
| return NJS_ERROR; |
| } |
| |
| index = -1; |
| array = njs_typed_array(this); |
| length = njs_typed_array_length(array); |
| |
| if (!njs_is_number(njs_arg(args, nargs, 1)) || length == 0) { |
| goto done; |
| } |
| |
| if (type & 2) { |
| /* lastIndexOf(). */ |
| |
| if (nargs > 2) { |
| ret = njs_value_to_integer(vm, njs_arg(args, nargs, 2), &from); |
| if (njs_slow_path(ret != NJS_OK)) { |
| return ret; |
| } |
| |
| } else { |
| from = length - 1; |
| } |
| |
| if (from >= 0) { |
| from = njs_min(from, length - 1); |
| |
| } else if (from < 0) { |
| from += length; |
| } |
| |
| to = -1; |
| increment = -1; |
| |
| if (from <= to) { |
| goto done; |
| } |
| |
| } else { |
| /* indexOf(), includes(). */ |
| |
| ret = njs_value_to_integer(vm, njs_arg(args, nargs, 2), &from); |
| if (njs_slow_path(ret != NJS_OK)) { |
| return ret; |
| } |
| |
| if (from < 0) { |
| from += length; |
| |
| if (from < 0) { |
| from = 0; |
| } |
| } |
| |
| to = length; |
| increment = 1; |
| |
| if (from >= to) { |
| goto done; |
| } |
| } |
| |
| if (njs_slow_path(njs_is_detached_buffer(array->buffer))) { |
| njs_type_error(vm, "detached buffer"); |
| return NJS_ERROR; |
| } |
| |
| v = njs_number(njs_argument(args, 1)); |
| |
| i64 = v; |
| integer = (v == i64); |
| |
| buffer = array->buffer; |
| offset = array->offset; |
| |
| switch (array->type) { |
| case NJS_OBJ_TYPE_INT8_ARRAY: |
| if (integer && ((int8_t) i64 == i64)) { |
| goto search8; |
| } |
| |
| break; |
| |
| case NJS_OBJ_TYPE_UINT8_CLAMPED_ARRAY: |
| case NJS_OBJ_TYPE_UINT8_ARRAY: |
| if (integer && ((uint8_t) i64 == i64)) { |
| search8: |
| u8 = &buffer->u.u8[0]; |
| for (i = from; i != to; i += increment) { |
| if (u8[offset + i] == (uint8_t) i64) { |
| index = i; |
| break; |
| } |
| } |
| } |
| |
| break; |
| |
| case NJS_OBJ_TYPE_INT16_ARRAY: |
| if (integer && ((int16_t) i64 == i64)) { |
| goto search16; |
| } |
| |
| break; |
| |
| case NJS_OBJ_TYPE_UINT16_ARRAY: |
| if (integer && ((uint16_t) i64 == i64)) { |
| search16: |
| u16 = &buffer->u.u16[0]; |
| for (i = from; i != to; i += increment) { |
| if (u16[offset + i] == (uint16_t) i64) { |
| index = i; |
| break; |
| } |
| } |
| } |
| |
| break; |
| |
| case NJS_OBJ_TYPE_INT32_ARRAY: |
| if (integer && ((int32_t) i64 == i64)) { |
| goto search32; |
| } |
| |
| break; |
| |
| case NJS_OBJ_TYPE_UINT32_ARRAY: |
| if (integer && ((uint32_t) i64 == i64)) { |
| search32: |
| u32 = &buffer->u.u32[0]; |
| for (i = from; i != to; i += increment) { |
| if (u32[offset + i] == (uint32_t) i64) { |
| index = i; |
| break; |
| } |
| } |
| } |
| |
| break; |
| |
| case NJS_OBJ_TYPE_FLOAT32_ARRAY: |
| f32 = &buffer->u.f32[0]; |
| |
| if (((float) v == v)) { |
| for (i = from; i != to; i += increment) { |
| if (f32[offset + i] == (float) v) { |
| index = i; |
| break; |
| } |
| } |
| |
| } else if ((type & 1) && isnan(v)) { |
| /* includes() handles NaN. */ |
| |
| for (i = from; i != to; i += increment) { |
| if (isnan(f32[offset + i])) { |
| index = i; |
| break; |
| } |
| } |
| } |
| |
| break; |
| |
| default: |
| |
| /* NJS_OBJ_TYPE_FLOAT64_ARRAY. */ |
| |
| f64 = &buffer->u.f64[0]; |
| |
| if ((type & 1) && isnan(v)) { |
| /* includes() handles NaN. */ |
| |
| for (i = from; i != to; i += increment) { |
| if (isnan(f64[offset + i])) { |
| index = i; |
| break; |
| } |
| } |
| |
| } else { |
| for (i = from; i != to; i += increment) { |
| if (f64[offset + i] == v) { |
| index = i; |
| break; |
| } |
| } |
| } |
| } |
| |
| done: |
| |
| /* Default values. */ |
| |
| if (type & 1) { |
| njs_set_boolean(&vm->retval, index != -1); |
| |
| } else { |
| njs_set_number(&vm->retval, index); |
| } |
| |
| return NJS_OK; |
| } |
| |
| |
| static njs_int_t |
| njs_typed_array_prototype_reduce(njs_vm_t *vm, njs_value_t *args, |
| njs_uint_t nargs, njs_index_t right) |
| { |
| int64_t i, from, to, increment, length; |
| njs_int_t ret; |
| njs_value_t *this, accumulator; |
| njs_value_t arguments[5]; |
| njs_function_t *function; |
| njs_typed_array_t *array; |
| njs_array_buffer_t *buffer; |
| |
| this = njs_argument(args, 0); |
| if (njs_slow_path(!njs_is_typed_array(this))) { |
| njs_type_error(vm, "this is not a typed array"); |
| return NJS_ERROR; |
| } |
| |
| array = njs_typed_array(this); |
| length = njs_typed_array_length(array); |
| |
| if (njs_slow_path(!njs_is_function(njs_arg(args, nargs, 1)))) { |
| njs_type_error(vm, "callback argument is not callable"); |
| return NJS_ERROR; |
| } |
| |
| function = njs_function(njs_argument(args, 1)); |
| |
| if (length == 0 && nargs <= 2) { |
| njs_type_error(vm, "Reduce of empty object with no initial value"); |
| return NJS_ERROR; |
| } |
| |
| if (right) { |
| from = length - 1; |
| to = -1; |
| increment = -1; |
| |
| } else { |
| from = 0; |
| to = length; |
| increment = 1; |
| } |
| |
| buffer = array->buffer; |
| |
| if (nargs > 2) { |
| accumulator = *njs_argument(args, 2); |
| |
| } else { |
| if (njs_slow_path(njs_is_detached_buffer(buffer))) { |
| njs_type_error(vm, "detached buffer"); |
| return NJS_ERROR; |
| } |
| |
| njs_set_number(&accumulator, njs_typed_array_prop(array, from)); |
| from += increment; |
| } |
| |
| for (i = from; i != to; i += increment) { |
| if (njs_slow_path(njs_is_detached_buffer(buffer))) { |
| njs_type_error(vm, "detached buffer"); |
| return NJS_ERROR; |
| } |
| |
| njs_set_undefined(&arguments[0]); |
| arguments[1] = accumulator; |
| njs_set_number(&arguments[2], njs_typed_array_prop(array, i)); |
| njs_set_number(&arguments[3], i); |
| njs_set_typed_array(&arguments[4], array); |
| |
| ret = njs_function_apply(vm, function, arguments, 5, &accumulator); |
| if (njs_slow_path(ret != NJS_OK)) { |
| return ret; |
| } |
| } |
| |
| vm->retval = accumulator; |
| |
| return NJS_OK; |
| } |
| |
| |
| static njs_int_t |
| njs_typed_array_prototype_reverse(njs_vm_t *vm, njs_value_t *args, |
| njs_uint_t nargs, njs_index_t unused) |
| { |
| double *f64; |
| uint8_t *u8; |
| int64_t i, length; |
| uint16_t *u16; |
| uint32_t *u32; |
| njs_value_t *this; |
| njs_typed_array_t *array; |
| njs_array_buffer_t *buffer; |
| |
| this = njs_argument(args, 0); |
| if (njs_slow_path(!njs_is_typed_array(this))) { |
| njs_type_error(vm, "this is not a typed array"); |
| return NJS_ERROR; |
| } |
| |
| array = njs_typed_array(this); |
| length = njs_typed_array_length(array); |
| |
| buffer = njs_typed_array_writable(vm, array); |
| if (njs_slow_path(buffer == NULL)) { |
| return NJS_ERROR; |
| } |
| |
| switch (array->type) { |
| case NJS_OBJ_TYPE_UINT8_ARRAY: |
| case NJS_OBJ_TYPE_UINT8_CLAMPED_ARRAY: |
| case NJS_OBJ_TYPE_INT8_ARRAY: |
| u8 = &buffer->u.u8[array->offset]; |
| |
| for (i = 0; i < length / 2; i++) { |
| njs_swap_u8(&u8[i], &u8[length - i - 1], 0); |
| } |
| |
| break; |
| |
| case NJS_OBJ_TYPE_INT16_ARRAY: |
| case NJS_OBJ_TYPE_UINT16_ARRAY: |
| u16 = &buffer->u.u16[array->offset]; |
| |
| for (i = 0; i < length / 2; i++) { |
| njs_swap_u16(&u16[i], &u16[length - i - 1], 0); |
| } |
| |
| break; |
| |
| case NJS_OBJ_TYPE_INT32_ARRAY: |
| case NJS_OBJ_TYPE_UINT32_ARRAY: |
| case NJS_OBJ_TYPE_FLOAT32_ARRAY: |
| u32 = &buffer->u.u32[array->offset]; |
| |
| for (i = 0; i < length / 2; i++) { |
| njs_swap_u32(&u32[i], &u32[length - i - 1], 0); |
| } |
| |
| break; |
| |
| default: |
| |
| /* NJS_OBJ_TYPE_FLOAT64_ARRAY. */ |
| |
| f64 = &buffer->u.f64[array->offset]; |
| |
| for (i = 0; i < length / 2; i++) { |
| njs_swap_u64(&f64[i], &f64[length - i - 1], 0); |
| } |
| } |
| |
| njs_set_typed_array(&vm->retval, array); |
| |
| return NJS_OK; |
| } |
| |
| |
| static int |
| njs_typed_array_compare_i8(const void *a, const void *b, void *c) |
| { |
| return *((const int8_t *) a) - *((const int8_t *) b); |
| } |
| |
| |
| static int |
| njs_typed_array_compare_u8(const void *a, const void *b, void *c) |
| { |
| return *((const uint8_t *) a) - *((const uint8_t *) b); |
| } |
| |
| |
| static int |
| njs_typed_array_compare_i16(const void *a, const void *b, void *c) |
| { |
| return *((const int16_t *) a) - *((const int16_t *) b); |
| } |
| |
| |
| static int |
| njs_typed_array_compare_u16(const void *a, const void *b, void *c) |
| { |
| return *((const uint16_t *) a) - *((const uint16_t *) b); |
| } |
| |
| |
| static int |
| njs_typed_array_compare_i32(const void *a, const void *b, void *c) |
| { |
| int32_t ai, bi; |
| |
| ai = *(const int32_t *) a; |
| bi = *(const int32_t *) b; |
| |
| return (ai > bi) - (ai < bi); |
| } |
| |
| |
| static int |
| njs_typed_array_compare_u32(const void *a, const void *b, void *c) |
| { |
| uint32_t au, bu; |
| |
| au = *(const uint32_t *) a; |
| bu = *(const uint32_t *) b; |
| |
| return (au > bu) - (au < bu); |
| } |
| |
| |
| njs_inline int |
| njs_typed_array_compare(double a, double b) |
| { |
| if (njs_slow_path(isnan(a))) { |
| if (isnan(b)) { |
| return 0; |
| } |
| |
| return 1; |
| } |
| |
| if (njs_slow_path(isnan(b))) { |
| return -1; |
| } |
| |
| if (a < b) { |
| return -1; |
| } |
| |
| if (a > b) { |
| return 1; |
| } |
| |
| return signbit(b) - signbit(a); |
| } |
| |
| |
| static int |
| njs_typed_array_compare_f32(const void *a, const void *b, void *c) |
| { |
| return njs_typed_array_compare(*(const float *) a, *(const float *) b); |
| } |
| |
| |
| static int |
| njs_typed_array_compare_f64(const void *a, const void *b, void *c) |
| { |
| return njs_typed_array_compare(*(const double *) a, *(const double *) b); |
| } |
| |
| |
| static double |
| njs_typed_array_get_u8(const void *a) |
| { |
| return *(const uint8_t *) a; |
| } |
| |
| |
| static double |
| njs_typed_array_get_i8(const void *a) |
| { |
| return *(const int8_t *) a; |
| } |
| |
| |
| static double |
| njs_typed_array_get_u16(const void *a) |
| { |
| return *(const uint16_t *) a; |
| } |
| |
| |
| static double |
| njs_typed_array_get_i16(const void *a) |
| { |
| return *(const int16_t *) a; |
| } |
| |
| |
| static double |
| njs_typed_array_get_u32(const void *a) |
| { |
| return *(const uint32_t *) a; |
| } |
| |
| |
| static double |
| njs_typed_array_get_i32(const void *a) |
| { |
| return *(const int32_t *) a; |
| } |
| |
| |
| static double |
| njs_typed_array_get_f32(const void *a) |
| { |
| return *(const float *) a; |
| } |
| |
| |
| static double |
| njs_typed_array_get_f64(const void *a) |
| { |
| return *(const double *) a; |
| } |
| |
| |
| typedef struct { |
| njs_vm_t *vm; |
| njs_array_buffer_t *buffer; |
| njs_function_t *function; |
| njs_bool_t exception; |
| double (*get)(const void *v); |
| } njs_typed_array_sort_ctx_t; |
| |
| typedef int (*njs_typed_array_cmp_t)(const void *, const void *, void *ctx); |
| |
| |
| static int |
| njs_typed_array_generic_compare(const void *a, const void *b, void *c) |
| { |
| double num; |
| njs_int_t ret; |
| njs_value_t arguments[3], retval; |
| njs_typed_array_sort_ctx_t *ctx; |
| |
| ctx = c; |
| |
| if (njs_slow_path(ctx->exception)) { |
| return 0; |
| } |
| |
| njs_set_undefined(&arguments[0]); |
| njs_set_number(&arguments[1], ctx->get(a)); |
| njs_set_number(&arguments[2], ctx->get(b)); |
| |
| ret = njs_function_apply(ctx->vm, ctx->function, arguments, 3, &retval); |
| if (njs_slow_path(ret != NJS_OK)) { |
| goto exception; |
| } |
| |
| ret = njs_value_to_number(ctx->vm, &retval, &num); |
| if (njs_slow_path(ret != NJS_OK)) { |
| goto exception; |
| } |
| |
| if (njs_slow_path(njs_is_detached_buffer(ctx->buffer))) { |
| njs_type_error(ctx->vm, "detached buffer"); |
| goto exception; |
| } |
| |
| if (njs_slow_path(isnan(num))) { |
| return 0; |
| } |
| |
| if (num != 0) { |
| return (num > 0) - (num < 0); |
| } |
| |
| return 0; |
| |
| exception: |
| |
| ctx->exception = 1; |
| |
| return 0; |
| } |
| |
| |
| static njs_int_t |
| njs_typed_array_prototype_sort(njs_vm_t *vm, njs_value_t *args, |
| njs_uint_t nargs, njs_index_t unused) |
| { |
| u_char *base, *orig; |
| int64_t length; |
| uint32_t element_size; |
| njs_value_t *this, *comparefn; |
| njs_typed_array_t *array; |
| njs_array_buffer_t *buffer; |
| njs_typed_array_cmp_t cmp; |
| njs_typed_array_sort_ctx_t ctx; |
| |
| this = njs_argument(args, 0); |
| if (njs_slow_path(!njs_is_typed_array(this))) { |
| njs_type_error(vm, "this is not a typed array"); |
| return NJS_ERROR; |
| } |
| |
| array = njs_typed_array(this); |
| if (njs_slow_path(njs_is_detached_buffer(array->buffer))) { |
| njs_type_error(vm, "detached buffer"); |
| return NJS_ERROR; |
| } |
| |
| ctx.vm = vm; |
| ctx.buffer = array->buffer; |
| ctx.exception = 0; |
| |
| comparefn = njs_arg(args, nargs, 1); |
| |
| if (njs_is_defined(comparefn)) { |
| if (njs_slow_path(!njs_is_function(comparefn))) { |
| njs_type_error(vm, "comparefn must be callable or undefined"); |
| return NJS_ERROR; |
| } |
| |
| ctx.function = njs_function(comparefn); |
| |
| } else { |
| ctx.function = NULL; |
| } |
| |
| switch (array->type) { |
| case NJS_OBJ_TYPE_UINT8_ARRAY: |
| case NJS_OBJ_TYPE_UINT8_CLAMPED_ARRAY: |
| cmp = njs_typed_array_compare_u8; |
| ctx.get = njs_typed_array_get_u8; |
| break; |
| |
| case NJS_OBJ_TYPE_INT8_ARRAY: |
| cmp = njs_typed_array_compare_i8; |
| ctx.get = njs_typed_array_get_i8; |
| break; |
| |
| case NJS_OBJ_TYPE_UINT16_ARRAY: |
| cmp = njs_typed_array_compare_u16; |
| ctx.get = njs_typed_array_get_u16; |
| break; |
| |
| case NJS_OBJ_TYPE_INT16_ARRAY: |
| cmp = njs_typed_array_compare_i16; |
| ctx.get = njs_typed_array_get_i16; |
| break; |
| |
| case NJS_OBJ_TYPE_UINT32_ARRAY: |
| cmp = njs_typed_array_compare_u32; |
| ctx.get = njs_typed_array_get_u32; |
| break; |
| |
| case NJS_OBJ_TYPE_INT32_ARRAY: |
| cmp = njs_typed_array_compare_i32; |
| ctx.get = njs_typed_array_get_i32; |
| break; |
| |
| case NJS_OBJ_TYPE_FLOAT32_ARRAY: |
| cmp = njs_typed_array_compare_f32; |
| ctx.get = njs_typed_array_get_f32; |
| break; |
| |
| default: |
| |
| /* NJS_OBJ_TYPE_FLOAT64_ARRAY. */ |
| |
| cmp = njs_typed_array_compare_f64; |
| ctx.get = njs_typed_array_get_f64; |
| break; |
| } |
| |
| buffer = njs_typed_array_writable(vm, array); |
| if (njs_slow_path(buffer == NULL)) { |
| return NJS_ERROR; |
| } |
| |
| length = njs_typed_array_length(array); |
| element_size = njs_typed_array_element_size(array->type); |
| base = &buffer->u.u8[array->offset * element_size]; |
| orig = base; |
| |
| if (ctx.function != NULL) { |
| cmp = njs_typed_array_generic_compare; |
| base = njs_mp_alloc(vm->mem_pool, length * element_size); |
| if (njs_slow_path(base == NULL)) { |
| njs_memory_error(vm); |
| return NJS_ERROR; |
| } |
| |
| memcpy(base, &buffer->u.u8[array->offset * element_size], |
| length * element_size); |
| } |
| |
| njs_qsort(base, length, element_size, cmp, &ctx); |
| if (ctx.function != NULL) { |
| if (&buffer->u.u8[array->offset * element_size] == orig) { |
| memcpy(orig, base, length * element_size); |
| } |
| |
| njs_mp_free(vm->mem_pool, base); |
| } |
| |
| if (njs_slow_path(ctx.exception)) { |
| return NJS_ERROR; |
| } |
| |
| njs_set_typed_array(&vm->retval, array); |
| |
| return NJS_OK; |
| } |
| |
| |
| njs_int_t |
| njs_typed_array_to_chain(njs_vm_t *vm, njs_chb_t *chain, |
| njs_typed_array_t *array, njs_value_t *sep) |
| { |
| size_t size, length, arr_length; |
| uint32_t i; |
| njs_string_prop_t separator; |
| |
| if (sep == NULL) { |
| sep = njs_value_arg(&njs_string_comma); |
| } |
| |
| (void) njs_string_prop(&separator, sep); |
| |
| arr_length = njs_typed_array_length(array); |
| |
| if (arr_length == 0) { |
| return 0; |
| } |
| |
| for (i = 0; i < arr_length; i++) { |
| njs_number_to_chain(vm, chain, njs_typed_array_prop(array, i)); |
| njs_chb_append(chain, separator.start, separator.size); |
| } |
| |
| njs_chb_drop(chain, separator.size); |
| |
| size = njs_chb_size(chain); |
| |
| if (njs_utf8_length(separator.start, separator.size) >= 0) { |
| length = size - (separator.size - separator.length) * (arr_length - 1); |
| |
| } else { |
| length = 0; |
| } |
| |
| return length; |
| } |
| |
| |
| static njs_int_t |
| njs_typed_array_prototype_join(njs_vm_t *vm, njs_value_t *args, |
| njs_uint_t nargs, njs_index_t unused) |
| { |
| u_char *p; |
| size_t size, length, arr_length; |
| njs_int_t ret; |
| njs_chb_t chain; |
| njs_value_t *this, *separator; |
| njs_typed_array_t *array; |
| |
| this = njs_argument(args, 0); |
| if (njs_slow_path(!njs_is_typed_array(this))) { |
| njs_type_error(vm, "this is not a typed array"); |
| return NJS_ERROR; |
| } |
| |
| array = njs_typed_array(this); |
| if (njs_slow_path(njs_is_detached_buffer(array->buffer))) { |
| njs_type_error(vm, "detached buffer"); |
| return NJS_ERROR; |
| } |
| |
| arr_length = njs_typed_array_length(array); |
| |
| separator = njs_arg(args, nargs, 1); |
| |
| if (njs_slow_path(!njs_is_string(separator))) { |
| if (njs_is_undefined(separator)) { |
| separator = njs_value_arg(&njs_string_comma); |
| |
| } else { |
| ret = njs_value_to_string(vm, separator, separator); |
| if (njs_slow_path(ret != NJS_OK)) { |
| return ret; |
| } |
| } |
| } |
| |
| if (arr_length == 0) { |
| vm->retval = njs_string_empty; |
| return NJS_OK; |
| } |
| |
| njs_chb_init(&chain, vm->mem_pool); |
| |
| length = njs_typed_array_to_chain(vm, &chain, array, separator); |
| size = njs_chb_size(&chain); |
| |
| p = njs_string_alloc(vm, &vm->retval, size, length); |
| if (njs_slow_path(p == NULL)) { |
| return NJS_ERROR; |
| } |
| |
| njs_chb_join_to(&chain, p); |
| njs_chb_destroy(&chain); |
| |
| return NJS_OK; |
| } |
| |
| |
| static njs_int_t |
| njs_typed_array_prototype_iterator_obj(njs_vm_t *vm, njs_value_t *args, |
| njs_uint_t nargs, njs_index_t kind) |
| { |
| njs_value_t *this; |
| njs_typed_array_t *array; |
| |
| this = njs_argument(args, 0); |
| if (njs_slow_path(!njs_is_typed_array(this))) { |
| njs_type_error(vm, "this is not a typed array"); |
| return NJS_ERROR; |
| } |
| |
| array = njs_typed_array(this); |
| if (njs_slow_path(njs_is_detached_buffer(array->buffer))) { |
| njs_type_error(vm, "detached buffer"); |
| return NJS_ERROR; |
| } |
| |
| return njs_array_iterator_create(vm, this, &vm->retval, kind); |
| } |
| |
| |
| static njs_int_t |
| njs_typed_array_constructor_intrinsic(njs_vm_t *vm, njs_value_t *args, |
| njs_uint_t nargs, njs_index_t unused) |
| { |
| njs_type_error(vm, "Abstract class TypedArray not directly constructable"); |
| |
| return NJS_ERROR; |
| } |
| |
| |
| static const njs_object_prop_t njs_typed_array_constructor_props[] = |
| { |
| { |
| .type = NJS_PROPERTY, |
| .name = njs_string("name"), |
| .value = njs_string("TypedArray"), |
| .configurable = 1, |
| }, |
| |
| { |
| .type = NJS_PROPERTY, |
| .name = njs_string("length"), |
| .value = njs_value(NJS_NUMBER, 0, 0), |
| .configurable = 1, |
| }, |
| |
| { |
| .type = NJS_PROPERTY_HANDLER, |
| .name = njs_string("prototype"), |
| .value = njs_prop_handler(njs_object_prototype_create), |
| }, |
| |
| { |
| .type = NJS_PROPERTY, |
| .name = njs_wellknown_symbol(NJS_SYMBOL_SPECIES), |
| .value = njs_value(NJS_INVALID, 1, NAN), |
| .getter = njs_native_function(njs_typed_array_get_this, 0), |
| .setter = njs_value(NJS_UNDEFINED, 0, NAN), |
| .writable = NJS_ATTRIBUTE_UNSET, |
| .configurable = 1, |
| .enumerable = 0, |
| }, |
| |
| { |
| .type = NJS_PROPERTY, |
| .name = njs_string("of"), |
| .value = njs_native_function(njs_typed_array_of, 0), |
| .writable = 1, |
| .configurable = 1, |
| }, |
| |
| { |
| .type = NJS_PROPERTY, |
| .name = njs_string("from"), |
| .value = njs_native_function(njs_typed_array_from, 1), |
| .writable = 1, |
| .configurable = 1, |
| }, |
| }; |
| |
| |
| static const njs_object_init_t njs_typed_array_constructor_init = { |
| njs_typed_array_constructor_props, |
| njs_nitems(njs_typed_array_constructor_props), |
| }; |
| |
| |
| static const njs_object_prop_t njs_typed_array_prototype_properties[] = |
| { |
| { |
| .type = NJS_PROPERTY, |
| .name = njs_wellknown_symbol(NJS_SYMBOL_TO_STRING_TAG), |
| .value = njs_value(NJS_INVALID, 1, NAN), |
| .getter = njs_native_function(njs_typed_array_get_string_tag, |
| 0), |
| .setter = njs_value(NJS_UNDEFINED, 0, NAN), |
| .writable = NJS_ATTRIBUTE_UNSET, |
| .configurable = 1, |
| .enumerable = 0, |
| }, |
| |
| { |
| .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("length"), |
| .value = njs_value(NJS_INVALID, 1, NAN), |
| .getter = njs_native_function(njs_typed_array_prototype_length, 0), |
| .setter = njs_value(NJS_UNDEFINED, 0, NAN), |
| .writable = NJS_ATTRIBUTE_UNSET, |
| .configurable = 1, |
| .enumerable = 0, |
| }, |
| |
| { |
| .type = NJS_PROPERTY, |
| .name = njs_string("copyWithin"), |
| .value = njs_native_function(njs_typed_array_prototype_copy_within, 2), |
| .writable = 1, |
| .configurable = 1, |
| }, |
| |
| { |
| .type = NJS_PROPERTY, |
| .name = njs_string("entries"), |
| .value = njs_native_function2(njs_typed_array_prototype_iterator_obj, 0, |
| NJS_ENUM_BOTH), |
| .writable = 1, |
| .configurable = 1, |
| }, |
| |
| { |
| .type = NJS_PROPERTY, |
| .name = njs_string("every"), |
| .value = njs_native_function2(njs_typed_array_prototype_iterator, 1, |
| NJS_ARRAY_EVERY), |
| .writable = 1, |
| .configurable = 1, |
| }, |
| |
| { |
| .type = NJS_PROPERTY, |
| .name = njs_string("filter"), |
| .value = njs_native_function2(njs_typed_array_prototype_iterator, 1, |
| NJS_ARRAY_FILTER), |
| .writable = 1, |
| .configurable = 1, |
| }, |
| |
| { |
| .type = NJS_PROPERTY, |
| .name = njs_string("find"), |
| .value = njs_native_function2(njs_typed_array_prototype_iterator, 1, |
| NJS_ARRAY_FIND), |
| .writable = 1, |
| .configurable = 1, |
| }, |
| |
| { |
| .type = NJS_PROPERTY, |
| .name = njs_string("findIndex"), |
| .value = njs_native_function2(njs_typed_array_prototype_iterator, 1, |
| NJS_ARRAY_FIND_INDEX), |
| .writable = 1, |
| .configurable = 1, |
| }, |
| |
| { |
| .type = NJS_PROPERTY, |
| .name = njs_string("forEach"), |
| .value = njs_native_function2(njs_typed_array_prototype_iterator, 1, |
| NJS_ARRAY_FOR_EACH), |
| .writable = 1, |
| .configurable = 1, |
| }, |
| |
| { |
| .type = NJS_PROPERTY, |
| .name = njs_string("includes"), |
| .value = njs_native_function2(njs_typed_array_prototype_index_of, 1, 1), |
| .writable = 1, |
| .configurable = 1, |
| }, |
| |
| { |
| .type = NJS_PROPERTY, |
| .name = njs_string("indexOf"), |
| .value = njs_native_function2(njs_typed_array_prototype_index_of, 1, 0), |
| .writable = 1, |
| .configurable = 1, |
| }, |
| |
| { |
| .type = NJS_PROPERTY, |
| .name = njs_string("join"), |
| .value = njs_native_function(njs_typed_array_prototype_join, 1), |
| .writable = 1, |
| .configurable = 1, |
| }, |
| |
| { |
| .type = NJS_PROPERTY, |
| .name = njs_string("fill"), |
| .value = njs_native_function(njs_typed_array_prototype_fill, 1), |
| .writable = 1, |
| .configurable = 1, |
| }, |
| |
| { |
| .type = NJS_PROPERTY, |
| .name = njs_string("keys"), |
| .value = njs_native_function2(njs_typed_array_prototype_iterator_obj, 0, |
| NJS_ENUM_KEYS), |
| .writable = 1, |
| .configurable = 1, |
| }, |
| |
| { |
| .type = NJS_PROPERTY, |
| .name = njs_string("lastIndexOf"), |
| .value = njs_native_function2(njs_typed_array_prototype_index_of, 1, 2), |
| .writable = 1, |
| .configurable = 1, |
| }, |
| |
| { |
| .type = NJS_PROPERTY, |
| .name = njs_string("map"), |
| .value = njs_native_function2(njs_typed_array_prototype_iterator, 1, |
| NJS_ARRAY_MAP), |
| .writable = 1, |
| .configurable = 1, |
| }, |
| |
| { |
| .type = NJS_PROPERTY, |
| .name = njs_string("reduce"), |
| .value = njs_native_function2(njs_typed_array_prototype_reduce, 1, 0), |
| .writable = 1, |
| .configurable = 1, |
| }, |
| |
| { |
| .type = NJS_PROPERTY, |
| .name = njs_string("reduceRight"), |
| .value = njs_native_function2(njs_typed_array_prototype_reduce, 1, 1), |
| .writable = 1, |
| .configurable = 1, |
| }, |
| |
| { |
| .type = NJS_PROPERTY, |
| .name = njs_string("reverse"), |
| .value = njs_native_function(njs_typed_array_prototype_reverse, 0), |
| .writable = 1, |
| .configurable = 1, |
| }, |
| |
| { |
| .type = NJS_PROPERTY, |
| .name = njs_string("set"), |
| .value = njs_native_function(njs_typed_array_prototype_set, 2), |
| .writable = 1, |
| .configurable = 1, |
| }, |
| |
| { |
| .type = NJS_PROPERTY, |
| .name = njs_string("slice"), |
| .value = njs_native_function2(njs_typed_array_prototype_slice, 2, 1), |
| .writable = 1, |
| .configurable = 1, |
| }, |
| |
| { |
| .type = NJS_PROPERTY, |
| .name = njs_string("some"), |
| .value = njs_native_function2(njs_typed_array_prototype_iterator, 1, |
| NJS_ARRAY_SOME), |
| .writable = 1, |
| .configurable = 1, |
| }, |
| |
| { |
| .type = NJS_PROPERTY, |
| .name = njs_string("sort"), |
| .value = njs_native_function(njs_typed_array_prototype_sort, 1), |
| .writable = 1, |
| .configurable = 1, |
| }, |
| |
| { |
| .type = NJS_PROPERTY, |
| .name = njs_string("subarray"), |
| .value = njs_native_function2(njs_typed_array_prototype_slice, 2, 0), |
| .writable = 1, |
| .configurable = 1, |
| }, |
| |
| { |
| .type = NJS_PROPERTY, |
| .name = njs_string("toString"), |
| .value = njs_native_function(njs_array_prototype_to_string, 0), |
| .writable = 1, |
| .configurable = 1, |
| }, |
| |
| { |
| .type = NJS_PROPERTY, |
| .name = njs_string("values"), |
| .value = njs_native_function2(njs_typed_array_prototype_iterator_obj, 0, |
| NJS_ENUM_VALUES), |
| .writable = 1, |
| .configurable = 1, |
| }, |
| |
| { |
| .type = NJS_PROPERTY, |
| .name = njs_wellknown_symbol(NJS_SYMBOL_ITERATOR), |
| .value = njs_native_function2(njs_typed_array_prototype_iterator_obj, 0, |
| NJS_ENUM_VALUES), |
| .writable = 1, |
| .configurable = 1, |
| }, |
| }; |
| |
| |
| static const njs_object_init_t njs_typed_array_prototype_init = { |
| njs_typed_array_prototype_properties, |
| njs_nitems(njs_typed_array_prototype_properties), |
| }; |
| |
| |
| const njs_object_type_init_t njs_typed_array_type_init = { |
| .constructor = njs_native_ctor(njs_typed_array_constructor_intrinsic, 0, 0), |
| .prototype_props = &njs_typed_array_prototype_init, |
| .constructor_props = &njs_typed_array_constructor_init, |
| .prototype_value = { .object = { .type = NJS_OBJECT } }, |
| }; |
| |
| |
| 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; |
| |
| ret = njs_value_to_index(vm, njs_arg(args, nargs, 2), &offset); |
| if (njs_slow_path(ret != NJS_OK)) { |
| return NJS_ERROR; |
| } |
| |
| buffer = njs_array_buffer(njs_argument(args, 1)); |
| if (njs_slow_path(njs_is_detached_buffer(buffer))) { |
| njs_type_error(vm, "detached buffer"); |
| 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), |
| }; |
| |
| |
| 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_slow_path(njs_is_detached_buffer(view->buffer))) { |
| njs_type_error(vm, "detached buffer"); |
| return NJS_ERROR; |
| } |
| |
| 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_slow_path(njs_is_detached_buffer(view->buffer))) { |
| njs_type_error(vm, "detached buffer"); |
| return NJS_ERROR; |
| } |
| |
| 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 |