1 /*
2 * Copyright (C) Igor Sysoev
3 * Copyright (C) NGINX, Inc.
4 */
5
6
7 #include <njs_main.h>
8
9
10 njs_array_buffer_t *
njs_array_buffer_alloc(njs_vm_t * vm,uint64_t size,njs_bool_t zeroing)11 njs_array_buffer_alloc(njs_vm_t *vm, uint64_t size, njs_bool_t zeroing)
12 {
13 njs_object_t *proto;
14 njs_array_buffer_t *array;
15
16 if (njs_slow_path(size > UINT32_MAX)) {
17 goto overflow;
18 }
19
20 array = njs_mp_alloc(vm->mem_pool, sizeof(njs_array_buffer_t));
21 if (njs_slow_path(array == NULL)) {
22 goto memory_error;
23 }
24
25 if (zeroing) {
26 array->u.data = njs_mp_zalloc(vm->mem_pool, size);
27
28 } else {
29 array->u.data = njs_mp_alloc(vm->mem_pool, size);
30 }
31
32 if (njs_slow_path(array->u.data == NULL)) {
33 goto memory_error;
34 }
35
36 proto = &vm->prototypes[NJS_OBJ_TYPE_ARRAY_BUFFER].object;
37
38 njs_lvlhsh_init(&array->object.hash);
39 njs_lvlhsh_init(&array->object.shared_hash);
40 array->object.__proto__ = proto;
41 array->object.slots = NULL;
42 array->object.type = NJS_ARRAY_BUFFER;
43 array->object.shared = 0;
44 array->object.extensible = 1;
45 array->object.error_data = 0;
46 array->object.fast_array = 0;
47 array->size = size;
48
49 return array;
50
51 memory_error:
52
53 njs_memory_error(vm);
54
55 return NULL;
56
57 overflow:
58
59 njs_range_error(vm, "Invalid array length");
60
61 return NULL;
62 }
63
64
65 static njs_int_t
njs_array_buffer_constructor(njs_vm_t * vm,njs_value_t * args,njs_uint_t nargs,njs_index_t unused)66 njs_array_buffer_constructor(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
67 njs_index_t unused)
68 {
69 uint64_t size;
70 njs_int_t ret;
71 njs_value_t *value;
72 njs_array_buffer_t *array;
73
74 if (!vm->top_frame->ctor) {
75 njs_type_error(vm, "Constructor ArrayBuffer requires 'new'");
76 return NJS_ERROR;
77 }
78
79 size = 0;
80 value = njs_arg(args, nargs, 1);
81
82 ret = njs_value_to_index(vm, value, &size);
83 if (njs_slow_path(ret != NJS_OK)) {
84 return NJS_ERROR;
85 }
86
87 array = njs_array_buffer_alloc(vm, size, 1);
88 if (njs_slow_path(array == NULL)) {
89 return NJS_ERROR;
90 }
91
92 njs_set_array_buffer(&vm->retval, array);
93
94 return NJS_OK;
95 }
96
97
98 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)99 njs_array_buffer_get_this(njs_vm_t *vm, njs_value_t *args,
100 njs_uint_t nargs, njs_index_t unused)
101 {
102 vm->retval = args[0];
103
104 return NJS_OK;
105 }
106
107
108 static njs_int_t
njs_array_buffer_is_view(njs_vm_t * vm,njs_value_t * args,njs_uint_t nargs,njs_index_t unused)109 njs_array_buffer_is_view(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
110 njs_index_t unused)
111 {
112 njs_set_boolean(&vm->retval, njs_is_typed_array(njs_arg(args, nargs, 1)));
113
114 return NJS_OK;
115 }
116
117
118 njs_int_t
njs_array_buffer_writable(njs_vm_t * vm,njs_array_buffer_t * buffer)119 njs_array_buffer_writable(njs_vm_t *vm, njs_array_buffer_t *buffer)
120 {
121 void *dst;
122
123 if (!buffer->object.shared) {
124 return NJS_OK;
125 }
126
127 dst = njs_mp_alloc(vm->mem_pool, buffer->size);
128 if (njs_slow_path(dst == NULL)) {
129 njs_memory_error(vm);
130 return NJS_ERROR;
131 }
132
133 memcpy(dst, buffer->u.data, buffer->size);
134
135 buffer->object.shared = 0;
136 buffer->u.data = dst;
137
138 return NJS_OK;
139 }
140
141
142 static const njs_object_prop_t njs_array_buffer_constructor_properties[] =
143 {
144 {
145 .type = NJS_PROPERTY,
146 .name = njs_string("name"),
147 .value = njs_string("ArrayBuffer"),
148 .configurable = 1,
149 },
150
151 {
152 .type = NJS_PROPERTY,
153 .name = njs_string("length"),
154 .value = njs_value(NJS_NUMBER, 1, 1.0),
155 .configurable = 1,
156 },
157
158 {
159 .type = NJS_PROPERTY_HANDLER,
160 .name = njs_string("prototype"),
161 .value = njs_prop_handler(njs_object_prototype_create),
162 },
163
164 {
165 .type = NJS_PROPERTY,
166 .name = njs_wellknown_symbol(NJS_SYMBOL_SPECIES),
167 .value = njs_value(NJS_INVALID, 1, NAN),
168 .getter = njs_native_function(njs_array_buffer_get_this, 0),
169 .setter = njs_value(NJS_UNDEFINED, 0, NAN),
170 .writable = NJS_ATTRIBUTE_UNSET,
171 .configurable = 1,
172 .enumerable = 0,
173 },
174
175 {
176 .type = NJS_PROPERTY,
177 .name = njs_string("isView"),
178 .value = njs_native_function(njs_array_buffer_is_view, 1),
179 .writable = 1,
180 .configurable = 1,
181 },
182 };
183
184
185 const njs_object_init_t njs_array_buffer_constructor_init = {
186 njs_array_buffer_constructor_properties,
187 njs_nitems(njs_array_buffer_constructor_properties),
188 };
189
190
191 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)192 njs_array_buffer_prototype_byte_length(njs_vm_t *vm, njs_value_t *args,
193 njs_uint_t nargs, njs_index_t unused)
194 {
195 njs_value_t *value;
196 njs_array_buffer_t *array;
197
198 value = njs_arg(args, nargs, 0);
199
200 if (!njs_is_array_buffer(value)) {
201 njs_type_error(vm, "Method ArrayBuffer.prototype.byteLength called "
202 "on incompatible receiver");
203 return NJS_ERROR;
204 }
205
206 array = njs_array_buffer(value);
207 if (njs_slow_path(njs_is_detached_buffer(array))) {
208 njs_type_error(vm, "detached buffer");
209 return NJS_ERROR;
210 }
211
212 njs_set_number(&vm->retval, array->size);
213
214 return NJS_OK;
215 }
216
217
218 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)219 njs_array_buffer_prototype_slice(njs_vm_t *vm, njs_value_t *args,
220 njs_uint_t nargs, njs_index_t unused)
221 {
222 int64_t len, start, end;
223 njs_int_t ret;
224 njs_value_t *value;
225 njs_array_buffer_t *this, *buffer;
226
227 value = njs_arg(args, nargs, 0);
228
229 if (!njs_is_array_buffer(value)) {
230 njs_type_error(vm, "Method ArrayBuffer.prototype.slice called "
231 "on incompatible receiver");
232 return NJS_ERROR;
233 }
234
235 this = njs_array_buffer(value);
236 len = njs_array_buffer_size(this);
237 end = len;
238
239 value = njs_arg(args, nargs, 1);
240
241 ret = njs_value_to_integer(vm, value, &start);
242 if (njs_slow_path(ret != NJS_OK)) {
243 return ret;
244 }
245
246 value = njs_arg(args, nargs, 2);
247
248 if (!njs_is_undefined(value)) {
249 ret = njs_value_to_integer(vm, value, &end);
250 if (njs_slow_path(ret != NJS_OK)) {
251 return ret;
252 }
253 }
254
255 buffer = njs_array_buffer_slice(vm, this, start, end);
256 if (njs_slow_path(buffer == NULL)) {
257 return NJS_ERROR;
258 }
259
260 njs_set_array_buffer(&vm->retval, buffer);
261
262 return NJS_OK;
263 }
264
265
266 static const njs_object_prop_t njs_array_buffer_prototype_properties[] =
267 {
268 {
269 .type = NJS_PROPERTY_HANDLER,
270 .name = njs_string("constructor"),
271 .value = njs_prop_handler(njs_object_prototype_create_constructor),
272 .writable = 1,
273 .configurable = 1,
274 },
275
276 {
277 .type = NJS_PROPERTY,
278 .name = njs_string("byteLength"),
279 .value = njs_value(NJS_INVALID, 1, NAN),
280 .getter = njs_native_function(njs_array_buffer_prototype_byte_length,
281 0),
282 .setter = njs_value(NJS_UNDEFINED, 0, NAN),
283 .writable = NJS_ATTRIBUTE_UNSET,
284 .configurable = 1,
285 .enumerable = 0,
286 },
287
288 {
289 .type = NJS_PROPERTY,
290 .name = njs_string("slice"),
291 .value = njs_native_function(njs_array_buffer_prototype_slice, 2),
292 .writable = 1,
293 .configurable = 1,
294 .enumerable = 0,
295 },
296
297 {
298 .type = NJS_PROPERTY,
299 .name = njs_wellknown_symbol(NJS_SYMBOL_TO_STRING_TAG),
300 .value = njs_string("ArrayBuffer"),
301 .configurable = 1,
302 },
303 };
304
305
306 const njs_object_init_t njs_array_buffer_prototype_init = {
307 njs_array_buffer_prototype_properties,
308 njs_nitems(njs_array_buffer_prototype_properties),
309 };
310
311
312 const njs_object_type_init_t njs_array_buffer_type_init = {
313 .constructor = njs_native_ctor(njs_array_buffer_constructor, 1, 0),
314 .prototype_props = &njs_array_buffer_prototype_init,
315 .constructor_props = &njs_array_buffer_constructor_init,
316 .prototype_value = { .object = { .type = NJS_OBJECT } },
317 };
318