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