/src/njs/src/njs_extern.c
Line | Count | Source (jump to first uncovered line) |
1 | | |
2 | | /* |
3 | | * Copyright (C) Igor Sysoev |
4 | | * Copyright (C) NGINX, Inc. |
5 | | */ |
6 | | |
7 | | |
8 | | #include <njs_main.h> |
9 | | |
10 | | |
11 | | static njs_int_t njs_external_prop_handler(njs_vm_t *vm, |
12 | | njs_object_prop_t *self, uint32_t atom_id, njs_value_t *value, |
13 | | njs_value_t *setval, njs_value_t *retval); |
14 | | |
15 | | |
16 | | static njs_int_t |
17 | | njs_external_add(njs_vm_t *vm, njs_arr_t *protos, |
18 | | const njs_external_t *external, njs_uint_t n) |
19 | 133k | { |
20 | 133k | size_t size; |
21 | 133k | njs_int_t ret; |
22 | 133k | njs_value_t prop_name; |
23 | 133k | const u_char *start; |
24 | 133k | njs_flathsh_t *hash; |
25 | 133k | njs_function_t *function; |
26 | 133k | njs_object_prop_t *prop; |
27 | 133k | njs_exotic_slots_t *slot, *next; |
28 | 133k | njs_flathsh_query_t lhq; |
29 | 133k | const njs_external_t *end; |
30 | | |
31 | 133k | slot = njs_arr_add(protos); |
32 | 133k | njs_memzero(slot, sizeof(njs_exotic_slots_t)); |
33 | | |
34 | 133k | hash = &slot->external_shared_hash; |
35 | 133k | njs_lvlhsh_init(hash); |
36 | | |
37 | 133k | if (n == 0) { |
38 | 0 | return NJS_OK; |
39 | 0 | } |
40 | | |
41 | 133k | lhq.replace = 0; |
42 | 133k | lhq.proto = &njs_object_hash_proto; |
43 | 133k | lhq.pool = vm->mem_pool; |
44 | | |
45 | 133k | end = external + n; |
46 | | |
47 | 1.32M | while (external < end) { |
48 | | |
49 | 1.18M | if ((external->flags & NJS_EXTERN_TYPE_MASK) == NJS_EXTERN_SELF) { |
50 | 0 | slot->writable = external->u.object.writable; |
51 | 0 | slot->configurable = external->u.object.configurable; |
52 | 0 | slot->enumerable = external->u.object.enumerable; |
53 | 0 | slot->prop_handler = external->u.object.prop_handler; |
54 | 0 | slot->magic32 = external->u.object.magic32; |
55 | 0 | slot->keys = external->u.object.keys; |
56 | |
|
57 | 0 | external++; |
58 | 0 | continue; |
59 | 0 | } |
60 | | |
61 | 1.18M | if (external->flags & NJS_EXTERN_SYMBOL) { |
62 | 111k | lhq.key_hash = external->name.symbol; |
63 | | |
64 | 1.07M | } else { |
65 | 1.07M | ret = njs_atom_string_create(vm, &prop_name, |
66 | 1.07M | external->name.string.start, |
67 | 1.07M | external->name.string.length); |
68 | 1.07M | if (njs_slow_path(ret != NJS_OK)) { |
69 | 0 | return NJS_ERROR; |
70 | 0 | } |
71 | | |
72 | 1.07M | lhq.key_hash = prop_name.atom_id; |
73 | 1.07M | } |
74 | | |
75 | 1.18M | ret = njs_flathsh_unique_insert(hash, &lhq); |
76 | 1.18M | if (njs_slow_path(ret != NJS_OK)) { |
77 | 0 | njs_internal_error(vm, "lvlhsh insert failed"); |
78 | 0 | return NJS_ERROR; |
79 | 0 | } |
80 | | |
81 | 1.18M | prop = lhq.value; |
82 | | |
83 | 1.18M | prop->type = NJS_PROPERTY; |
84 | 1.18M | prop->enumerable = external->enumerable; |
85 | 1.18M | prop->configurable = external->configurable; |
86 | 1.18M | prop->writable = external->writable; |
87 | 1.18M | prop->u.value = njs_value_invalid; |
88 | | |
89 | | |
90 | 1.18M | switch (external->flags & NJS_EXTERN_TYPE_MASK) { |
91 | 802k | case NJS_EXTERN_METHOD: |
92 | 802k | function = njs_mp_zalloc(vm->mem_pool, sizeof(njs_function_t)); |
93 | 802k | if (njs_slow_path(function == NULL)) { |
94 | 0 | goto memory_error; |
95 | 0 | } |
96 | | |
97 | 802k | function->object.shared_hash = vm->shared->arrow_instance_hash; |
98 | 802k | function->object.type = NJS_FUNCTION; |
99 | 802k | function->object.shared = 1; |
100 | 802k | function->object.extensible = 1; |
101 | 802k | function->native = 1; |
102 | 802k | function->u.native = external->u.method.native; |
103 | 802k | function->magic8 = external->u.method.magic8; |
104 | 802k | function->ctor = external->u.method.ctor; |
105 | | |
106 | 802k | njs_set_function(njs_prop_value(prop), function); |
107 | | |
108 | 802k | break; |
109 | | |
110 | 364k | case NJS_EXTERN_PROPERTY: |
111 | 364k | if (external->u.property.handler != NULL) { |
112 | 252k | prop->type = NJS_PROPERTY_HANDLER; |
113 | 252k | prop->u.value.type = NJS_INVALID; |
114 | 252k | prop->u.value.data.truth = 1; |
115 | 252k | njs_prop_magic16(prop) = external->u.property.magic16; |
116 | 252k | njs_prop_magic32(prop) = external->u.property.magic32; |
117 | 252k | njs_prop_handler(prop) = external->u.property.handler; |
118 | | |
119 | 252k | } else { |
120 | 111k | start = (u_char *) external->u.property.value; |
121 | 111k | size = njs_strlen(start); |
122 | | |
123 | 111k | ret = njs_string_create(vm, &prop->u.value, start, size); |
124 | 111k | if (njs_slow_path(ret != NJS_OK)) { |
125 | 0 | return NJS_ERROR; |
126 | 0 | } |
127 | 111k | } |
128 | | |
129 | 364k | break; |
130 | | |
131 | 364k | case NJS_EXTERN_OBJECT: |
132 | 22.2k | default: |
133 | 22.2k | next = njs_arr_item(protos, protos->items); |
134 | | |
135 | 22.2k | ret = njs_external_add(vm, protos, external->u.object.properties, |
136 | 22.2k | external->u.object.nproperties); |
137 | 22.2k | if (njs_slow_path(ret != NJS_OK)) { |
138 | 0 | return ret; |
139 | 0 | } |
140 | | |
141 | 22.2k | prop->type = NJS_PROPERTY_HANDLER; |
142 | 22.2k | prop->u.value.type = NJS_INVALID; |
143 | 22.2k | prop->u.value.data.truth = 1; |
144 | 22.2k | njs_prop_magic16(prop) = next - slot; |
145 | 22.2k | njs_prop_magic32(prop) = lhq.key_hash; |
146 | 22.2k | njs_prop_handler(prop) = njs_external_prop_handler; |
147 | | |
148 | 22.2k | if (external->u.object.prop_handler) { |
149 | 0 | if (next->prop_handler) { |
150 | 0 | njs_internal_error(vm, "overwritten self prop_handler"); |
151 | 0 | return NJS_ERROR; |
152 | 0 | } |
153 | | |
154 | 0 | next->writable = external->u.object.writable; |
155 | 0 | next->configurable = external->u.object.configurable; |
156 | 0 | next->enumerable = external->u.object.enumerable; |
157 | |
|
158 | 0 | next->prop_handler = external->u.object.prop_handler; |
159 | 0 | next->magic32 = external->u.object.magic32; |
160 | 0 | } |
161 | | |
162 | 22.2k | if (external->u.object.keys) { |
163 | 0 | if (next->keys) { |
164 | 0 | njs_internal_error(vm, "overwritten self keys"); |
165 | 0 | return NJS_ERROR; |
166 | 0 | } |
167 | | |
168 | 0 | next->keys = external->u.object.keys; |
169 | 0 | } |
170 | | |
171 | 22.2k | break; |
172 | 1.18M | } |
173 | | |
174 | 1.18M | external++; |
175 | 1.18M | } |
176 | | |
177 | 133k | return NJS_OK; |
178 | | |
179 | 0 | memory_error: |
180 | |
|
181 | 0 | njs_memory_error(vm); |
182 | |
|
183 | 0 | return NJS_ERROR; |
184 | 133k | } |
185 | | |
186 | | |
187 | | static njs_int_t |
188 | | njs_external_prop_handler(njs_vm_t *vm, njs_object_prop_t *self, |
189 | | uint32_t atom_id, njs_value_t *value, njs_value_t *setval, |
190 | | njs_value_t *retval) |
191 | 6 | { |
192 | 6 | njs_int_t ret; |
193 | 6 | njs_object_prop_t *prop; |
194 | 6 | njs_external_ptr_t external; |
195 | 6 | njs_object_value_t *ov; |
196 | 6 | njs_exotic_slots_t *slots; |
197 | 6 | njs_flathsh_query_t lhq; |
198 | | |
199 | 6 | if (njs_slow_path(retval == NULL)) { |
200 | 0 | return NJS_DECLINED; |
201 | 0 | } |
202 | | |
203 | 6 | slots = NULL; |
204 | | |
205 | 6 | if (njs_slow_path(setval != NULL)) { |
206 | 0 | *retval = *setval; |
207 | |
|
208 | 6 | } else { |
209 | 6 | ov = njs_object_value_alloc(vm, NJS_OBJ_TYPE_OBJECT, 0, NULL); |
210 | 6 | if (njs_slow_path(ov == NULL)) { |
211 | 0 | return NJS_ERROR; |
212 | 0 | } |
213 | | |
214 | 6 | slots = njs_object(value)->slots + njs_prop_magic16(self); |
215 | | |
216 | 6 | ov->object.shared_hash = slots->external_shared_hash; |
217 | 6 | ov->object.slots = slots; |
218 | | |
219 | 6 | external = njs_vm_external(vm, NJS_PROTO_ID_ANY, value); |
220 | | |
221 | 6 | njs_set_data(&ov->value, external, njs_value_external_tag(value)); |
222 | 6 | njs_set_object_value(retval, ov); |
223 | 6 | } |
224 | | |
225 | 6 | lhq.key_hash = atom_id; |
226 | 6 | lhq.replace = 1; |
227 | 6 | lhq.pool = vm->mem_pool; |
228 | 6 | lhq.proto = &njs_object_hash_proto; |
229 | | |
230 | 6 | ret = njs_flathsh_unique_insert(njs_object_hash(value), &lhq); |
231 | 6 | if (njs_slow_path(ret != NJS_OK)) { |
232 | 0 | njs_internal_error(vm, "lvlhsh insert/replace failed"); |
233 | 0 | return NJS_ERROR; |
234 | 0 | } |
235 | | |
236 | 6 | prop = lhq.value; |
237 | | |
238 | 6 | prop->type = NJS_PROPERTY; |
239 | 6 | prop->enumerable = self->enumerable; |
240 | 6 | prop->configurable = self->configurable; |
241 | 6 | prop->writable = self->writable; |
242 | | |
243 | 6 | prop->u.value = *retval; |
244 | | |
245 | 6 | return NJS_OK; |
246 | 6 | } |
247 | | |
248 | | |
249 | | static njs_uint_t |
250 | | njs_external_protos(const njs_external_t *external, njs_uint_t size) |
251 | 133k | { |
252 | 133k | njs_uint_t n; |
253 | | |
254 | 133k | n = 1; |
255 | | |
256 | 1.32M | while (size != 0) { |
257 | 1.18M | if ((external->flags & 3) == NJS_EXTERN_OBJECT) { |
258 | 22.2k | n += njs_external_protos(external->u.object.properties, |
259 | 22.2k | external->u.object.nproperties); |
260 | 22.2k | } |
261 | | |
262 | 1.18M | size--; |
263 | 1.18M | external++; |
264 | 1.18M | } |
265 | | |
266 | 133k | return n; |
267 | 133k | } |
268 | | |
269 | | |
270 | | njs_int_t |
271 | | njs_vm_external_prototype(njs_vm_t *vm, const njs_external_t *definition, |
272 | | njs_uint_t n) |
273 | 111k | { |
274 | 111k | njs_arr_t *protos, **pr; |
275 | 111k | njs_int_t ret; |
276 | 111k | njs_uint_t size; |
277 | | |
278 | 111k | size = njs_external_protos(definition, n) + 1; |
279 | | |
280 | 111k | protos = njs_arr_create(vm->mem_pool, size, sizeof(njs_exotic_slots_t)); |
281 | 111k | if (njs_slow_path(protos == NULL)) { |
282 | 0 | njs_memory_error(vm); |
283 | 0 | return -1; |
284 | 0 | } |
285 | | |
286 | 111k | ret = njs_external_add(vm, protos, definition, n); |
287 | 111k | if (njs_slow_path(ret != NJS_OK)) { |
288 | 0 | njs_internal_error(vm, "njs_vm_external_add() failed"); |
289 | 0 | return -1; |
290 | 0 | } |
291 | | |
292 | 111k | if (vm->protos == NULL) { |
293 | 7.42k | vm->protos = njs_arr_create(vm->mem_pool, 4, sizeof(njs_arr_t *)); |
294 | 7.42k | if (njs_slow_path(vm->protos == NULL)) { |
295 | 0 | return -1; |
296 | 0 | } |
297 | 7.42k | } |
298 | | |
299 | 111k | pr = njs_arr_add(vm->protos); |
300 | 111k | if (njs_slow_path(pr == NULL)) { |
301 | 0 | return -1; |
302 | 0 | } |
303 | | |
304 | 111k | *pr = protos; |
305 | | |
306 | 111k | return vm->protos->items - 1; |
307 | 111k | } |
308 | | |
309 | | |
310 | | static njs_int_t |
311 | | njs_vm_external_constructor_handler(njs_vm_t *vm, njs_object_prop_t *prop, |
312 | | uint32_t atom_id, njs_value_t *value, njs_value_t *setval, |
313 | | njs_value_t *retval) |
314 | 0 | { |
315 | 0 | njs_set_function(retval, &njs_vm_ctor(vm, njs_prop_magic32(prop))); |
316 | |
|
317 | 0 | return NJS_OK; |
318 | 0 | } |
319 | | |
320 | | |
321 | | njs_int_t |
322 | | njs_vm_external_constructor(njs_vm_t *vm, const njs_str_t *name, |
323 | | const njs_function_native_t native, const njs_external_t *ctor_props, |
324 | | njs_uint_t ctor_nprops, const njs_external_t *proto_props, |
325 | | njs_uint_t proto_nprops) |
326 | 0 | { |
327 | 0 | njs_int_t ret, index, proto_id; |
328 | 0 | njs_arr_t **pprotos; |
329 | 0 | njs_function_t *constructor; |
330 | 0 | njs_exotic_slots_t *slots; |
331 | 0 | njs_object_prototype_t *prototype; |
332 | |
|
333 | 0 | index = njs_vm_ctor_push(vm); |
334 | 0 | if (njs_slow_path(index < 0)) { |
335 | 0 | njs_internal_error(vm, "njs_vm_ctor_push() failed"); |
336 | 0 | return -1; |
337 | 0 | } |
338 | | |
339 | 0 | proto_id = njs_vm_external_prototype(vm, proto_props, proto_nprops); |
340 | 0 | if (njs_slow_path(proto_id < 0)) { |
341 | 0 | njs_internal_error(vm, "njs_vm_external_prototype(proto_props) failed"); |
342 | 0 | return -1; |
343 | 0 | } |
344 | | |
345 | 0 | prototype = njs_shared_prototype(vm->shared, index); |
346 | 0 | njs_memzero(prototype, sizeof(njs_object_prototype_t)); |
347 | 0 | prototype->object.type = NJS_OBJECT; |
348 | 0 | prototype->object.extensible = 1; |
349 | |
|
350 | 0 | pprotos = njs_arr_item(vm->protos, proto_id); |
351 | 0 | slots = (*pprotos)->start; |
352 | 0 | prototype->object.shared_hash = slots->external_shared_hash; |
353 | |
|
354 | 0 | proto_id = njs_vm_external_prototype(vm, ctor_props, ctor_nprops); |
355 | 0 | if (njs_slow_path(proto_id < 0)) { |
356 | 0 | njs_internal_error(vm, "njs_vm_external_prototype(ctor_props) failed"); |
357 | 0 | return -1; |
358 | 0 | } |
359 | | |
360 | 0 | constructor = njs_shared_ctor(vm->shared, index); |
361 | 0 | njs_memzero(constructor, sizeof(njs_function_t)); |
362 | 0 | constructor->object.type = NJS_FUNCTION; |
363 | 0 | constructor->u.native = native; |
364 | 0 | constructor->magic8 = index; |
365 | 0 | constructor->native = 1; |
366 | 0 | constructor->ctor = 1; |
367 | |
|
368 | 0 | pprotos = njs_arr_item(vm->protos, proto_id); |
369 | 0 | slots = (*pprotos)->start; |
370 | 0 | constructor->object.shared_hash = slots->external_shared_hash; |
371 | |
|
372 | 0 | ret = njs_vm_bind_handler(vm, name, njs_vm_external_constructor_handler, 0, |
373 | 0 | index, 1); |
374 | 0 | if (njs_slow_path(ret != NJS_OK)) { |
375 | 0 | return NJS_ERROR; |
376 | 0 | } |
377 | | |
378 | 0 | return index; |
379 | 0 | } |
380 | | |
381 | | |
382 | | njs_int_t |
383 | | njs_vm_external_create(njs_vm_t *vm, njs_value_t *value, njs_int_t proto_id, |
384 | | njs_external_ptr_t external, njs_bool_t shared) |
385 | 52.0k | { |
386 | 52.0k | njs_arr_t **pprotos; |
387 | 52.0k | njs_object_value_t *ov; |
388 | 52.0k | njs_exotic_slots_t *slots; |
389 | | |
390 | 52.0k | if (vm->protos == NULL || (njs_int_t) vm->protos->items <= proto_id) { |
391 | 0 | return NJS_ERROR; |
392 | 0 | } |
393 | | |
394 | 52.0k | ov = njs_object_value_alloc(vm, NJS_OBJ_TYPE_OBJECT, 0, NULL); |
395 | 52.0k | if (njs_slow_path(ov == NULL)) { |
396 | 0 | return NJS_ERROR; |
397 | 0 | } |
398 | | |
399 | 52.0k | pprotos = njs_arr_item(vm->protos, proto_id); |
400 | 52.0k | slots = (*pprotos)->start; |
401 | | |
402 | 52.0k | ov->object.shared_hash = slots->external_shared_hash; |
403 | 52.0k | ov->object.shared = shared; |
404 | 52.0k | ov->object.slots = slots; |
405 | | |
406 | 52.0k | njs_set_object_value(value, ov); |
407 | 52.0k | njs_set_data(&ov->value, external, njs_make_tag(proto_id)); |
408 | | |
409 | 52.0k | return NJS_OK; |
410 | 52.0k | } |
411 | | |
412 | | |
413 | | njs_external_ptr_t |
414 | | njs_vm_external(njs_vm_t *vm, njs_int_t proto_id, const njs_value_t *value) |
415 | 6 | { |
416 | 6 | njs_external_ptr_t external; |
417 | | |
418 | 6 | if (njs_fast_path(njs_is_object_data(value, njs_make_tag(proto_id)))) { |
419 | 6 | external = njs_object_data(value); |
420 | 6 | if (external == NULL) { |
421 | 6 | external = vm->external; |
422 | 6 | } |
423 | | |
424 | 6 | return external; |
425 | 6 | } |
426 | | |
427 | 0 | return NULL; |
428 | 6 | } |
429 | | |
430 | | |
431 | | njs_int_t |
432 | | njs_value_external_tag(const njs_value_t *value) |
433 | 6 | { |
434 | 6 | if (njs_is_object_data(value, njs_make_tag(NJS_PROTO_ID_ANY))) { |
435 | 6 | return njs_object_value(value)->data.magic32; |
436 | 6 | } |
437 | | |
438 | 0 | return -1; |
439 | 6 | } |
440 | | |
441 | | |
442 | | njs_int_t |
443 | | njs_external_property(njs_vm_t *vm, njs_object_prop_t *prop, uint32_t unused, |
444 | | njs_value_t *value, njs_value_t *setval, njs_value_t *retval) |
445 | 0 | { |
446 | 0 | char *p; |
447 | 0 | njs_int_t i; |
448 | 0 | njs_uint_t ui; |
449 | |
|
450 | 0 | p = njs_vm_external(vm, NJS_PROTO_ID_ANY, value); |
451 | 0 | if (p == NULL) { |
452 | 0 | njs_value_undefined_set(retval); |
453 | 0 | return NJS_DECLINED; |
454 | 0 | } |
455 | | |
456 | 0 | switch (njs_vm_prop_magic16(prop)) { |
457 | 0 | case NJS_EXTERN_TYPE_INT: |
458 | 0 | i = *(njs_int_t *) (p + njs_vm_prop_magic32(prop)); |
459 | 0 | njs_value_number_set(retval, i); |
460 | 0 | break; |
461 | | |
462 | 0 | case NJS_EXTERN_TYPE_UINT: |
463 | 0 | ui = *(njs_uint_t *) (p + njs_vm_prop_magic32(prop)); |
464 | 0 | njs_value_number_set(retval, ui); |
465 | 0 | break; |
466 | | |
467 | 0 | case NJS_EXTERN_TYPE_VALUE: |
468 | 0 | default: |
469 | 0 | njs_value_assign(retval, |
470 | 0 | (njs_value_t *) (p + njs_vm_prop_magic32(prop))); |
471 | |
|
472 | 0 | } |
473 | | |
474 | 0 | return NJS_OK; |
475 | 0 | } |