1 
2 /*
3  * Copyright (C) Igor Sysoev
4  * Copyright (C) NGINX, Inc.
5  */
6 
7 
8 #include <njs_main.h>
9 
10 
11 static njs_int_t njs_descriptor_prop(njs_vm_t *vm,
12     njs_object_prop_t *prop, const njs_value_t *desc);
13 
14 
15 njs_object_prop_t *
njs_object_prop_alloc(njs_vm_t * vm,const njs_value_t * name,const njs_value_t * value,uint8_t attributes)16 njs_object_prop_alloc(njs_vm_t *vm, const njs_value_t *name,
17     const njs_value_t *value, uint8_t attributes)
18 {
19     njs_object_prop_t  *prop;
20 
21     prop = njs_mp_align(vm->mem_pool, sizeof(njs_value_t),
22                         sizeof(njs_object_prop_t));
23 
24     if (njs_fast_path(prop != NULL)) {
25         /* GC: retain. */
26         prop->value = *value;
27 
28         /* GC: retain. */
29         prop->name = *name;
30 
31         prop->type = NJS_PROPERTY;
32         prop->writable = attributes;
33         prop->enumerable = attributes;
34         prop->configurable = attributes;
35 
36         njs_set_invalid(&prop->getter);
37         njs_set_invalid(&prop->setter);
38 
39         return prop;
40     }
41 
42     njs_memory_error(vm);
43 
44     return NULL;
45 }
46 
47 
48 njs_int_t
njs_object_property(njs_vm_t * vm,const njs_value_t * value,njs_lvlhsh_query_t * lhq,njs_value_t * retval)49 njs_object_property(njs_vm_t *vm, const njs_value_t *value,
50     njs_lvlhsh_query_t *lhq, njs_value_t *retval)
51 {
52     njs_int_t          ret;
53     njs_object_t       *object;
54     njs_object_prop_t  *prop;
55 
56     object = njs_object(value);
57 
58     do {
59         ret = njs_lvlhsh_find(&object->hash, lhq);
60 
61         if (njs_fast_path(ret == NJS_OK)) {
62             goto found;
63         }
64 
65         ret = njs_lvlhsh_find(&object->shared_hash, lhq);
66 
67         if (njs_fast_path(ret == NJS_OK)) {
68             goto found;
69         }
70 
71         object = object->__proto__;
72 
73     } while (object != NULL);
74 
75     njs_set_undefined(retval);
76 
77     return NJS_DECLINED;
78 
79 found:
80 
81     prop = lhq->value;
82 
83     if (njs_is_data_descriptor(prop)) {
84         *retval = prop->value;
85         return NJS_OK;
86     }
87 
88     if (njs_is_undefined(&prop->getter)) {
89         njs_set_undefined(retval);
90         return NJS_OK;
91     }
92 
93     return njs_function_apply(vm, njs_function(&prop->getter), value,
94                               1, retval);
95 }
96 
97 
98 njs_object_prop_t *
njs_object_property_add(njs_vm_t * vm,njs_value_t * object,njs_value_t * key,njs_bool_t replace)99 njs_object_property_add(njs_vm_t *vm, njs_value_t *object, njs_value_t *key,
100     njs_bool_t replace)
101 {
102     njs_int_t           ret;
103     njs_value_t         key_value;
104     njs_object_prop_t   *prop;
105     njs_lvlhsh_query_t  lhq;
106 
107     prop = njs_object_prop_alloc(vm, key, &njs_value_invalid, 1);
108     if (njs_slow_path(prop == NULL)) {
109         return NULL;
110     }
111 
112     ret = njs_value_to_key(vm, &key_value, key);
113     if (njs_slow_path(ret != NJS_OK)) {
114         return NULL;
115     }
116 
117     lhq.proto = &njs_object_hash_proto;
118     njs_string_get(&key_value, &lhq.key);
119     lhq.key_hash = njs_djb_hash(lhq.key.start, lhq.key.length);
120     lhq.value = prop;
121     lhq.replace = replace;
122     lhq.pool = vm->mem_pool;
123 
124     ret = njs_lvlhsh_insert(njs_object_hash(object), &lhq);
125     if (njs_slow_path(ret != NJS_OK)) {
126         njs_internal_error(vm, "lvlhsh insert failed");
127         return NULL;
128     }
129 
130     return prop;
131 }
132 
133 
134 /*
135  * ES5.1, 8.12.9: [[DefineOwnProperty]]
136  */
137 njs_int_t
njs_object_prop_define(njs_vm_t * vm,njs_value_t * object,njs_value_t * name,njs_value_t * value,njs_object_prop_define_t type)138 njs_object_prop_define(njs_vm_t *vm, njs_value_t *object,
139     njs_value_t *name, njs_value_t *value, njs_object_prop_define_t type)
140 {
141     uint32_t              length;
142     njs_int_t             ret;
143     njs_array_t           *array;
144     njs_object_prop_t     *prop, *prev;
145     njs_property_query_t  pq;
146 
147     static const njs_str_t  length_key = njs_str("length");
148 
149     if (njs_slow_path(!njs_is_key(name))) {
150         ret = njs_value_to_key(vm, name, name);
151         if (njs_slow_path(ret != NJS_OK)) {
152             return ret;
153         }
154     }
155 
156 again:
157 
158     njs_property_query_init(&pq, NJS_PROPERTY_QUERY_SET, 1);
159 
160     ret = njs_property_query(vm, &pq, object, name);
161     if (njs_slow_path(ret == NJS_ERROR)) {
162         return ret;
163     }
164 
165     prop = njs_object_prop_alloc(vm, name, &njs_value_invalid,
166                                  NJS_ATTRIBUTE_UNSET);
167     if (njs_slow_path(prop == NULL)) {
168         return NJS_ERROR;
169     }
170 
171     switch (type) {
172 
173     case NJS_OBJECT_PROP_DESCRIPTOR:
174         if (njs_descriptor_prop(vm, prop, value) != NJS_OK) {
175             return NJS_ERROR;
176         }
177 
178         break;
179 
180     case NJS_OBJECT_PROP_GETTER:
181         prop->getter = *value;
182         njs_set_invalid(&prop->setter);
183         prop->enumerable = NJS_ATTRIBUTE_TRUE;
184         prop->configurable = NJS_ATTRIBUTE_TRUE;
185 
186         break;
187 
188     case NJS_OBJECT_PROP_SETTER:
189         prop->setter = *value;
190         njs_set_invalid(&prop->getter);
191         prop->enumerable = NJS_ATTRIBUTE_TRUE;
192         prop->configurable = NJS_ATTRIBUTE_TRUE;
193 
194         break;
195     }
196 
197     if (njs_fast_path(ret == NJS_DECLINED)) {
198 
199 set_prop:
200 
201         if (!njs_object(object)->extensible) {
202             njs_key_string_get(vm, &pq.key,  &pq.lhq.key);
203             njs_type_error(vm, "Cannot add property \"%V\", "
204                            "object is not extensible", &pq.lhq.key);
205             return NJS_ERROR;
206         }
207 
208         if (njs_slow_path(njs_is_typed_array(object)
209                           && njs_is_string(name)))
210         {
211             /* Integer-Indexed Exotic Objects [[DefineOwnProperty]]. */
212             if (!isnan(njs_string_to_index(name))) {
213                 njs_type_error(vm, "Invalid typed array index");
214                 return NJS_ERROR;
215             }
216         }
217 
218         /* 6.2.5.6 CompletePropertyDescriptor */
219 
220         if (njs_is_accessor_descriptor(prop)) {
221             if (!njs_is_valid(&prop->getter)) {
222                 njs_set_undefined(&prop->getter);
223             }
224 
225             if (!njs_is_valid(&prop->setter)) {
226                 njs_set_undefined(&prop->setter);
227             }
228 
229         } else {
230             if (prop->writable == NJS_ATTRIBUTE_UNSET) {
231                 prop->writable = 0;
232             }
233 
234             if (!njs_is_valid(&prop->value)) {
235                 njs_set_undefined(&prop->value);
236             }
237         }
238 
239         if (prop->enumerable == NJS_ATTRIBUTE_UNSET) {
240             prop->enumerable = 0;
241         }
242 
243         if (prop->configurable == NJS_ATTRIBUTE_UNSET) {
244             prop->configurable = 0;
245         }
246 
247         if (njs_slow_path(pq.lhq.value != NULL)) {
248             prev = pq.lhq.value;
249 
250             if (njs_slow_path(prev->type == NJS_WHITEOUT)) {
251                 /* Previously deleted property.  */
252                 *prev = *prop;
253             }
254 
255         } else {
256             pq.lhq.value = prop;
257             pq.lhq.replace = 0;
258             pq.lhq.pool = vm->mem_pool;
259 
260             ret = njs_lvlhsh_insert(njs_object_hash(object), &pq.lhq);
261             if (njs_slow_path(ret != NJS_OK)) {
262                 njs_internal_error(vm, "lvlhsh insert failed");
263                 return NJS_ERROR;
264             }
265         }
266 
267         return NJS_OK;
268     }
269 
270     /* Updating existing prop. */
271 
272     prev = pq.lhq.value;
273 
274     switch (prev->type) {
275     case NJS_PROPERTY:
276     case NJS_PROPERTY_HANDLER:
277         break;
278 
279     case NJS_PROPERTY_REF:
280         if (njs_is_accessor_descriptor(prop)
281             || prop->configurable == NJS_ATTRIBUTE_FALSE
282             || prop->enumerable == NJS_ATTRIBUTE_FALSE
283             || prop->writable == NJS_ATTRIBUTE_FALSE)
284         {
285             array = njs_array(object);
286             length = array->length;
287 
288             ret = njs_array_convert_to_slow_array(vm, array);
289             if (njs_slow_path(ret != NJS_OK)) {
290                 return ret;
291             }
292 
293             ret = njs_array_length_redefine(vm, object, length);
294             if (njs_slow_path(ret != NJS_OK)) {
295                 return ret;
296             }
297 
298             goto again;
299         }
300 
301         if (njs_is_valid(&prop->value)) {
302             *prev->value.data.u.value = prop->value;
303         } else {
304             njs_set_undefined(prev->value.data.u.value);
305         }
306 
307         return NJS_OK;
308 
309     case NJS_PROPERTY_TYPED_ARRAY_REF:
310         if (njs_is_accessor_descriptor(prop)) {
311             goto exception;
312         }
313 
314         if (prop->configurable == NJS_ATTRIBUTE_TRUE ||
315             prop->enumerable == NJS_ATTRIBUTE_FALSE ||
316             prop->writable == NJS_ATTRIBUTE_FALSE)
317         {
318             goto exception;
319         }
320 
321         if (njs_is_valid(&prop->value)) {
322             return njs_typed_array_set_value(vm, njs_typed_array(&prev->value),
323                                              prev->value.data.magic32,
324                                              &prop->value);
325         }
326 
327         return NJS_OK;
328 
329     default:
330         njs_internal_error(vm, "unexpected property type \"%s\" "
331                            "while defining property",
332                            njs_prop_type_string(prev->type));
333 
334         return NJS_ERROR;
335     }
336 
337     /* 9.1.6.3 ValidateAndApplyPropertyDescriptor */
338 
339     if (!prev->configurable) {
340 
341         if (prop->configurable == NJS_ATTRIBUTE_TRUE) {
342             goto exception;
343         }
344 
345         if (prop->enumerable != NJS_ATTRIBUTE_UNSET
346             && prev->enumerable != prop->enumerable)
347         {
348             goto exception;
349         }
350     }
351 
352     if (njs_is_generic_descriptor(prop)) {
353         goto done;
354     }
355 
356     if (njs_is_data_descriptor(prev) != njs_is_data_descriptor(prop)) {
357         if (!prev->configurable) {
358             goto exception;
359         }
360 
361         /*
362          * 6.b-c Preserve the existing values of the converted property's
363          * [[Configurable]] and [[Enumerable]] attributes and set the rest of
364          * the property's attributes to their default values.
365          */
366 
367         if (njs_is_data_descriptor(prev)) {
368             njs_set_undefined(&prev->getter);
369             njs_set_undefined(&prev->setter);
370 
371             njs_set_invalid(&prev->value);
372             prev->writable = NJS_ATTRIBUTE_UNSET;
373 
374         } else {
375             njs_set_undefined(&prev->value);
376             prev->writable = NJS_ATTRIBUTE_FALSE;
377 
378             njs_set_invalid(&prev->getter);
379             njs_set_invalid(&prev->setter);
380         }
381 
382 
383     } else if (njs_is_data_descriptor(prev)
384                && njs_is_data_descriptor(prop))
385     {
386         if (!prev->configurable && !prev->writable) {
387             if (prop->writable == NJS_ATTRIBUTE_TRUE) {
388                 goto exception;
389             }
390 
391             if (njs_is_valid(&prop->value)
392                 && prev->type != NJS_PROPERTY_HANDLER
393                 && !njs_values_same(&prop->value, &prev->value))
394             {
395                 goto exception;
396             }
397         }
398 
399     } else {
400         if (!prev->configurable) {
401             if (njs_is_valid(&prop->getter)
402                 && !njs_values_strict_equal(&prop->getter, &prev->getter))
403             {
404                 goto exception;
405             }
406 
407             if (njs_is_valid(&prop->setter)
408                 && !njs_values_strict_equal(&prop->setter, &prev->setter))
409             {
410                 goto exception;
411             }
412         }
413     }
414 
415 done:
416 
417     if (njs_is_valid(&prop->value) || njs_is_accessor_descriptor(prop)) {
418         if (prev->type == NJS_PROPERTY_HANDLER) {
419             if (prev->writable) {
420                 ret = prev->value.data.u.prop_handler(vm, prev, object,
421                                                       &prop->value,
422                                                       &vm->retval);
423                 if (njs_slow_path(ret == NJS_ERROR)) {
424                     return ret;
425                 }
426 
427                 if (ret == NJS_DECLINED) {
428                     pq.lhq.value = NULL;
429                     goto set_prop;
430                 }
431             }
432 
433         } else {
434             if (njs_slow_path(pq.lhq.key_hash == NJS_LENGTH_HASH)) {
435                 if (njs_strstr_eq(&pq.lhq.key, &length_key)) {
436                     ret = njs_array_length_set(vm, object, prev, &prop->value);
437                     if (ret != NJS_DECLINED) {
438                         return ret;
439                     }
440                 }
441             }
442 
443             prev->value = prop->value;
444         }
445     }
446 
447     /*
448      * 9. For each field of Desc that is present, set the corresponding
449      * attribute of the property named P of object O to the value of the field.
450      */
451 
452     if (njs_is_valid(&prop->getter)) {
453         prev->getter = prop->getter;
454     }
455 
456     if (njs_is_valid(&prop->setter)) {
457         prev->setter = prop->setter;
458     }
459 
460     if (prop->writable != NJS_ATTRIBUTE_UNSET) {
461         prev->writable = prop->writable;
462     }
463 
464     if (prop->enumerable != NJS_ATTRIBUTE_UNSET) {
465         prev->enumerable = prop->enumerable;
466     }
467 
468     if (prop->configurable != NJS_ATTRIBUTE_UNSET) {
469         prev->configurable = prop->configurable;
470     }
471 
472     return NJS_OK;
473 
474 exception:
475 
476     njs_key_string_get(vm, &pq.key,  &pq.lhq.key);
477     njs_type_error(vm, "Cannot redefine property: \"%V\"", &pq.lhq.key);
478 
479     return NJS_ERROR;
480 }
481 
482 
483 njs_int_t
njs_prop_private_copy(njs_vm_t * vm,njs_property_query_t * pq)484 njs_prop_private_copy(njs_vm_t *vm, njs_property_query_t *pq)
485 {
486     njs_int_t          ret;
487     njs_value_t        *value;
488     njs_object_t       *object;
489     njs_function_t     *function;
490     njs_object_prop_t  *prop, *shared;
491 
492     prop = njs_mp_align(vm->mem_pool, sizeof(njs_value_t),
493                         sizeof(njs_object_prop_t));
494     if (njs_slow_path(prop == NULL)) {
495         njs_memory_error(vm);
496         return NJS_ERROR;
497     }
498 
499     shared = pq->lhq.value;
500     *prop = *shared;
501 
502     pq->lhq.replace = 0;
503     pq->lhq.value = prop;
504     pq->lhq.pool = vm->mem_pool;
505 
506     ret = njs_lvlhsh_insert(&pq->prototype->hash, &pq->lhq);
507     if (njs_slow_path(ret != NJS_OK)) {
508         njs_internal_error(vm, "lvlhsh insert failed");
509         return NJS_ERROR;
510     }
511 
512     if (njs_is_accessor_descriptor(prop)) {
513         if (njs_is_function(&prop->getter)) {
514             function = njs_function_value_copy(vm, &prop->getter);
515             if (njs_slow_path(function == NULL)) {
516                 return NJS_ERROR;
517             }
518 
519             if (njs_is_function(&prop->setter)
520                 && function->native && njs_function(&prop->setter)->native
521                 && function->u.native == njs_function(&prop->setter)->u.native)
522             {
523                 prop->setter = prop->getter;
524                 return NJS_OK;
525             }
526         }
527 
528         if (njs_is_function(&prop->setter)) {
529             function = njs_function_value_copy(vm, &prop->setter);
530             if (njs_slow_path(function == NULL)) {
531                 return NJS_ERROR;
532             }
533         }
534 
535         return NJS_OK;
536     }
537 
538     value = &prop->value;
539 
540     switch (value->type) {
541     case NJS_OBJECT:
542     case NJS_OBJECT_VALUE:
543         object = njs_object_value_copy(vm, value);
544         if (njs_slow_path(object == NULL)) {
545             return NJS_ERROR;
546         }
547 
548         value->data.u.object = object;
549         return NJS_OK;
550 
551     case NJS_FUNCTION:
552         function = njs_function_value_copy(vm, &prop->value);
553         if (njs_slow_path(function == NULL)) {
554             return NJS_ERROR;
555         }
556 
557         return njs_function_name_set(vm, function, &prop->name, NULL);
558 
559     default:
560         break;
561     }
562 
563     return NJS_OK;
564 }
565 
566 
567 static njs_int_t
njs_descriptor_prop(njs_vm_t * vm,njs_object_prop_t * prop,const njs_value_t * desc)568 njs_descriptor_prop(njs_vm_t *vm, njs_object_prop_t *prop,
569     const njs_value_t *desc)
570 {
571     njs_int_t           ret;
572     njs_bool_t          data, accessor;
573     njs_value_t         value;
574     njs_lvlhsh_query_t  lhq;
575 
576     static const njs_value_t  get_string = njs_string("get");
577 
578     if (!njs_is_object(desc)) {
579         njs_type_error(vm, "property descriptor must be an object");
580         return NJS_ERROR;
581     }
582 
583     data = 0;
584     accessor = 0;
585 
586     njs_object_property_init(&lhq, &get_string, NJS_GET_HASH);
587 
588     ret = njs_object_property(vm, desc, &lhq, &value);
589 
590     if (njs_slow_path(ret == NJS_ERROR)) {
591         return NJS_ERROR;
592     }
593 
594     if (ret == NJS_OK) {
595         if (njs_is_defined(&value) && !njs_is_function(&value)) {
596             njs_type_error(vm, "Getter must be a function");
597             return NJS_ERROR;
598         }
599 
600         accessor = 1;
601         prop->getter = value;
602 
603     } else {
604         /* NJS_DECLINED */
605         njs_set_invalid(&prop->getter);
606     }
607 
608     lhq.key = njs_str_value("set");
609     lhq.key_hash = NJS_SET_HASH;
610 
611     ret = njs_object_property(vm, desc, &lhq, &value);
612 
613     if (njs_slow_path(ret == NJS_ERROR)) {
614         return ret;
615     }
616 
617     if (ret == NJS_OK) {
618         if (njs_is_defined(&value) && !njs_is_function(&value)) {
619             njs_type_error(vm, "Setter must be a function");
620             return NJS_ERROR;
621         }
622 
623         accessor = 1;
624         prop->setter = value;
625 
626     } else {
627         /* NJS_DECLINED */
628         njs_set_invalid(&prop->setter);
629     }
630 
631     lhq.key = njs_str_value("value");
632     lhq.key_hash = NJS_VALUE_HASH;
633 
634     ret = njs_object_property(vm, desc, &lhq, &value);
635 
636     if (njs_slow_path(ret == NJS_ERROR)) {
637         return ret;
638     }
639 
640     if (ret == NJS_OK) {
641         data = 1;
642         prop->value = value;
643     }
644 
645     lhq.key = njs_str_value("writable");
646     lhq.key_hash = NJS_WRITABABLE_HASH;
647 
648     ret = njs_object_property(vm, desc, &lhq, &value);
649 
650     if (njs_slow_path(ret == NJS_ERROR)) {
651         return ret;
652     }
653 
654     if (ret == NJS_OK) {
655         data = 1;
656         prop->writable = njs_is_true(&value);
657     }
658 
659     lhq.key = njs_str_value("enumerable");
660     lhq.key_hash = NJS_ENUMERABLE_HASH;
661 
662     ret = njs_object_property(vm, desc, &lhq, &value);
663 
664     if (njs_slow_path(ret == NJS_ERROR)) {
665         return ret;
666     }
667 
668     if (ret == NJS_OK) {
669         prop->enumerable = njs_is_true(&value);
670     }
671 
672     lhq.key = njs_str_value("configurable");
673     lhq.key_hash = NJS_CONFIGURABLE_HASH;
674 
675     ret = njs_object_property(vm, desc, &lhq, &value);
676 
677     if (njs_slow_path(ret == NJS_ERROR)) {
678         return ret;
679     }
680 
681     if (ret == NJS_OK) {
682         prop->configurable = njs_is_true(&value);
683     }
684 
685     if (accessor && data) {
686         njs_type_error(vm, "Cannot both specify accessors "
687                            "and a value or writable attribute");
688         return NJS_ERROR;
689     }
690 
691     return NJS_OK;
692 }
693 
694 
695 static const njs_value_t  njs_object_value_string = njs_string("value");
696 static const njs_value_t  njs_object_get_string = njs_string("get");
697 static const njs_value_t  njs_object_set_string = njs_string("set");
698 static const njs_value_t  njs_object_writable_string =
699                                                     njs_string("writable");
700 static const njs_value_t  njs_object_enumerable_string =
701                                                     njs_string("enumerable");
702 static const njs_value_t  njs_object_configurable_string =
703                                                     njs_string("configurable");
704 
705 
706 njs_int_t
njs_object_prop_descriptor(njs_vm_t * vm,njs_value_t * dest,njs_value_t * value,njs_value_t * key)707 njs_object_prop_descriptor(njs_vm_t *vm, njs_value_t *dest,
708     njs_value_t *value, njs_value_t *key)
709 {
710     njs_int_t             ret;
711     njs_object_t          *desc;
712     njs_object_prop_t     *pr, *prop;
713     const njs_value_t     *setval;
714     njs_lvlhsh_query_t    lhq;
715     njs_property_query_t  pq;
716 
717     njs_property_query_init(&pq, NJS_PROPERTY_QUERY_GET, 1);
718 
719     if (njs_slow_path(!njs_is_key(key))) {
720         ret = njs_value_to_key(vm, key, key);
721         if (njs_slow_path(ret != NJS_OK)) {
722             return ret;
723         }
724     }
725 
726     ret = njs_property_query(vm, &pq, value, key);
727 
728     switch (ret) {
729     case NJS_OK:
730         prop = pq.lhq.value;
731 
732         switch (prop->type) {
733         case NJS_PROPERTY:
734             break;
735 
736         case NJS_PROPERTY_HANDLER:
737             pq.scratch = *prop;
738             prop = &pq.scratch;
739             ret = prop->value.data.u.prop_handler(vm, prop, value, NULL,
740                                                   &prop->value);
741             if (njs_slow_path(ret == NJS_ERROR)) {
742                 return ret;
743             }
744 
745             break;
746 
747         default:
748             njs_type_error(vm, "unexpected property type: %s",
749                            njs_prop_type_string(prop->type));
750             return NJS_ERROR;
751         }
752 
753         break;
754 
755     case NJS_DECLINED:
756         njs_set_undefined(dest);
757         return NJS_OK;
758 
759     case NJS_ERROR:
760     default:
761         return ret;
762     }
763 
764     desc = njs_object_alloc(vm);
765     if (njs_slow_path(desc == NULL)) {
766         return NJS_ERROR;
767     }
768 
769     lhq.proto = &njs_object_hash_proto;
770     lhq.replace = 0;
771     lhq.pool = vm->mem_pool;
772 
773     if (njs_is_data_descriptor(prop)) {
774 
775         lhq.key = njs_str_value("value");
776         lhq.key_hash = NJS_VALUE_HASH;
777 
778         pr = njs_object_prop_alloc(vm, &njs_object_value_string, &prop->value,
779                                    1);
780         if (njs_slow_path(pr == NULL)) {
781             return NJS_ERROR;
782         }
783 
784         lhq.value = pr;
785 
786         ret = njs_lvlhsh_insert(&desc->hash, &lhq);
787         if (njs_slow_path(ret != NJS_OK)) {
788             njs_internal_error(vm, "lvlhsh insert failed");
789             return NJS_ERROR;
790         }
791 
792         lhq.key = njs_str_value("writable");
793         lhq.key_hash = NJS_WRITABABLE_HASH;
794 
795         setval = (prop->writable == 1) ? &njs_value_true : &njs_value_false;
796 
797         pr = njs_object_prop_alloc(vm, &njs_object_writable_string, setval, 1);
798         if (njs_slow_path(pr == NULL)) {
799             return NJS_ERROR;
800         }
801 
802         lhq.value = pr;
803 
804         ret = njs_lvlhsh_insert(&desc->hash, &lhq);
805         if (njs_slow_path(ret != NJS_OK)) {
806             njs_internal_error(vm, "lvlhsh insert failed");
807             return NJS_ERROR;
808         }
809 
810     } else {
811 
812         lhq.key = njs_str_value("get");
813         lhq.key_hash = NJS_GET_HASH;
814 
815         pr = njs_object_prop_alloc(vm, &njs_object_get_string, &prop->getter,
816                                    1);
817         if (njs_slow_path(pr == NULL)) {
818             return NJS_ERROR;
819         }
820 
821         lhq.value = pr;
822 
823         ret = njs_lvlhsh_insert(&desc->hash, &lhq);
824         if (njs_slow_path(ret != NJS_OK)) {
825             njs_internal_error(vm, "lvlhsh insert failed");
826             return NJS_ERROR;
827         }
828 
829         lhq.key = njs_str_value("set");
830         lhq.key_hash = NJS_SET_HASH;
831 
832         pr = njs_object_prop_alloc(vm, &njs_object_set_string, &prop->setter,
833                                    1);
834         if (njs_slow_path(pr == NULL)) {
835             return NJS_ERROR;
836         }
837 
838         lhq.value = pr;
839 
840         ret = njs_lvlhsh_insert(&desc->hash, &lhq);
841         if (njs_slow_path(ret != NJS_OK)) {
842             njs_internal_error(vm, "lvlhsh insert failed");
843             return NJS_ERROR;
844         }
845     }
846 
847     lhq.key = njs_str_value("enumerable");
848     lhq.key_hash = NJS_ENUMERABLE_HASH;
849 
850     setval = (prop->enumerable == 1) ? &njs_value_true : &njs_value_false;
851 
852     pr = njs_object_prop_alloc(vm, &njs_object_enumerable_string, setval, 1);
853     if (njs_slow_path(pr == NULL)) {
854         return NJS_ERROR;
855     }
856 
857     lhq.value = pr;
858 
859     ret = njs_lvlhsh_insert(&desc->hash, &lhq);
860     if (njs_slow_path(ret != NJS_OK)) {
861         njs_internal_error(vm, "lvlhsh insert failed");
862         return NJS_ERROR;
863     }
864 
865     lhq.key = njs_str_value("configurable");
866     lhq.key_hash = NJS_CONFIGURABLE_HASH;
867 
868     setval = (prop->configurable == 1) ? &njs_value_true : &njs_value_false;
869 
870     pr = njs_object_prop_alloc(vm, &njs_object_configurable_string, setval, 1);
871     if (njs_slow_path(pr == NULL)) {
872         return NJS_ERROR;
873     }
874 
875     lhq.value = pr;
876 
877     ret = njs_lvlhsh_insert(&desc->hash, &lhq);
878     if (njs_slow_path(ret != NJS_OK)) {
879         njs_internal_error(vm, "lvlhsh insert failed");
880         return NJS_ERROR;
881     }
882 
883     njs_set_object(dest, desc);
884 
885     return NJS_OK;
886 }
887 
888 
889 const char *
njs_prop_type_string(njs_object_prop_type_t type)890 njs_prop_type_string(njs_object_prop_type_t type)
891 {
892     switch (type) {
893     case NJS_PROPERTY_REF:
894         return "property_ref";
895 
896     case NJS_PROPERTY_HANDLER:
897         return "property handler";
898 
899     case NJS_WHITEOUT:
900         return "whiteout";
901 
902     case NJS_PROPERTY:
903         return "property";
904 
905     default:
906         return "unknown";
907     }
908 }
909 
910 
911 njs_int_t
njs_object_prop_init(njs_vm_t * vm,const njs_object_init_t * init,const njs_object_prop_t * base,njs_value_t * value,njs_value_t * retval)912 njs_object_prop_init(njs_vm_t *vm, const njs_object_init_t* init,
913     const njs_object_prop_t *base, njs_value_t *value, njs_value_t *retval)
914 {
915     njs_int_t           ret;
916     njs_object_t        *object;
917     njs_object_prop_t   *prop;
918     njs_lvlhsh_query_t  lhq;
919 
920     object = njs_object_alloc(vm);
921     if (object == NULL) {
922         return NJS_ERROR;
923     }
924 
925     ret = njs_object_hash_create(vm, &object->hash, init->properties,
926                                  init->items);
927     if (njs_slow_path(ret != NJS_OK)) {
928         return NJS_ERROR;
929     }
930 
931     prop = njs_mp_align(vm->mem_pool, sizeof(njs_value_t),
932                         sizeof(njs_object_prop_t));
933     if (njs_slow_path(prop == NULL)) {
934         njs_memory_error(vm);
935         return NJS_ERROR;
936     }
937 
938     *prop = *base;
939 
940     prop->type = NJS_PROPERTY;
941     njs_set_object(&prop->value, object);
942 
943     lhq.proto = &njs_object_hash_proto;
944     njs_string_get(&prop->name, &lhq.key);
945     lhq.key_hash = njs_djb_hash(lhq.key.start, lhq.key.length);
946     lhq.value = prop;
947     lhq.replace = 1;
948     lhq.pool = vm->mem_pool;
949 
950     ret = njs_lvlhsh_insert(njs_object_hash(value), &lhq);
951     if (njs_fast_path(ret == NJS_OK)) {
952         *retval = prop->value;
953         return NJS_OK;
954     }
955 
956     njs_internal_error(vm, "lvlhsh insert failed");
957 
958     return NJS_ERROR;
959 }
960