1
2 /*
3 * Copyright (C) Igor Sysoev
4 * Copyright (C) NGINX, Inc.
5 */
6
7
8 #include <njs_main.h>
9
10
11 typedef enum {
12 NJS_OBJECT_INTEGRITY_SEALED,
13 NJS_OBJECT_INTEGRITY_FROZEN,
14 } njs_object_integrity_level_t;
15
16
17 static njs_int_t njs_object_hash_test(njs_lvlhsh_query_t *lhq, void *data);
18 static njs_object_prop_t *njs_object_exist_in_proto(const njs_object_t *begin,
19 const njs_object_t *end, njs_lvlhsh_query_t *lhq);
20 static njs_int_t njs_object_enumerate_array(njs_vm_t *vm,
21 const njs_array_t *array, njs_array_t *items, njs_object_enum_t kind);
22 static njs_int_t njs_object_enumerate_typed_array(njs_vm_t *vm,
23 const njs_typed_array_t *array, njs_array_t *items, njs_object_enum_t kind);
24 static njs_int_t njs_object_enumerate_string(njs_vm_t *vm,
25 const njs_value_t *value, njs_array_t *items, njs_object_enum_t kind);
26 static njs_int_t njs_object_enumerate_object(njs_vm_t *vm,
27 const njs_object_t *object, njs_array_t *items, njs_object_enum_t kind,
28 njs_object_enum_type_t type, njs_bool_t all);
29 static njs_int_t njs_object_own_enumerate_object(njs_vm_t *vm,
30 const njs_object_t *object, const njs_object_t *parent, njs_array_t *items,
31 njs_object_enum_t kind, njs_object_enum_type_t type, njs_bool_t all);
32 static njs_int_t njs_object_define_properties(njs_vm_t *vm, njs_value_t *args,
33 njs_uint_t nargs, njs_index_t unused);
34 static njs_int_t njs_object_set_prototype(njs_vm_t *vm, njs_object_t *object,
35 const njs_value_t *value);
36
37
38 njs_object_t *
njs_object_alloc(njs_vm_t * vm)39 njs_object_alloc(njs_vm_t *vm)
40 {
41 njs_object_t *object;
42
43 object = njs_mp_alloc(vm->mem_pool, sizeof(njs_object_t));
44
45 if (njs_fast_path(object != NULL)) {
46 njs_lvlhsh_init(&object->hash);
47 njs_lvlhsh_init(&object->shared_hash);
48 object->__proto__ = &vm->prototypes[NJS_OBJ_TYPE_OBJECT].object;
49 object->slots = NULL;
50 object->type = NJS_OBJECT;
51 object->shared = 0;
52 object->extensible = 1;
53 object->error_data = 0;
54 object->fast_array = 0;
55
56 return object;
57 }
58
59 njs_memory_error(vm);
60
61 return NULL;
62 }
63
64
65 njs_object_t *
njs_object_value_copy(njs_vm_t * vm,njs_value_t * value)66 njs_object_value_copy(njs_vm_t *vm, njs_value_t *value)
67 {
68 size_t size;
69 njs_object_t *object;
70
71 object = njs_object(value);
72
73 if (!object->shared) {
74 return object;
75 }
76
77 size = njs_is_object_value(value) ? sizeof(njs_object_value_t)
78 : sizeof(njs_object_t);
79 object = njs_mp_alloc(vm->mem_pool, size);
80
81 if (njs_fast_path(object != NULL)) {
82 memcpy(object, njs_object(value), size);
83 object->__proto__ = &vm->prototypes[NJS_OBJ_TYPE_OBJECT].object;
84 object->shared = 0;
85 value->data.u.object = object;
86 return object;
87 }
88
89 njs_memory_error(vm);
90
91 return NULL;
92 }
93
94
95 njs_object_value_t *
njs_object_value_alloc(njs_vm_t * vm,njs_uint_t prototype_index,size_t extra,const njs_value_t * value)96 njs_object_value_alloc(njs_vm_t *vm, njs_uint_t prototype_index, size_t extra,
97 const njs_value_t *value)
98 {
99 njs_object_value_t *ov;
100
101 ov = njs_mp_alloc(vm->mem_pool, sizeof(njs_object_value_t) + extra);
102 if (njs_slow_path(ov == NULL)) {
103 njs_memory_error(vm);
104 return NULL;
105 }
106
107 njs_lvlhsh_init(&ov->object.hash);
108
109 if (prototype_index == NJS_OBJ_TYPE_STRING) {
110 ov->object.shared_hash = vm->shared->string_instance_hash;
111
112 } else {
113 njs_lvlhsh_init(&ov->object.shared_hash);
114 }
115
116 ov->object.type = NJS_OBJECT_VALUE;
117 ov->object.shared = 0;
118 ov->object.extensible = 1;
119 ov->object.error_data = 0;
120 ov->object.fast_array = 0;
121
122 ov->object.__proto__ = &vm->prototypes[prototype_index].object;
123 ov->object.slots = NULL;
124
125 if (value != NULL) {
126 ov->value = *value;
127 }
128
129 return ov;
130 }
131
132
133 njs_int_t
njs_object_hash_create(njs_vm_t * vm,njs_lvlhsh_t * hash,const njs_object_prop_t * prop,njs_uint_t n)134 njs_object_hash_create(njs_vm_t *vm, njs_lvlhsh_t *hash,
135 const njs_object_prop_t *prop, njs_uint_t n)
136 {
137 njs_int_t ret;
138 njs_lvlhsh_query_t lhq;
139
140 lhq.replace = 0;
141 lhq.proto = &njs_object_hash_proto;
142 lhq.pool = vm->mem_pool;
143
144 while (n != 0) {
145
146 njs_object_property_key_set(&lhq, &prop->name, 0);
147
148 lhq.value = (void *) prop;
149
150 ret = njs_lvlhsh_insert(hash, &lhq);
151 if (njs_slow_path(ret != NJS_OK)) {
152 njs_internal_error(vm, "lvlhsh insert failed");
153 return NJS_ERROR;
154 }
155
156 prop++;
157 n--;
158 }
159
160 return NJS_OK;
161 }
162
163
164 const njs_lvlhsh_proto_t njs_object_hash_proto
165 njs_aligned(64) =
166 {
167 NJS_LVLHSH_DEFAULT,
168 njs_object_hash_test,
169 njs_lvlhsh_alloc,
170 njs_lvlhsh_free,
171 };
172
173
174 static njs_int_t
njs_object_hash_test(njs_lvlhsh_query_t * lhq,void * data)175 njs_object_hash_test(njs_lvlhsh_query_t *lhq, void *data)
176 {
177 size_t size;
178 u_char *start;
179 njs_value_t *name;
180 njs_object_prop_t *prop;
181
182 prop = data;
183 name = &prop->name;
184
185 if (njs_slow_path(njs_is_symbol(name))) {
186 return ((njs_symbol_key(name) == lhq->key_hash)
187 && lhq->key.start == NULL) ? NJS_OK : NJS_DECLINED;
188 }
189
190 /* string. */
191
192 size = name->short_string.size;
193
194 if (size != NJS_STRING_LONG) {
195 if (lhq->key.length != size) {
196 return NJS_DECLINED;
197 }
198
199 start = name->short_string.start;
200
201 } else {
202 if (lhq->key.length != name->long_string.size) {
203 return NJS_DECLINED;
204 }
205
206 start = name->long_string.data->start;
207 }
208
209 if (memcmp(start, lhq->key.start, lhq->key.length) == 0) {
210 return NJS_OK;
211 }
212
213 return NJS_DECLINED;
214 }
215
216
217 static njs_int_t
njs_object_constructor(njs_vm_t * vm,njs_value_t * args,njs_uint_t nargs,njs_index_t unused)218 njs_object_constructor(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
219 njs_index_t unused)
220 {
221 njs_uint_t type, index;
222 njs_value_t *value;
223 njs_object_t *object;
224 njs_object_value_t *obj_val;
225
226 value = njs_arg(args, nargs, 1);
227 type = value->type;
228
229 if (njs_is_null_or_undefined(value)) {
230 object = njs_object_alloc(vm);
231 if (njs_slow_path(object == NULL)) {
232 return NJS_ERROR;
233 }
234
235 njs_set_object(&vm->retval, object);
236
237 return NJS_OK;
238 }
239
240 if (njs_is_primitive(value)) {
241 index = njs_primitive_prototype_index(type);
242 obj_val = njs_object_value_alloc(vm, index, 0, value);
243 if (njs_slow_path(obj_val == NULL)) {
244 return NJS_ERROR;
245 }
246
247 njs_set_object_value(&vm->retval, obj_val);
248
249 return NJS_OK;
250 }
251
252 if (njs_slow_path(!njs_is_object(value))) {
253 njs_type_error(vm, "unexpected constructor argument:%s",
254 njs_type_string(type));
255
256 return NJS_ERROR;
257 }
258
259 njs_value_assign(&vm->retval, value);
260
261 return NJS_OK;
262 }
263
264
265 static njs_int_t
njs_object_create(njs_vm_t * vm,njs_value_t * args,njs_uint_t nargs,njs_index_t unused)266 njs_object_create(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
267 njs_index_t unused)
268 {
269 njs_value_t *value, *descs, arguments[3];
270 njs_object_t *object;
271
272 value = njs_arg(args, nargs, 1);
273
274 if (njs_is_object(value) || njs_is_null(value)) {
275
276 object = njs_object_alloc(vm);
277 if (njs_slow_path(object == NULL)) {
278 return NJS_ERROR;
279 }
280
281 if (!njs_is_null(value)) {
282 /* GC */
283 object->__proto__ = njs_object(value);
284
285 } else {
286 object->__proto__ = NULL;
287 }
288
289 njs_set_object(&vm->retval, object);
290
291 descs = njs_arg(args, nargs, 2);
292
293 if (njs_slow_path(!njs_is_undefined(descs))) {
294 arguments[0] = args[0];
295 arguments[1] = vm->retval;
296 arguments[2] = *descs;
297
298 return njs_object_define_properties(vm, arguments, 3, unused);
299 }
300
301 return NJS_OK;
302 }
303
304 njs_type_error(vm, "prototype may only be an object or null: %s",
305 njs_type_string(value->type));
306
307 return NJS_ERROR;
308 }
309
310
311 static njs_int_t
njs_object_keys(njs_vm_t * vm,njs_value_t * args,njs_uint_t nargs,njs_index_t unused)312 njs_object_keys(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
313 njs_index_t unused)
314 {
315 njs_value_t *value;
316 njs_array_t *keys;
317
318 value = njs_arg(args, nargs, 1);
319
320 if (njs_is_null_or_undefined(value)) {
321 njs_type_error(vm, "cannot convert %s argument to object",
322 njs_type_string(value->type));
323
324 return NJS_ERROR;
325 }
326
327 keys = njs_value_own_enumerate(vm, value, NJS_ENUM_KEYS,
328 NJS_ENUM_STRING, 0);
329 if (keys == NULL) {
330 return NJS_ERROR;
331 }
332
333 njs_set_array(&vm->retval, keys);
334
335 return NJS_OK;
336 }
337
338
339 static njs_int_t
njs_object_values(njs_vm_t * vm,njs_value_t * args,njs_uint_t nargs,njs_index_t unused)340 njs_object_values(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
341 njs_index_t unused)
342 {
343 njs_array_t *array;
344 njs_value_t *value;
345
346 value = njs_arg(args, nargs, 1);
347
348 if (njs_is_null_or_undefined(value)) {
349 njs_type_error(vm, "cannot convert %s argument to object",
350 njs_type_string(value->type));
351
352 return NJS_ERROR;
353 }
354
355 array = njs_value_own_enumerate(vm, value, NJS_ENUM_VALUES,
356 NJS_ENUM_STRING, 0);
357 if (array == NULL) {
358 return NJS_ERROR;
359 }
360
361 njs_set_array(&vm->retval, array);
362
363 return NJS_OK;
364 }
365
366
367 static njs_int_t
njs_object_entries(njs_vm_t * vm,njs_value_t * args,njs_uint_t nargs,njs_index_t unused)368 njs_object_entries(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
369 njs_index_t unused)
370 {
371 njs_array_t *array;
372 njs_value_t *value;
373
374 value = njs_arg(args, nargs, 1);
375
376 if (njs_is_null_or_undefined(value)) {
377 njs_type_error(vm, "cannot convert %s argument to object",
378 njs_type_string(value->type));
379
380 return NJS_ERROR;
381 }
382
383 array = njs_value_own_enumerate(vm, value, NJS_ENUM_BOTH,
384 NJS_ENUM_STRING, 0);
385 if (array == NULL) {
386 return NJS_ERROR;
387 }
388
389 njs_set_array(&vm->retval, array);
390
391 return NJS_OK;
392 }
393
394
395 static njs_object_prop_t *
njs_object_exist_in_proto(const njs_object_t * object,const njs_object_t * end,njs_lvlhsh_query_t * lhq)396 njs_object_exist_in_proto(const njs_object_t *object, const njs_object_t *end,
397 njs_lvlhsh_query_t *lhq)
398 {
399 njs_int_t ret;
400 njs_object_prop_t *prop;
401
402 while (object != end) {
403 ret = njs_lvlhsh_find(&object->hash, lhq);
404
405 if (njs_fast_path(ret == NJS_OK)) {
406 prop = lhq->value;
407
408 if (prop->type == NJS_WHITEOUT) {
409 goto next;
410 }
411
412 return lhq->value;
413 }
414
415 ret = njs_lvlhsh_find(&object->shared_hash, lhq);
416
417 if (njs_fast_path(ret == NJS_OK)) {
418 return lhq->value;
419 }
420
421 next:
422
423 object = object->__proto__;
424 }
425
426 return NULL;
427 }
428
429
430 njs_inline njs_int_t
njs_object_enumerate_value(njs_vm_t * vm,const njs_object_t * object,njs_array_t * items,njs_object_enum_t kind,njs_object_enum_type_t type,njs_bool_t all)431 njs_object_enumerate_value(njs_vm_t *vm, const njs_object_t *object,
432 njs_array_t *items, njs_object_enum_t kind, njs_object_enum_type_t type,
433 njs_bool_t all)
434 {
435 njs_int_t ret;
436 njs_object_value_t *obj_val;
437
438 if (type & NJS_ENUM_STRING) {
439 switch (object->type) {
440 case NJS_ARRAY:
441 ret = njs_object_enumerate_array(vm, (njs_array_t *) object, items,
442 kind);
443 break;
444
445 case NJS_TYPED_ARRAY:
446 ret = njs_object_enumerate_typed_array(vm,
447 (njs_typed_array_t *) object,
448 items, kind);
449 break;
450
451 case NJS_OBJECT_VALUE:
452 obj_val = (njs_object_value_t *) object;
453
454 if (njs_is_string(&obj_val->value)) {
455 ret = njs_object_enumerate_string(vm, &obj_val->value, items,
456 kind);
457 break;
458 }
459
460 /* Fall through. */
461
462 default:
463 goto object;
464 }
465
466 if (njs_slow_path(ret != NJS_OK)) {
467 return NJS_ERROR;
468 }
469 }
470
471 object:
472
473 ret = njs_object_enumerate_object(vm, object, items, kind, type, all);
474 if (njs_slow_path(ret != NJS_OK)) {
475 return NJS_ERROR;
476 }
477
478 return NJS_OK;
479 }
480
481
482 njs_inline njs_int_t
njs_object_own_enumerate_value(njs_vm_t * vm,const njs_object_t * object,const njs_object_t * parent,njs_array_t * items,njs_object_enum_t kind,njs_object_enum_type_t type,njs_bool_t all)483 njs_object_own_enumerate_value(njs_vm_t *vm, const njs_object_t *object,
484 const njs_object_t *parent, njs_array_t *items, njs_object_enum_t kind,
485 njs_object_enum_type_t type, njs_bool_t all)
486 {
487 njs_int_t ret;
488 njs_object_value_t *obj_val;
489
490 if (type & NJS_ENUM_STRING) {
491 switch (object->type) {
492 case NJS_ARRAY:
493 ret = njs_object_enumerate_array(vm, (njs_array_t *) object, items,
494 kind);
495 break;
496
497 case NJS_TYPED_ARRAY:
498 ret = njs_object_enumerate_typed_array(vm,
499 (njs_typed_array_t *) object,
500 items, kind);
501 break;
502
503 case NJS_OBJECT_VALUE:
504 obj_val = (njs_object_value_t *) object;
505
506 if (njs_is_string(&obj_val->value)) {
507 ret = njs_object_enumerate_string(vm, &obj_val->value, items,
508 kind);
509 break;
510 }
511
512 /* Fall through. */
513
514 default:
515 goto object;
516 }
517
518 if (njs_slow_path(ret != NJS_OK)) {
519 return NJS_ERROR;
520 }
521 }
522
523 object:
524
525 ret = njs_object_own_enumerate_object(vm, object, parent, items, kind,
526 type, all);
527 if (njs_slow_path(ret != NJS_OK)) {
528 return NJS_ERROR;
529 }
530
531 return NJS_OK;
532 }
533
534
535 njs_array_t *
njs_object_enumerate(njs_vm_t * vm,const njs_object_t * object,njs_object_enum_t kind,njs_object_enum_type_t type,njs_bool_t all)536 njs_object_enumerate(njs_vm_t *vm, const njs_object_t *object,
537 njs_object_enum_t kind, njs_object_enum_type_t type, njs_bool_t all)
538 {
539 njs_int_t ret;
540 njs_array_t *items;
541
542 items = njs_array_alloc(vm, 1, 0, NJS_ARRAY_SPARE);
543 if (njs_slow_path(items == NULL)) {
544 return NULL;
545 }
546
547 ret = njs_object_enumerate_value(vm, object, items, kind, type, all);
548 if (njs_slow_path(ret != NJS_OK)) {
549 return NULL;
550 }
551
552 return items;
553 }
554
555
556 njs_array_t *
njs_object_own_enumerate(njs_vm_t * vm,const njs_object_t * object,njs_object_enum_t kind,njs_object_enum_type_t type,njs_bool_t all)557 njs_object_own_enumerate(njs_vm_t *vm, const njs_object_t *object,
558 njs_object_enum_t kind, njs_object_enum_type_t type, njs_bool_t all)
559 {
560 njs_int_t ret;
561 njs_array_t *items;
562
563 items = njs_array_alloc(vm, 1, 0, NJS_ARRAY_SPARE);
564 if (njs_slow_path(items == NULL)) {
565 return NULL;
566 }
567
568 ret = njs_object_own_enumerate_value(vm, object, object, items, kind, type,
569 all);
570 if (njs_slow_path(ret != NJS_OK)) {
571 return NULL;
572 }
573
574 return items;
575 }
576
577
578 njs_inline njs_bool_t
njs_is_enumerable(const njs_value_t * value,njs_object_enum_type_t type)579 njs_is_enumerable(const njs_value_t *value, njs_object_enum_type_t type)
580 {
581 return (njs_is_string(value) && (type & NJS_ENUM_STRING))
582 || (njs_is_symbol(value) && (type & NJS_ENUM_SYMBOL));
583 }
584
585
586 static njs_int_t
njs_object_enumerate_array(njs_vm_t * vm,const njs_array_t * array,njs_array_t * items,njs_object_enum_t kind)587 njs_object_enumerate_array(njs_vm_t *vm, const njs_array_t *array,
588 njs_array_t *items, njs_object_enum_t kind)
589 {
590 njs_int_t ret;
591 njs_value_t *p, *start, *end;
592 njs_array_t *entry;
593
594 if (!array->object.fast_array) {
595 return NJS_OK;
596 }
597
598 start = array->start;
599
600 p = start;
601 end = p + array->length;
602
603 switch (kind) {
604 case NJS_ENUM_KEYS:
605 while (p < end) {
606 if (njs_is_valid(p)) {
607 ret = njs_array_expand(vm, items, 0, 1);
608 if (njs_slow_path(ret != NJS_OK)) {
609 return NJS_ERROR;
610 }
611
612 njs_uint32_to_string(&items->start[items->length++], p - start);
613 }
614
615 p++;
616 }
617
618 break;
619
620 case NJS_ENUM_VALUES:
621 while (p < end) {
622 if (njs_is_valid(p)) {
623 ret = njs_array_add(vm, items, p);
624 if (njs_slow_path(ret != NJS_OK)) {
625 return NJS_ERROR;
626 }
627 }
628
629 p++;
630 }
631
632 break;
633
634 case NJS_ENUM_BOTH:
635 while (p < end) {
636 if (njs_is_valid(p)) {
637 entry = njs_array_alloc(vm, 0, 2, 0);
638 if (njs_slow_path(entry == NULL)) {
639 return NJS_ERROR;
640 }
641
642 njs_uint32_to_string(&entry->start[0], p - start);
643 entry->start[1] = *p;
644
645 ret = njs_array_expand(vm, items, 0, 1);
646 if (njs_slow_path(ret != NJS_OK)) {
647 return NJS_ERROR;
648 }
649
650 njs_set_array(&items->start[items->length++], entry);
651 }
652
653 p++;
654 }
655
656 break;
657 }
658
659 return NJS_OK;
660 }
661
662
663 static njs_int_t
njs_object_enumerate_typed_array(njs_vm_t * vm,const njs_typed_array_t * array,njs_array_t * items,njs_object_enum_t kind)664 njs_object_enumerate_typed_array(njs_vm_t *vm, const njs_typed_array_t *array,
665 njs_array_t *items, njs_object_enum_t kind)
666 {
667 uint32_t i, length;
668 njs_int_t ret;
669 njs_value_t *item;
670 njs_array_t *entry;
671
672 length = njs_typed_array_length(array);
673
674 ret = njs_array_expand(vm, items, 0, length);
675 if (njs_slow_path(ret != NJS_OK)) {
676 return NJS_ERROR;
677 }
678
679 item = &items->start[items->length];
680
681 switch (kind) {
682 case NJS_ENUM_KEYS:
683 for (i = 0; i < length; i++) {
684 njs_uint32_to_string(item++, i);
685 }
686
687 break;
688
689 case NJS_ENUM_VALUES:
690 for (i = 0; i < length; i++) {
691 njs_set_number(item++, njs_typed_array_prop(array, i));
692 }
693
694 break;
695
696 case NJS_ENUM_BOTH:
697 for (i = 0; i < length; i++) {
698 entry = njs_array_alloc(vm, 0, 2, 0);
699 if (njs_slow_path(entry == NULL)) {
700 return NJS_ERROR;
701 }
702
703 njs_uint32_to_string(&entry->start[0], i);
704 njs_set_number(&entry->start[1], njs_typed_array_prop(array, i));
705
706 njs_set_array(item++, entry);
707 }
708
709 break;
710 }
711
712 items->length += length;
713
714 return NJS_OK;
715 }
716
717
718 static njs_int_t
njs_object_enumerate_string(njs_vm_t * vm,const njs_value_t * value,njs_array_t * items,njs_object_enum_t kind)719 njs_object_enumerate_string(njs_vm_t *vm, const njs_value_t *value,
720 njs_array_t *items, njs_object_enum_t kind)
721 {
722 u_char *begin;
723 uint32_t i, len, size;
724 njs_int_t ret;
725 njs_value_t *item, *string;
726 njs_array_t *entry;
727 const u_char *src, *end;
728 njs_string_prop_t str_prop;
729
730 len = (uint32_t) njs_string_prop(&str_prop, value);
731
732 ret = njs_array_expand(vm, items, 0, len);
733 if (njs_slow_path(ret != NJS_OK)) {
734 return NJS_ERROR;
735 }
736
737 item = &items->start[items->length];
738
739 switch (kind) {
740 case NJS_ENUM_KEYS:
741 for (i = 0; i < len; i++) {
742 njs_uint32_to_string(item++, i);
743 }
744
745 break;
746
747 case NJS_ENUM_VALUES:
748 if (str_prop.size == (size_t) len) {
749 /* Byte or ASCII string. */
750
751 for (i = 0; i < len; i++) {
752 begin = njs_string_short_start(item);
753 *begin = str_prop.start[i];
754
755 njs_string_short_set(item, 1, 1);
756
757 item++;
758 }
759
760 } else {
761 /* UTF-8 string. */
762
763 src = str_prop.start;
764 end = src + str_prop.size;
765
766 do {
767 begin = (u_char *) src;
768 njs_utf8_copy(njs_string_short_start(item), &src, end);
769 size = (uint32_t) (src - begin);
770
771 njs_string_short_set(item, size, 1);
772
773 item++;
774
775 } while (src != end);
776 }
777
778 break;
779
780 case NJS_ENUM_BOTH:
781 if (str_prop.size == (size_t) len) {
782 /* Byte or ASCII string. */
783
784 for (i = 0; i < len; i++) {
785
786 entry = njs_array_alloc(vm, 0, 2, 0);
787 if (njs_slow_path(entry == NULL)) {
788 return NJS_ERROR;
789 }
790
791 njs_uint32_to_string(&entry->start[0], i);
792
793 string = &entry->start[1];
794
795 begin = njs_string_short_start(string);
796 *begin = str_prop.start[i];
797
798 njs_string_short_set(string, 1, 1);
799
800 njs_set_array(item, entry);
801
802 item++;
803 }
804
805 } else {
806 /* UTF-8 string. */
807
808 src = str_prop.start;
809 end = src + str_prop.size;
810 i = 0;
811
812 do {
813 entry = njs_array_alloc(vm, 0, 2, 0);
814 if (njs_slow_path(entry == NULL)) {
815 return NJS_ERROR;
816 }
817
818 njs_uint32_to_string(&entry->start[0], i++);
819
820 string = &entry->start[1];
821
822 begin = (u_char *) src;
823 njs_utf8_copy(njs_string_short_start(string), &src, end);
824 size = (uint32_t) (src - begin);
825
826 njs_string_short_set(string, size, 1);
827
828 njs_set_array(item, entry);
829
830 item++;
831
832 } while (src != end);
833 }
834
835 break;
836 }
837
838 items->length += len;
839
840 return NJS_OK;
841 }
842
843
844 static njs_int_t
njs_object_enumerate_object(njs_vm_t * vm,const njs_object_t * object,njs_array_t * items,njs_object_enum_t kind,njs_object_enum_type_t type,njs_bool_t all)845 njs_object_enumerate_object(njs_vm_t *vm, const njs_object_t *object,
846 njs_array_t *items, njs_object_enum_t kind, njs_object_enum_type_t type,
847 njs_bool_t all)
848 {
849 njs_int_t ret;
850 const njs_object_t *proto;
851
852 ret = njs_object_own_enumerate_object(vm, object, object, items, kind,
853 type, all);
854 if (njs_slow_path(ret != NJS_OK)) {
855 return NJS_ERROR;
856 }
857
858 proto = object->__proto__;
859
860 while (proto != NULL) {
861 ret = njs_object_own_enumerate_value(vm, proto, object, items, kind,
862 type, all);
863 if (njs_slow_path(ret != NJS_OK)) {
864 return NJS_ERROR;
865 }
866
867 proto = proto->__proto__;
868 }
869
870 return NJS_OK;
871 }
872
873
874 static njs_int_t
njs_object_own_enumerate_object(njs_vm_t * vm,const njs_object_t * object,const njs_object_t * parent,njs_array_t * items,njs_object_enum_t kind,njs_object_enum_type_t type,njs_bool_t all)875 njs_object_own_enumerate_object(njs_vm_t *vm, const njs_object_t *object,
876 const njs_object_t *parent, njs_array_t *items, njs_object_enum_t kind,
877 njs_object_enum_type_t type, njs_bool_t all)
878 {
879 njs_int_t ret;
880 njs_value_t value;
881 njs_array_t *entry;
882 njs_lvlhsh_each_t lhe;
883 njs_object_prop_t *prop, *ext_prop;
884 njs_lvlhsh_query_t lhq;
885 const njs_lvlhsh_t *hash;
886
887 lhq.proto = &njs_object_hash_proto;
888
889 njs_lvlhsh_each_init(&lhe, &njs_object_hash_proto);
890 hash = &object->hash;
891
892 switch (kind) {
893 case NJS_ENUM_KEYS:
894 for ( ;; ) {
895 prop = njs_lvlhsh_each(hash, &lhe);
896
897 if (prop == NULL) {
898 break;
899 }
900
901 if (!njs_is_enumerable(&prop->name, type)) {
902 continue;
903 }
904
905 njs_object_property_key_set(&lhq, &prop->name, lhe.key_hash);
906
907 ext_prop = njs_object_exist_in_proto(parent, object, &lhq);
908
909 if (ext_prop == NULL && prop->type != NJS_WHITEOUT
910 && (prop->enumerable || all))
911 {
912 ret = njs_array_add(vm, items, &prop->name);
913 if (njs_slow_path(ret != NJS_OK)) {
914 return NJS_ERROR;
915 }
916 }
917 }
918
919 njs_lvlhsh_each_init(&lhe, &njs_object_hash_proto);
920 hash = &object->shared_hash;
921
922 for ( ;; ) {
923 prop = njs_lvlhsh_each(hash, &lhe);
924
925 if (prop == NULL) {
926 break;
927 }
928
929 if (!njs_is_enumerable(&prop->name, type)) {
930 continue;
931 }
932
933 njs_object_property_key_set(&lhq, &prop->name, lhe.key_hash);
934
935 ret = njs_lvlhsh_find(&object->hash, &lhq);
936
937 if (ret != NJS_OK) {
938 ext_prop = njs_object_exist_in_proto(parent, object, &lhq);
939
940 if (ext_prop == NULL && (prop->enumerable || all)) {
941 ret = njs_array_add(vm, items, &prop->name);
942 if (njs_slow_path(ret != NJS_OK)) {
943 return NJS_ERROR;
944 }
945 }
946 }
947 }
948
949 break;
950
951 case NJS_ENUM_VALUES:
952 for ( ;; ) {
953 prop = njs_lvlhsh_each(hash, &lhe);
954
955 if (prop == NULL) {
956 break;
957 }
958
959 if (!njs_is_enumerable(&prop->name, type)) {
960 continue;
961 }
962
963 njs_object_property_key_set(&lhq, &prop->name, lhe.key_hash);
964
965 ext_prop = njs_object_exist_in_proto(parent, object, &lhq);
966
967 if (ext_prop == NULL && prop->type != NJS_WHITEOUT
968 && (prop->enumerable || all))
969 {
970 ret = njs_array_add(vm, items, &prop->value);
971 if (njs_slow_path(ret != NJS_OK)) {
972 return NJS_ERROR;
973 }
974 }
975 }
976
977 njs_lvlhsh_each_init(&lhe, &njs_object_hash_proto);
978 hash = &object->shared_hash;
979
980 for ( ;; ) {
981 prop = njs_lvlhsh_each(hash, &lhe);
982
983 if (prop == NULL) {
984 break;
985 }
986
987 if (!njs_is_enumerable(&prop->name, type)) {
988 continue;
989 }
990
991 njs_object_property_key_set(&lhq, &prop->name, lhe.key_hash);
992
993 ret = njs_lvlhsh_find(&object->hash, &lhq);
994
995 if (ret != NJS_OK) {
996 ext_prop = njs_object_exist_in_proto(parent, object, &lhq);
997
998 if (ext_prop == NULL && (prop->enumerable || all)) {
999 ret = njs_array_add(vm, items, &prop->value);
1000 if (njs_slow_path(ret != NJS_OK)) {
1001 return NJS_ERROR;
1002 }
1003 }
1004 }
1005 }
1006
1007 break;
1008
1009 case NJS_ENUM_BOTH:
1010 for ( ;; ) {
1011 prop = njs_lvlhsh_each(hash, &lhe);
1012
1013 if (prop == NULL) {
1014 break;
1015 }
1016
1017 if (!njs_is_enumerable(&prop->name, type)) {
1018 continue;
1019 }
1020
1021 njs_object_property_key_set(&lhq, &prop->name, lhe.key_hash);
1022
1023 ext_prop = njs_object_exist_in_proto(parent, object, &lhq);
1024
1025 if (ext_prop == NULL && prop->type != NJS_WHITEOUT
1026 && (prop->enumerable || all))
1027 {
1028 entry = njs_array_alloc(vm, 0, 2, 0);
1029 if (njs_slow_path(entry == NULL)) {
1030 return NJS_ERROR;
1031 }
1032
1033 njs_string_copy(&entry->start[0], &prop->name);
1034 entry->start[1] = prop->value;
1035
1036 njs_set_array(&value, entry);
1037
1038 ret = njs_array_add(vm, items, &value);
1039 if (njs_slow_path(ret != NJS_OK)) {
1040 return NJS_ERROR;
1041 }
1042 }
1043 }
1044
1045 njs_lvlhsh_each_init(&lhe, &njs_object_hash_proto);
1046 hash = &object->shared_hash;
1047
1048 for ( ;; ) {
1049 prop = njs_lvlhsh_each(hash, &lhe);
1050
1051 if (prop == NULL) {
1052 break;
1053 }
1054
1055 if (!njs_is_enumerable(&prop->name, type)) {
1056 continue;
1057 }
1058
1059 njs_object_property_key_set(&lhq, &prop->name, lhe.key_hash);
1060
1061 ret = njs_lvlhsh_find(&object->hash, &lhq);
1062
1063 if (ret != NJS_OK && (prop->enumerable || all)) {
1064 ext_prop = njs_object_exist_in_proto(parent, object, &lhq);
1065
1066 if (ext_prop == NULL) {
1067 entry = njs_array_alloc(vm, 0, 2, 0);
1068 if (njs_slow_path(entry == NULL)) {
1069 return NJS_ERROR;
1070 }
1071
1072 njs_string_copy(&entry->start[0], &prop->name);
1073 entry->start[1] = prop->value;
1074
1075 njs_set_array(&value, entry);
1076
1077 ret = njs_array_add(vm, items, &value);
1078 if (njs_slow_path(ret != NJS_OK)) {
1079 return NJS_ERROR;
1080 }
1081 }
1082 }
1083 }
1084
1085 break;
1086 }
1087
1088 return NJS_OK;
1089 }
1090
1091
1092 njs_inline njs_int_t
njs_traverse_visit(njs_arr_t * list,const njs_value_t * value)1093 njs_traverse_visit(njs_arr_t *list, const njs_value_t *value)
1094 {
1095 njs_object_t **p;
1096
1097 if (njs_is_object(value)) {
1098 p = njs_arr_add(list);
1099 if (njs_slow_path(p == NULL)) {
1100 return NJS_ERROR;
1101 }
1102
1103 *p = njs_object(value);
1104 }
1105
1106 return NJS_OK;
1107 }
1108
1109
1110 njs_inline njs_int_t
njs_traverse_visited(njs_arr_t * list,const njs_value_t * value)1111 njs_traverse_visited(njs_arr_t *list, const njs_value_t *value)
1112 {
1113 njs_uint_t items, n;
1114 njs_object_t **start, *obj;
1115
1116 if (!njs_is_object(value)) {
1117 /* External. */
1118 return 0;
1119 }
1120
1121 start = list->start;
1122 items = list->items;
1123 obj = njs_object(value);
1124
1125 for (n = 0; n < items; n++) {
1126 if (start[n] == obj) {
1127 return 1;
1128 }
1129 }
1130
1131 return 0;
1132 }
1133
1134
1135 njs_int_t
njs_object_traverse(njs_vm_t * vm,njs_object_t * object,void * ctx,njs_object_traverse_cb_t cb)1136 njs_object_traverse(njs_vm_t *vm, njs_object_t *object, void *ctx,
1137 njs_object_traverse_cb_t cb)
1138 {
1139 njs_int_t ret;
1140 njs_arr_t visited;
1141 njs_object_t **start;
1142 njs_value_t value, *key;
1143 njs_traverse_t *s;
1144 njs_object_prop_t *prop;
1145 njs_property_query_t pq;
1146 njs_traverse_t state[NJS_TRAVERSE_MAX_DEPTH];
1147
1148 s = &state[0];
1149 s->prop = NULL;
1150 s->parent = NULL;
1151 s->index = 0;
1152 njs_set_object(&s->value, object);
1153 s->keys = njs_value_own_enumerate(vm, &s->value, NJS_ENUM_KEYS,
1154 NJS_ENUM_STRING | NJS_ENUM_SYMBOL, 1);
1155 if (njs_slow_path(s->keys == NULL)) {
1156 return NJS_ERROR;
1157 }
1158
1159 start = njs_arr_init(vm->mem_pool, &visited, NULL, 8, sizeof(void *));
1160 if (njs_slow_path(start == NULL)) {
1161 return NJS_ERROR;
1162 }
1163
1164 (void) njs_traverse_visit(&visited, &s->value);
1165
1166 for ( ;; ) {
1167
1168 if (s->index >= s->keys->length) {
1169 njs_array_destroy(vm, s->keys);
1170 s->keys = NULL;
1171
1172 if (s == &state[0]) {
1173 goto done;
1174 }
1175
1176 s--;
1177 continue;
1178 }
1179
1180 njs_property_query_init(&pq, NJS_PROPERTY_QUERY_GET, 0);
1181 key = &s->keys->start[s->index++];
1182
1183 ret = njs_property_query(vm, &pq, &s->value, key);
1184 if (njs_slow_path(ret != NJS_OK)) {
1185 if (ret == NJS_DECLINED) {
1186 continue;
1187 }
1188
1189 return NJS_ERROR;
1190 }
1191
1192 prop = pq.lhq.value;
1193 s->prop = prop;
1194
1195 ret = cb(vm, s, ctx);
1196 if (njs_slow_path(ret != NJS_OK)) {
1197 return ret;
1198 }
1199
1200 njs_value_assign(&value, &prop->value);
1201
1202 if (prop->type == NJS_PROPERTY_HANDLER) {
1203 ret = prop->value.data.u.prop_handler(vm, prop, &s->value, NULL,
1204 &value);
1205 if (njs_slow_path(ret == NJS_ERROR)) {
1206 return ret;
1207
1208 }
1209 }
1210
1211 if (njs_is_object(&value)
1212 && !njs_traverse_visited(&visited, &value))
1213 {
1214 ret = njs_traverse_visit(&visited, &value);
1215 if (njs_slow_path(ret != NJS_OK)) {
1216 return NJS_ERROR;
1217 }
1218
1219 if (s == &state[NJS_TRAVERSE_MAX_DEPTH - 1]) {
1220 njs_type_error(vm, "njs_object_traverse() recursion limit:%d",
1221 NJS_TRAVERSE_MAX_DEPTH);
1222 return NJS_ERROR;
1223 }
1224
1225 s++;
1226 s->prop = NULL;
1227 s->parent = &s[-1];
1228 s->index = 0;
1229 njs_value_assign(&s->value, &value);
1230 s->keys = njs_value_own_enumerate(vm, &s->value, NJS_ENUM_KEYS,
1231 NJS_ENUM_STRING | NJS_ENUM_SYMBOL, 1);
1232 if (njs_slow_path(s->keys == NULL)) {
1233 return NJS_ERROR;
1234 }
1235 }
1236 }
1237
1238 done:
1239
1240 njs_arr_destroy(&visited);
1241
1242 return NJS_OK;
1243 }
1244
1245
1246 static njs_int_t
njs_object_define_property(njs_vm_t * vm,njs_value_t * args,njs_uint_t nargs,njs_index_t unused)1247 njs_object_define_property(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
1248 njs_index_t unused)
1249 {
1250 njs_int_t ret;
1251 njs_value_t *value, *name, *desc, lvalue;
1252
1253 if (!njs_is_object(njs_arg(args, nargs, 1))) {
1254 njs_type_error(vm, "Object.defineProperty is called on non-object");
1255 return NJS_ERROR;
1256 }
1257
1258 desc = njs_arg(args, nargs, 3);
1259
1260 if (!njs_is_object(desc)) {
1261 njs_type_error(vm, "descriptor is not an object");
1262 return NJS_ERROR;
1263 }
1264
1265 value = njs_argument(args, 1);
1266 name = njs_lvalue_arg(&lvalue, args, nargs, 2);
1267
1268 ret = njs_object_prop_define(vm, value, name, desc,
1269 NJS_OBJECT_PROP_DESCRIPTOR);
1270 if (njs_slow_path(ret != NJS_OK)) {
1271 return NJS_ERROR;
1272 }
1273
1274 vm->retval = *value;
1275
1276 return NJS_OK;
1277 }
1278
1279
1280 static njs_int_t
njs_object_define_properties(njs_vm_t * vm,njs_value_t * args,njs_uint_t nargs,njs_index_t unused)1281 njs_object_define_properties(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
1282 njs_index_t unused)
1283 {
1284 uint32_t i, length;
1285 njs_int_t ret;
1286 njs_array_t *keys;
1287 njs_value_t desc, *value, *descs;
1288 njs_object_prop_t *prop;
1289 njs_property_query_t pq;
1290
1291 if (!njs_is_object(njs_arg(args, nargs, 1))) {
1292 njs_type_error(vm, "Object.defineProperties is called on non-object");
1293 return NJS_ERROR;
1294 }
1295
1296 descs = njs_arg(args, nargs, 2);
1297 ret = njs_value_to_object(vm, descs);
1298 if (njs_slow_path(ret != NJS_OK)) {
1299 return ret;
1300 }
1301
1302 keys = njs_value_own_enumerate(vm, descs, NJS_ENUM_KEYS,
1303 NJS_ENUM_STRING | NJS_ENUM_SYMBOL, 0);
1304 if (njs_slow_path(keys == NULL)) {
1305 return NJS_ERROR;
1306 }
1307
1308 length = keys->length;
1309 value = njs_argument(args, 1);
1310 njs_property_query_init(&pq, NJS_PROPERTY_QUERY_GET, 0);
1311
1312 for (i = 0; i < length; i++) {
1313 ret = njs_property_query(vm, &pq, descs, &keys->start[i]);
1314 if (njs_slow_path(ret == NJS_ERROR)) {
1315 goto done;
1316 }
1317
1318 prop = pq.lhq.value;
1319
1320 if (ret == NJS_DECLINED || !prop->enumerable) {
1321 continue;
1322 }
1323
1324 ret = njs_value_property(vm, descs, &keys->start[i], &desc);
1325 if (njs_slow_path(ret == NJS_ERROR)) {
1326 goto done;
1327 }
1328
1329 ret = njs_object_prop_define(vm, value, &keys->start[i], &desc,
1330 NJS_OBJECT_PROP_DESCRIPTOR);
1331 if (njs_slow_path(ret != NJS_OK)) {
1332 goto done;
1333 }
1334 }
1335
1336 ret = NJS_OK;
1337 vm->retval = *value;
1338
1339 done:
1340
1341 njs_array_destroy(vm, keys);
1342
1343 return ret;
1344 }
1345
1346
1347 static njs_int_t
njs_object_get_own_property_descriptor(njs_vm_t * vm,njs_value_t * args,njs_uint_t nargs,njs_index_t unused)1348 njs_object_get_own_property_descriptor(njs_vm_t *vm, njs_value_t *args,
1349 njs_uint_t nargs, njs_index_t unused)
1350 {
1351 njs_value_t lvalue, *value, *property;
1352
1353 value = njs_arg(args, nargs, 1);
1354
1355 if (njs_is_null_or_undefined(value)) {
1356 njs_type_error(vm, "cannot convert %s argument to object",
1357 njs_type_string(value->type));
1358 return NJS_ERROR;
1359 }
1360
1361 property = njs_lvalue_arg(&lvalue, args, nargs, 2);
1362
1363 return njs_object_prop_descriptor(vm, &vm->retval, value, property);
1364 }
1365
1366
1367 static njs_int_t
njs_object_get_own_property_descriptors(njs_vm_t * vm,njs_value_t * args,njs_uint_t nargs,njs_index_t unused)1368 njs_object_get_own_property_descriptors(njs_vm_t *vm, njs_value_t *args,
1369 njs_uint_t nargs, njs_index_t unused)
1370 {
1371 njs_int_t ret;
1372 uint32_t i, length;
1373 njs_array_t *names;
1374 njs_value_t descriptor, *value, *key;
1375 njs_object_t *descriptors;
1376 njs_object_prop_t *pr;
1377 njs_lvlhsh_query_t lhq;
1378
1379 value = njs_arg(args, nargs, 1);
1380
1381 if (njs_is_null_or_undefined(value)) {
1382 njs_type_error(vm, "cannot convert %s argument to object",
1383 njs_type_string(value->type));
1384
1385 return NJS_ERROR;
1386 }
1387
1388 names = njs_value_own_enumerate(vm, value, NJS_ENUM_KEYS,
1389 NJS_ENUM_STRING | NJS_ENUM_SYMBOL, 1);
1390 if (njs_slow_path(names == NULL)) {
1391 return NJS_ERROR;
1392 }
1393
1394 length = names->length;
1395
1396 descriptors = njs_object_alloc(vm);
1397 if (njs_slow_path(descriptors == NULL)) {
1398 ret = NJS_ERROR;
1399 goto done;
1400 }
1401
1402 lhq.replace = 0;
1403 lhq.pool = vm->mem_pool;
1404 lhq.proto = &njs_object_hash_proto;
1405
1406 for (i = 0; i < length; i++) {
1407 key = &names->start[i];
1408 ret = njs_object_prop_descriptor(vm, &descriptor, value, key);
1409 if (njs_slow_path(ret != NJS_OK)) {
1410 ret = NJS_ERROR;
1411 goto done;
1412 }
1413
1414 pr = njs_object_prop_alloc(vm, key, &descriptor, 1);
1415 if (njs_slow_path(pr == NULL)) {
1416 ret = NJS_ERROR;
1417 goto done;
1418 }
1419
1420 njs_object_property_key_set(&lhq, key, 0);
1421 lhq.value = pr;
1422
1423 ret = njs_lvlhsh_insert(&descriptors->hash, &lhq);
1424 if (njs_slow_path(ret != NJS_OK)) {
1425 njs_internal_error(vm, "lvlhsh insert failed");
1426 goto done;
1427 }
1428 }
1429
1430 ret = NJS_OK;
1431 njs_set_object(&vm->retval, descriptors);
1432
1433 done:
1434
1435 njs_array_destroy(vm, names);
1436
1437 return ret;
1438 }
1439
1440
1441 static njs_int_t
njs_object_get_own_property(njs_vm_t * vm,njs_value_t * args,njs_uint_t nargs,njs_index_t type)1442 njs_object_get_own_property(njs_vm_t *vm, njs_value_t *args,
1443 njs_uint_t nargs, njs_index_t type)
1444 {
1445 njs_array_t *names;
1446 njs_value_t *value;
1447
1448 value = njs_arg(args, nargs, 1);
1449
1450 if (njs_is_null_or_undefined(value)) {
1451 njs_type_error(vm, "cannot convert %s argument to object",
1452 njs_type_string(value->type));
1453
1454 return NJS_ERROR;
1455 }
1456
1457 names = njs_value_own_enumerate(vm, value, NJS_ENUM_KEYS,
1458 type, 1);
1459 if (names == NULL) {
1460 return NJS_ERROR;
1461 }
1462
1463 njs_set_array(&vm->retval, names);
1464
1465 return NJS_OK;
1466 }
1467
1468
1469 static njs_int_t
njs_object_get_prototype_of(njs_vm_t * vm,njs_value_t * args,njs_uint_t nargs,njs_index_t unused)1470 njs_object_get_prototype_of(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
1471 njs_index_t unused)
1472 {
1473 uint32_t index;
1474 njs_value_t *value;
1475
1476 value = njs_arg(args, nargs, 1);
1477
1478 if (njs_is_object(value)) {
1479 njs_object_prototype_proto(vm, NULL, value, NULL, &vm->retval);
1480 return NJS_OK;
1481 }
1482
1483 if (!njs_is_null_or_undefined(value)) {
1484 index = njs_primitive_prototype_index(value->type);
1485
1486 if (njs_is_symbol(value)) {
1487 njs_set_object(&vm->retval, &vm->prototypes[index].object);
1488
1489 } else {
1490 njs_set_object_value(&vm->retval,
1491 &vm->prototypes[index].object_value);
1492 }
1493
1494 return NJS_OK;
1495 }
1496
1497 njs_type_error(vm, "cannot convert %s argument to object",
1498 njs_type_string(value->type));
1499
1500 return NJS_ERROR;
1501 }
1502
1503
1504 static njs_int_t
njs_object_set_prototype_of(njs_vm_t * vm,njs_value_t * args,njs_uint_t nargs,njs_index_t unused)1505 njs_object_set_prototype_of(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
1506 njs_index_t unused)
1507 {
1508 njs_int_t ret;
1509 njs_value_t *value, *proto;
1510
1511 value = njs_arg(args, nargs, 1);
1512 if (njs_slow_path(njs_is_null_or_undefined(value))) {
1513 njs_type_error(vm, "cannot convert %s argument to object",
1514 njs_type_string(value->type));
1515 return NJS_ERROR;
1516 }
1517
1518 proto = njs_arg(args, nargs, 2);
1519 if (njs_slow_path(!njs_is_object(proto) && !njs_is_null(proto))) {
1520 njs_type_error(vm, "prototype may only be an object or null: %s",
1521 njs_type_string(proto->type));
1522 return NJS_ERROR;
1523 }
1524
1525 if (njs_slow_path(!njs_is_object(value))) {
1526 vm->retval = *value;
1527
1528 return NJS_OK;
1529 }
1530
1531 ret = njs_object_set_prototype(vm, njs_object(value), proto);
1532 if (njs_fast_path(ret == NJS_OK)) {
1533 vm->retval = *value;
1534
1535 return NJS_OK;
1536 }
1537
1538 if (ret == NJS_DECLINED) {
1539 njs_type_error(vm, "Cannot set property \"prototype\", "
1540 "object is not extensible");
1541
1542 } else {
1543 njs_type_error(vm, "Cyclic __proto__ value");
1544 }
1545
1546 return NJS_ERROR;
1547 }
1548
1549
1550 /* 7.3.15 SetIntegrityLevel */
1551
1552 static njs_int_t
njs_object_set_integrity_level(njs_vm_t * vm,njs_value_t * args,njs_uint_t nargs,njs_index_t level)1553 njs_object_set_integrity_level(njs_vm_t *vm, njs_value_t *args,
1554 njs_uint_t nargs, njs_index_t level)
1555 {
1556 njs_int_t ret;
1557 njs_value_t *value;
1558 njs_lvlhsh_t *hash;
1559 njs_object_t *object;
1560 njs_object_prop_t *prop;
1561 njs_lvlhsh_each_t lhe;
1562
1563 value = njs_arg(args, nargs, 1);
1564
1565 if (njs_slow_path(!njs_is_object(value))) {
1566 vm->retval = *value;
1567 return NJS_OK;
1568 }
1569
1570 if (njs_slow_path(level == NJS_OBJECT_INTEGRITY_FROZEN
1571 && njs_is_typed_array(value)
1572 && njs_typed_array_length(njs_typed_array(value)) != 0))
1573 {
1574 njs_type_error(vm, "Cannot freeze array buffer views with elements");
1575 return NJS_ERROR;
1576 }
1577
1578 if (njs_is_fast_array(value)) {
1579 ret = njs_array_convert_to_slow_array(vm, njs_array(value));
1580 if (ret != NJS_OK) {
1581 return ret;
1582 }
1583 }
1584
1585 object = njs_object(value);
1586 object->extensible = 0;
1587
1588 njs_lvlhsh_each_init(&lhe, &njs_object_hash_proto);
1589
1590 hash = &object->hash;
1591
1592 for ( ;; ) {
1593 prop = njs_lvlhsh_each(hash, &lhe);
1594
1595 if (prop == NULL) {
1596 break;
1597 }
1598
1599 if (level == NJS_OBJECT_INTEGRITY_FROZEN
1600 && !njs_is_accessor_descriptor(prop))
1601 {
1602 prop->writable = 0;
1603 }
1604
1605 prop->configurable = 0;
1606 }
1607
1608 vm->retval = *value;
1609
1610 return NJS_OK;
1611 }
1612
1613
1614 static njs_int_t
njs_object_test_integrity_level(njs_vm_t * vm,njs_value_t * args,njs_uint_t nargs,njs_index_t level)1615 njs_object_test_integrity_level(njs_vm_t *vm, njs_value_t *args,
1616 njs_uint_t nargs, njs_index_t level)
1617 {
1618 njs_value_t *value;
1619 njs_lvlhsh_t *hash;
1620 njs_object_t *object;
1621 njs_object_prop_t *prop;
1622 njs_lvlhsh_each_t lhe;
1623 const njs_value_t *retval;
1624
1625 value = njs_arg(args, nargs, 1);
1626
1627 if (njs_slow_path(!njs_is_object(value))) {
1628 vm->retval = njs_value_true;
1629 return NJS_OK;
1630 }
1631
1632 retval = &njs_value_false;
1633
1634 object = njs_object(value);
1635
1636 if (object->extensible) {
1637 goto done;
1638 }
1639
1640 if (njs_slow_path(level == NJS_OBJECT_INTEGRITY_FROZEN)
1641 && njs_is_typed_array(value)
1642 && njs_typed_array_length(njs_typed_array(value)) != 0)
1643 {
1644 goto done;
1645 }
1646
1647 njs_lvlhsh_each_init(&lhe, &njs_object_hash_proto);
1648
1649 hash = &object->hash;
1650
1651 for ( ;; ) {
1652 prop = njs_lvlhsh_each(hash, &lhe);
1653
1654 if (prop == NULL) {
1655 break;
1656 }
1657
1658 if (prop->configurable) {
1659 goto done;
1660 }
1661
1662 if (level == NJS_OBJECT_INTEGRITY_FROZEN
1663 && njs_is_data_descriptor(prop) && prop->writable)
1664 {
1665 goto done;
1666 }
1667 }
1668
1669 retval = &njs_value_true;
1670
1671 done:
1672
1673 vm->retval = *retval;
1674
1675 return NJS_OK;
1676 }
1677
1678
1679 static njs_int_t
njs_object_prevent_extensions(njs_vm_t * vm,njs_value_t * args,njs_uint_t nargs,njs_index_t unused)1680 njs_object_prevent_extensions(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
1681 njs_index_t unused)
1682 {
1683 njs_value_t *value;
1684
1685 value = njs_arg(args, nargs, 1);
1686
1687 if (!njs_is_object(value)) {
1688 vm->retval = *value;
1689 return NJS_OK;
1690 }
1691
1692 njs_object(&args[1])->extensible = 0;
1693
1694 vm->retval = *value;
1695
1696 return NJS_OK;
1697 }
1698
1699
1700 static njs_int_t
njs_object_is_extensible(njs_vm_t * vm,njs_value_t * args,njs_uint_t nargs,njs_index_t unused)1701 njs_object_is_extensible(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
1702 njs_index_t unused)
1703 {
1704 njs_value_t *value;
1705 const njs_value_t *retval;
1706
1707 value = njs_arg(args, nargs, 1);
1708
1709 if (!njs_is_object(value)) {
1710 vm->retval = njs_value_false;
1711 return NJS_OK;
1712 }
1713
1714 retval = njs_object(value)->extensible ? &njs_value_true
1715 : &njs_value_false;
1716
1717 vm->retval = *retval;
1718
1719 return NJS_OK;
1720 }
1721
1722
1723 static njs_int_t
njs_object_assign(njs_vm_t * vm,njs_value_t * args,njs_uint_t nargs,njs_index_t unused)1724 njs_object_assign(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
1725 njs_index_t unused)
1726 {
1727 uint32_t i, j, length;
1728 njs_int_t ret;
1729 njs_array_t *names;
1730 njs_value_t *key, *source, *value, setval;
1731 njs_object_prop_t *prop;
1732 njs_property_query_t pq;
1733
1734 value = njs_arg(args, nargs, 1);
1735
1736 ret = njs_value_to_object(vm, value);
1737 if (njs_slow_path(ret != NJS_OK)) {
1738 return ret;
1739 }
1740
1741 names = NULL;
1742
1743 for (i = 2; i < nargs; i++) {
1744 source = &args[i];
1745
1746 names = njs_value_own_enumerate(vm, source, NJS_ENUM_KEYS,
1747 NJS_ENUM_STRING | NJS_ENUM_SYMBOL, 1);
1748 if (njs_slow_path(names == NULL)) {
1749 return NJS_ERROR;
1750 }
1751
1752 length = names->length;
1753
1754 for (j = 0; j < length; j++) {
1755 key = &names->start[j];
1756
1757 njs_property_query_init(&pq, NJS_PROPERTY_QUERY_GET, 1);
1758
1759 ret = njs_property_query(vm, &pq, source, key);
1760 if (njs_slow_path(ret != NJS_OK)) {
1761 goto exception;
1762 }
1763
1764 prop = pq.lhq.value;
1765 if (!prop->enumerable) {
1766 continue;
1767 }
1768
1769 ret = njs_value_property(vm, source, key, &setval);
1770 if (njs_slow_path(ret != NJS_OK)) {
1771 goto exception;
1772 }
1773
1774 ret = njs_value_property_set(vm, value, key, &setval);
1775 if (njs_slow_path(ret != NJS_OK)) {
1776 goto exception;
1777 }
1778 }
1779
1780 njs_array_destroy(vm, names);
1781 }
1782
1783 vm->retval = *value;
1784
1785 return NJS_OK;
1786
1787 exception:
1788
1789 njs_array_destroy(vm, names);
1790
1791 return NJS_ERROR;
1792 }
1793
1794
1795 static njs_int_t
njs_object_is(njs_vm_t * vm,njs_value_t * args,njs_uint_t nargs,njs_index_t unused)1796 njs_object_is(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
1797 njs_index_t unused)
1798 {
1799 njs_set_boolean(&vm->retval, njs_values_same(njs_arg(args, nargs, 1),
1800 njs_arg(args, nargs, 2)));
1801
1802 return NJS_OK;
1803 }
1804
1805
1806 /*
1807 * The __proto__ property of booleans, numbers and strings primitives,
1808 * of objects created by Boolean(), Number(), and String() constructors,
1809 * and of Boolean.prototype, Number.prototype, and String.prototype objects.
1810 */
1811
1812 njs_int_t
njs_primitive_prototype_get_proto(njs_vm_t * vm,njs_object_prop_t * prop,njs_value_t * value,njs_value_t * setval,njs_value_t * retval)1813 njs_primitive_prototype_get_proto(njs_vm_t *vm, njs_object_prop_t *prop,
1814 njs_value_t *value, njs_value_t *setval, njs_value_t *retval)
1815 {
1816 njs_uint_t index;
1817 njs_object_t *proto;
1818
1819 /*
1820 * The __proto__ getters reside in object prototypes of primitive types
1821 * and have to return different results for primitive type and for objects.
1822 */
1823 if (njs_is_object(value)) {
1824 proto = njs_object(value)->__proto__;
1825
1826 } else {
1827 index = njs_primitive_prototype_index(value->type);
1828 proto = &vm->prototypes[index].object;
1829 }
1830
1831 if (proto != NULL) {
1832 njs_set_type_object(retval, proto, proto->type);
1833
1834 } else {
1835 njs_set_undefined(retval);
1836 }
1837
1838 return NJS_OK;
1839 }
1840
1841
1842 /*
1843 * The "prototype" property of Object(), Array() and other functions is
1844 * created on demand in the functions' private hash by the "prototype"
1845 * getter. The properties are set to appropriate prototype.
1846 */
1847
1848 njs_int_t
njs_object_prototype_create(njs_vm_t * vm,njs_object_prop_t * prop,njs_value_t * value,njs_value_t * setval,njs_value_t * retval)1849 njs_object_prototype_create(njs_vm_t *vm, njs_object_prop_t *prop,
1850 njs_value_t *value, njs_value_t *setval, njs_value_t *retval)
1851 {
1852 int32_t index;
1853 njs_function_t *function;
1854 const njs_value_t *proto;
1855
1856 proto = NULL;
1857 function = njs_function(value);
1858 index = function - vm->constructors;
1859
1860 if (index >= 0 && index < NJS_OBJ_TYPE_MAX) {
1861 proto = njs_property_prototype_create(vm, &function->object.hash,
1862 &vm->prototypes[index].object);
1863 }
1864
1865 if (proto == NULL) {
1866 proto = &njs_value_undefined;
1867 }
1868
1869 *retval = *proto;
1870
1871 return NJS_OK;
1872 }
1873
1874
1875 njs_value_t *
njs_property_prototype_create(njs_vm_t * vm,njs_lvlhsh_t * hash,njs_object_t * prototype)1876 njs_property_prototype_create(njs_vm_t *vm, njs_lvlhsh_t *hash,
1877 njs_object_t *prototype)
1878 {
1879 njs_int_t ret;
1880 njs_object_prop_t *prop;
1881 njs_lvlhsh_query_t lhq;
1882
1883 static const njs_value_t proto_string = njs_string("prototype");
1884
1885 prop = njs_object_prop_alloc(vm, &proto_string, &njs_value_undefined, 0);
1886 if (njs_slow_path(prop == NULL)) {
1887 return NULL;
1888 }
1889
1890 /* GC */
1891
1892 njs_set_type_object(&prop->value, prototype, prototype->type);
1893
1894 lhq.value = prop;
1895 lhq.key_hash = NJS_PROTOTYPE_HASH;
1896 lhq.key = njs_str_value("prototype");
1897 lhq.replace = 1;
1898 lhq.pool = vm->mem_pool;
1899 lhq.proto = &njs_object_hash_proto;
1900
1901 ret = njs_lvlhsh_insert(hash, &lhq);
1902
1903 if (njs_fast_path(ret == NJS_OK)) {
1904 return &prop->value;
1905 }
1906
1907 njs_internal_error(vm, "lvlhsh insert failed");
1908
1909 return NULL;
1910 }
1911
1912
1913 static const njs_object_prop_t njs_object_constructor_properties[] =
1914 {
1915 {
1916 .type = NJS_PROPERTY,
1917 .name = njs_string("name"),
1918 .value = njs_string("Object"),
1919 .configurable = 1,
1920 },
1921
1922 {
1923 .type = NJS_PROPERTY,
1924 .name = njs_string("length"),
1925 .value = njs_value(NJS_NUMBER, 1, 1.0),
1926 .configurable = 1,
1927 },
1928
1929 {
1930 .type = NJS_PROPERTY_HANDLER,
1931 .name = njs_string("prototype"),
1932 .value = njs_prop_handler(njs_object_prototype_create),
1933 },
1934
1935 {
1936 .type = NJS_PROPERTY,
1937 .name = njs_string("create"),
1938 .value = njs_native_function(njs_object_create, 2),
1939 .writable = 1,
1940 .configurable = 1,
1941 },
1942
1943 {
1944 .type = NJS_PROPERTY,
1945 .name = njs_string("keys"),
1946 .value = njs_native_function(njs_object_keys, 1),
1947 .writable = 1,
1948 .configurable = 1,
1949 },
1950
1951 {
1952 .type = NJS_PROPERTY,
1953 .name = njs_string("values"),
1954 .value = njs_native_function(njs_object_values, 1),
1955 .writable = 1,
1956 .configurable = 1,
1957 },
1958
1959 {
1960 .type = NJS_PROPERTY,
1961 .name = njs_string("entries"),
1962 .value = njs_native_function(njs_object_entries, 1),
1963 .writable = 1,
1964 .configurable = 1,
1965 },
1966
1967 {
1968 .type = NJS_PROPERTY,
1969 .name = njs_string("defineProperty"),
1970 .value = njs_native_function(njs_object_define_property, 3),
1971 .writable = 1,
1972 .configurable = 1,
1973 },
1974
1975 {
1976 .type = NJS_PROPERTY,
1977 .name = njs_long_string("defineProperties"),
1978 .value = njs_native_function(njs_object_define_properties, 2),
1979 .writable = 1,
1980 .configurable = 1,
1981 },
1982
1983 {
1984 .type = NJS_PROPERTY,
1985 .name = njs_long_string("getOwnPropertyDescriptor"),
1986 .value = njs_native_function(njs_object_get_own_property_descriptor, 2),
1987 .writable = 1,
1988 .configurable = 1,
1989 },
1990
1991 {
1992 .type = NJS_PROPERTY,
1993 .name = njs_long_string("getOwnPropertyDescriptors"),
1994 .value = njs_native_function(njs_object_get_own_property_descriptors,
1995 1),
1996 .writable = 1,
1997 .configurable = 1,
1998 },
1999
2000 {
2001 .type = NJS_PROPERTY,
2002 .name = njs_long_string("getOwnPropertyNames"),
2003 .value = njs_native_function2(njs_object_get_own_property, 1,
2004 NJS_ENUM_STRING),
2005 .writable = 1,
2006 .configurable = 1,
2007 },
2008
2009 {
2010 .type = NJS_PROPERTY,
2011 .name = njs_long_string("getOwnPropertySymbols"),
2012 .value = njs_native_function2(njs_object_get_own_property, 1,
2013 NJS_ENUM_SYMBOL),
2014 .writable = 1,
2015 .configurable = 1,
2016 },
2017
2018 {
2019 .type = NJS_PROPERTY,
2020 .name = njs_string("getPrototypeOf"),
2021 .value = njs_native_function(njs_object_get_prototype_of, 1),
2022 .writable = 1,
2023 .configurable = 1,
2024 },
2025
2026 {
2027 .type = NJS_PROPERTY,
2028 .name = njs_string("setPrototypeOf"),
2029 .value = njs_native_function(njs_object_set_prototype_of, 2),
2030 .writable = 1,
2031 .configurable = 1,
2032 },
2033
2034 {
2035 .type = NJS_PROPERTY,
2036 .name = njs_string("freeze"),
2037 .value = njs_native_function2(njs_object_set_integrity_level,
2038 1, NJS_OBJECT_INTEGRITY_FROZEN),
2039 .writable = 1,
2040 .configurable = 1,
2041 },
2042
2043 {
2044 .type = NJS_PROPERTY,
2045 .name = njs_string("isFrozen"),
2046 .value = njs_native_function2(njs_object_test_integrity_level,
2047 1, NJS_OBJECT_INTEGRITY_FROZEN),
2048 .writable = 1,
2049 .configurable = 1,
2050 },
2051
2052 {
2053 .type = NJS_PROPERTY,
2054 .name = njs_string("seal"),
2055 .value = njs_native_function2(njs_object_set_integrity_level,
2056 1, NJS_OBJECT_INTEGRITY_SEALED),
2057 .writable = 1,
2058 .configurable = 1,
2059 },
2060
2061 {
2062 .type = NJS_PROPERTY,
2063 .name = njs_string("isSealed"),
2064 .value = njs_native_function2(njs_object_test_integrity_level,
2065 1, NJS_OBJECT_INTEGRITY_SEALED),
2066 .writable = 1,
2067 .configurable = 1,
2068 },
2069
2070 {
2071 .type = NJS_PROPERTY,
2072 .name = njs_long_string("preventExtensions"),
2073 .value = njs_native_function(njs_object_prevent_extensions, 1),
2074 .writable = 1,
2075 .configurable = 1,
2076 },
2077
2078 {
2079 .type = NJS_PROPERTY,
2080 .name = njs_string("isExtensible"),
2081 .value = njs_native_function(njs_object_is_extensible, 1),
2082 .writable = 1,
2083 .configurable = 1,
2084 },
2085
2086 {
2087 .type = NJS_PROPERTY,
2088 .name = njs_string("assign"),
2089 .value = njs_native_function(njs_object_assign, 2),
2090 .writable = 1,
2091 .configurable = 1,
2092 },
2093
2094 {
2095 .type = NJS_PROPERTY,
2096 .name = njs_string("is"),
2097 .value = njs_native_function(njs_object_is, 2),
2098 .writable = 1,
2099 .configurable = 1,
2100 },
2101 };
2102
2103
2104 const njs_object_init_t njs_object_constructor_init = {
2105 njs_object_constructor_properties,
2106 njs_nitems(njs_object_constructor_properties),
2107 };
2108
2109
2110 static njs_int_t
njs_object_set_prototype(njs_vm_t * vm,njs_object_t * object,const njs_value_t * value)2111 njs_object_set_prototype(njs_vm_t *vm, njs_object_t *object,
2112 const njs_value_t *value)
2113 {
2114 const njs_object_t *proto;
2115
2116 proto = njs_object(value);
2117
2118 if (njs_slow_path(object->__proto__ == proto)) {
2119 return NJS_OK;
2120 }
2121
2122 if (!object->extensible) {
2123 return NJS_DECLINED;
2124 }
2125
2126 if (njs_slow_path(proto == NULL)) {
2127 object->__proto__ = NULL;
2128 return NJS_OK;
2129 }
2130
2131 do {
2132 if (proto == object) {
2133 return NJS_ERROR;
2134 }
2135
2136 proto = proto->__proto__;
2137
2138 } while (proto != NULL);
2139
2140 object->__proto__ = njs_object(value);
2141
2142 return NJS_OK;
2143 }
2144
2145
2146 njs_int_t
njs_object_prototype_proto(njs_vm_t * vm,njs_object_prop_t * prop,njs_value_t * value,njs_value_t * setval,njs_value_t * retval)2147 njs_object_prototype_proto(njs_vm_t *vm, njs_object_prop_t *prop,
2148 njs_value_t *value, njs_value_t *setval, njs_value_t *retval)
2149 {
2150 njs_int_t ret;
2151 njs_object_t *proto, *object;
2152
2153 if (!njs_is_object(value)) {
2154 *retval = *value;
2155 return NJS_OK;
2156 }
2157
2158 object = njs_object(value);
2159
2160 if (setval != NULL) {
2161 if (njs_is_object(setval) || njs_is_null(setval)) {
2162 ret = njs_object_set_prototype(vm, object, setval);
2163 if (njs_slow_path(ret == NJS_ERROR)) {
2164 njs_type_error(vm, "Cyclic __proto__ value");
2165 return NJS_ERROR;
2166 }
2167 }
2168
2169 njs_set_undefined(retval);
2170
2171 return NJS_OK;
2172 }
2173
2174 proto = object->__proto__;
2175
2176 if (njs_fast_path(proto != NULL)) {
2177 njs_set_type_object(retval, proto, proto->type);
2178
2179 } else {
2180 *retval = njs_value_null;
2181 }
2182
2183 return NJS_OK;
2184 }
2185
2186
2187 /*
2188 * The "constructor" property of Object(), Array() and other functions
2189 * prototypes is created on demand in the prototypes' private hash by the
2190 * "constructor" getter. The properties are set to appropriate function.
2191 */
2192
2193 njs_int_t
njs_object_prototype_create_constructor(njs_vm_t * vm,njs_object_prop_t * prop,njs_value_t * value,njs_value_t * setval,njs_value_t * retval)2194 njs_object_prototype_create_constructor(njs_vm_t *vm, njs_object_prop_t *prop,
2195 njs_value_t *value, njs_value_t *setval, njs_value_t *retval)
2196 {
2197 int32_t index;
2198 njs_value_t *cons, constructor;
2199 njs_object_t *object;
2200 njs_object_prototype_t *prototype;
2201
2202 if (setval != NULL) {
2203 if (!njs_is_object(value)) {
2204 njs_type_error(vm, "Cannot create propery \"constructor\" on %s",
2205 njs_type_string(value->type));
2206 return NJS_ERROR;
2207 }
2208
2209 cons = njs_property_constructor_set(vm, njs_object_hash(value), setval);
2210 if (njs_slow_path(cons == NULL)) {
2211 return NJS_ERROR;
2212 }
2213
2214 *retval = *cons;
2215 return NJS_OK;
2216 }
2217
2218 if (njs_is_object(value)) {
2219 object = njs_object(value);
2220
2221 do {
2222 prototype = (njs_object_prototype_t *) object;
2223 index = prototype - vm->prototypes;
2224
2225 if (index >= 0 && index < NJS_OBJ_TYPE_MAX) {
2226 goto found;
2227 }
2228
2229 object = object->__proto__;
2230
2231 } while (object != NULL);
2232
2233 return NJS_ERROR;
2234
2235 } else {
2236 index = njs_primitive_prototype_index(value->type);
2237 prototype = &vm->prototypes[index];
2238 }
2239
2240 found:
2241
2242 njs_set_function(&constructor, &vm->constructors[index]);
2243 setval = &constructor;
2244
2245 cons = njs_property_constructor_set(vm, &prototype->object.hash, setval);
2246 if (njs_slow_path(cons == NULL)) {
2247 return NJS_ERROR;
2248 }
2249
2250 *retval = *cons;
2251 return NJS_OK;
2252 }
2253
2254
2255 njs_value_t *
njs_property_constructor_set(njs_vm_t * vm,njs_lvlhsh_t * hash,njs_value_t * constructor)2256 njs_property_constructor_set(njs_vm_t *vm, njs_lvlhsh_t *hash,
2257 njs_value_t *constructor)
2258 {
2259 njs_int_t ret;
2260 njs_object_prop_t *prop;
2261 njs_lvlhsh_query_t lhq;
2262
2263 static const njs_value_t constructor_string = njs_string("constructor");
2264
2265 prop = njs_object_prop_alloc(vm, &constructor_string, constructor, 1);
2266 if (njs_slow_path(prop == NULL)) {
2267 return NULL;
2268 }
2269
2270 /* GC */
2271
2272 prop->value = *constructor;
2273 prop->enumerable = 0;
2274
2275 lhq.value = prop;
2276 lhq.key_hash = NJS_CONSTRUCTOR_HASH;
2277 lhq.key = njs_str_value("constructor");
2278 lhq.replace = 1;
2279 lhq.pool = vm->mem_pool;
2280 lhq.proto = &njs_object_hash_proto;
2281
2282 ret = njs_lvlhsh_insert(hash, &lhq);
2283
2284 if (njs_fast_path(ret == NJS_OK)) {
2285 return &prop->value;
2286 }
2287
2288 njs_internal_error(vm, "lvlhsh insert/replace failed");
2289
2290 return NULL;
2291 }
2292
2293
2294 static njs_int_t
njs_object_prototype_value_of(njs_vm_t * vm,njs_value_t * args,njs_uint_t nargs,njs_index_t unused)2295 njs_object_prototype_value_of(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
2296 njs_index_t unused)
2297 {
2298 vm->retval = *njs_argument(args, 0);
2299
2300 if (!njs_is_object(&vm->retval)) {
2301 return njs_value_to_object(vm, &vm->retval);
2302 }
2303
2304 return NJS_OK;
2305 }
2306
2307
2308 static const njs_value_t njs_object_null_string = njs_string("[object Null]");
2309 static const njs_value_t njs_object_undefined_string =
2310 njs_long_string("[object Undefined]");
2311 static const njs_value_t njs_object_boolean_string =
2312 njs_long_string("[object Boolean]");
2313 static const njs_value_t njs_object_number_string =
2314 njs_long_string("[object Number]");
2315 static const njs_value_t njs_object_string_string =
2316 njs_long_string("[object String]");
2317 static const njs_value_t njs_object_object_string =
2318 njs_long_string("[object Object]");
2319 static const njs_value_t njs_object_array_string =
2320 njs_string("[object Array]");
2321 static const njs_value_t njs_object_function_string =
2322 njs_long_string("[object Function]");
2323 static const njs_value_t njs_object_regexp_string =
2324 njs_long_string("[object RegExp]");
2325 static const njs_value_t njs_object_date_string = njs_string("[object Date]");
2326 static const njs_value_t njs_object_error_string =
2327 njs_string("[object Error]");
2328 static const njs_value_t njs_object_arguments_string =
2329 njs_long_string("[object Arguments]");
2330
2331
2332 njs_int_t
njs_object_prototype_to_string(njs_vm_t * vm,njs_value_t * args,njs_uint_t nargs,njs_index_t unused)2333 njs_object_prototype_to_string(njs_vm_t *vm, njs_value_t *args,
2334 njs_uint_t nargs, njs_index_t unused)
2335 {
2336 u_char *p;
2337 njs_int_t ret;
2338 njs_value_t tag, *this;
2339 njs_string_prop_t string;
2340 const njs_value_t *name;
2341
2342 this = njs_argument(args, 0);
2343
2344 if (njs_is_null_or_undefined(this)) {
2345 vm->retval = njs_is_null(this) ? njs_object_null_string
2346 : njs_object_undefined_string;
2347
2348 return NJS_OK;
2349 }
2350
2351 ret = njs_value_to_object(vm, this);
2352 if (njs_slow_path(ret != NJS_OK)) {
2353 return ret;
2354 }
2355
2356 name = &njs_object_object_string;
2357
2358 if (njs_is_array(this)) {
2359 name = &njs_object_array_string;
2360
2361 } else if (njs_is_object(this)
2362 && njs_lvlhsh_eq(&njs_object(this)->shared_hash,
2363 &vm->shared->arguments_object_instance_hash))
2364 {
2365 name = &njs_object_arguments_string;
2366
2367 } else if (njs_is_function(this)) {
2368 name = &njs_object_function_string;
2369
2370 } else if (njs_is_error(this)) {
2371 name = &njs_object_error_string;
2372
2373 } else if (njs_is_object_value(this)) {
2374
2375 switch (njs_object_value(this)->type) {
2376 case NJS_BOOLEAN:
2377 name = &njs_object_boolean_string;
2378 break;
2379
2380 case NJS_NUMBER:
2381 name = &njs_object_number_string;
2382 break;
2383
2384 case NJS_STRING:
2385 name = &njs_object_string_string;
2386 break;
2387
2388 default:
2389 break;
2390 }
2391
2392 } else if (njs_is_date(this)) {
2393 name = &njs_object_date_string;
2394
2395 } else if (njs_is_regexp(this)) {
2396 name = &njs_object_regexp_string;
2397 }
2398
2399 ret = njs_object_string_tag(vm, this, &tag);
2400 if (njs_slow_path(ret == NJS_ERROR)) {
2401 return ret;
2402 }
2403
2404 if (ret == NJS_DECLINED) {
2405 if (njs_slow_path(name == NULL)) {
2406 njs_internal_error(vm, "Unknown value type");
2407
2408 return NJS_ERROR;
2409 }
2410
2411 vm->retval = *name;
2412
2413 return NJS_OK;
2414 }
2415
2416 (void) njs_string_prop(&string, &tag);
2417
2418 p = njs_string_alloc(vm, &vm->retval, string.size + njs_length("[object ]"),
2419 string.length + njs_length("[object ]"));
2420 if (njs_slow_path(p == NULL)) {
2421 return NJS_ERROR;
2422 }
2423
2424 p = njs_cpymem(p, "[object ", 8);
2425 p = njs_cpymem(p, string.start, string.size);
2426 *p = ']';
2427
2428 return NJS_OK;
2429 }
2430
2431
2432 static njs_int_t
njs_object_prototype_has_own_property(njs_vm_t * vm,njs_value_t * args,njs_uint_t nargs,njs_index_t unused)2433 njs_object_prototype_has_own_property(njs_vm_t *vm, njs_value_t *args,
2434 njs_uint_t nargs, njs_index_t unused)
2435 {
2436 njs_int_t ret;
2437 njs_value_t *value, *property;
2438 njs_property_query_t pq;
2439
2440 value = njs_arg(args, nargs, 0);
2441
2442 if (njs_is_null_or_undefined(value)) {
2443 njs_type_error(vm, "cannot convert %s argument to object",
2444 njs_type_string(value->type));
2445 return NJS_ERROR;
2446 }
2447
2448 property = njs_arg(args, nargs, 1);
2449
2450 njs_property_query_init(&pq, NJS_PROPERTY_QUERY_GET, 1);
2451
2452 ret = njs_property_query(vm, &pq, value, property);
2453
2454 switch (ret) {
2455 case NJS_OK:
2456 vm->retval = njs_value_true;
2457 return NJS_OK;
2458
2459 case NJS_DECLINED:
2460 vm->retval = njs_value_false;
2461 return NJS_OK;
2462
2463 case NJS_ERROR:
2464 default:
2465 return ret;
2466 }
2467 }
2468
2469
2470 static njs_int_t
njs_object_prototype_prop_is_enumerable(njs_vm_t * vm,njs_value_t * args,njs_uint_t nargs,njs_index_t unused)2471 njs_object_prototype_prop_is_enumerable(njs_vm_t *vm, njs_value_t *args,
2472 njs_uint_t nargs, njs_index_t unused)
2473 {
2474 njs_int_t ret;
2475 njs_value_t *value, *property;
2476 const njs_value_t *retval;
2477 njs_object_prop_t *prop;
2478 njs_property_query_t pq;
2479
2480 value = njs_arg(args, nargs, 0);
2481
2482 if (njs_is_null_or_undefined(value)) {
2483 njs_type_error(vm, "cannot convert %s argument to object",
2484 njs_type_string(value->type));
2485 return NJS_ERROR;
2486 }
2487
2488 property = njs_arg(args, nargs, 1);
2489
2490 njs_property_query_init(&pq, NJS_PROPERTY_QUERY_GET, 1);
2491
2492 ret = njs_property_query(vm, &pq, value, property);
2493
2494 switch (ret) {
2495 case NJS_OK:
2496 prop = pq.lhq.value;
2497 retval = prop->enumerable ? &njs_value_true : &njs_value_false;
2498 break;
2499
2500 case NJS_DECLINED:
2501 retval = &njs_value_false;
2502 break;
2503
2504 case NJS_ERROR:
2505 default:
2506 return ret;
2507 }
2508
2509 vm->retval = *retval;
2510
2511 return NJS_OK;
2512 }
2513
2514
2515 static njs_int_t
njs_object_prototype_is_prototype_of(njs_vm_t * vm,njs_value_t * args,njs_uint_t nargs,njs_index_t unused)2516 njs_object_prototype_is_prototype_of(njs_vm_t *vm, njs_value_t *args,
2517 njs_uint_t nargs, njs_index_t unused)
2518 {
2519 njs_value_t *prototype, *value;
2520 njs_object_t *object, *proto;
2521 const njs_value_t *retval;
2522
2523 if (njs_slow_path(njs_is_null_or_undefined(njs_arg(args, nargs, 0)))) {
2524 njs_type_error(vm, "cannot convert undefined to object");
2525 return NJS_ERROR;
2526 }
2527
2528 retval = &njs_value_false;
2529 prototype = &args[0];
2530 value = njs_arg(args, nargs, 1);
2531
2532 if (njs_is_object(prototype) && njs_is_object(value)) {
2533 proto = njs_object(prototype);
2534 object = njs_object(value);
2535
2536 do {
2537 object = object->__proto__;
2538
2539 if (object == proto) {
2540 retval = &njs_value_true;
2541 break;
2542 }
2543
2544 } while (object != NULL);
2545 }
2546
2547 vm->retval = *retval;
2548
2549 return NJS_OK;
2550 }
2551
2552
2553 static const njs_object_prop_t njs_object_prototype_properties[] =
2554 {
2555 {
2556 .type = NJS_PROPERTY_HANDLER,
2557 .name = njs_string("__proto__"),
2558 .value = njs_prop_handler(njs_object_prototype_proto),
2559 .writable = 1,
2560 .configurable = 1,
2561 },
2562
2563 {
2564 .type = NJS_PROPERTY_HANDLER,
2565 .name = njs_string("constructor"),
2566 .value = njs_prop_handler(njs_object_prototype_create_constructor),
2567 .writable = 1,
2568 .configurable = 1,
2569 },
2570
2571 {
2572 .type = NJS_PROPERTY,
2573 .name = njs_string("valueOf"),
2574 .value = njs_native_function(njs_object_prototype_value_of, 0),
2575 .writable = 1,
2576 .configurable = 1,
2577 },
2578
2579 {
2580 .type = NJS_PROPERTY,
2581 .name = njs_string("toString"),
2582 .value = njs_native_function(njs_object_prototype_to_string, 0),
2583 .writable = 1,
2584 .configurable = 1,
2585 },
2586
2587 {
2588 .type = NJS_PROPERTY,
2589 .name = njs_string("hasOwnProperty"),
2590 .value = njs_native_function(njs_object_prototype_has_own_property, 1),
2591 .writable = 1,
2592 .configurable = 1,
2593 },
2594
2595 {
2596 .type = NJS_PROPERTY,
2597 .name = njs_long_string("propertyIsEnumerable"),
2598 .value = njs_native_function(njs_object_prototype_prop_is_enumerable,
2599 1),
2600 .writable = 1,
2601 .configurable = 1,
2602 },
2603
2604 {
2605 .type = NJS_PROPERTY,
2606 .name = njs_string("isPrototypeOf"),
2607 .value = njs_native_function(njs_object_prototype_is_prototype_of, 1),
2608 .writable = 1,
2609 .configurable = 1,
2610 },
2611 };
2612
2613
2614 const njs_object_init_t njs_object_prototype_init = {
2615 njs_object_prototype_properties,
2616 njs_nitems(njs_object_prototype_properties),
2617 };
2618
2619
2620 njs_int_t
njs_object_length(njs_vm_t * vm,njs_value_t * value,int64_t * length)2621 njs_object_length(njs_vm_t *vm, njs_value_t *value, int64_t *length)
2622 {
2623 njs_int_t ret;
2624 njs_value_t value_length;
2625
2626 const njs_value_t string_length = njs_string("length");
2627
2628 ret = njs_value_property(vm, value, njs_value_arg(&string_length),
2629 &value_length);
2630 if (njs_slow_path(ret == NJS_ERROR)) {
2631 return ret;
2632 }
2633
2634 return njs_value_to_length(vm, &value_length, length);
2635 }
2636
2637
2638 const njs_object_type_init_t njs_obj_type_init = {
2639 .constructor = njs_native_ctor(njs_object_constructor, 1, 0),
2640 .constructor_props = &njs_object_constructor_init,
2641 .prototype_props = &njs_object_prototype_init,
2642 .prototype_value = { .object = { .type = NJS_OBJECT } },
2643 };
2644