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