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_object_property_query(njs_vm_t *vm,
12     njs_property_query_t *pq, njs_object_t *object,
13     const njs_value_t *key);
14 static njs_int_t njs_array_property_query(njs_vm_t *vm,
15     njs_property_query_t *pq, njs_array_t *array, uint32_t index);
16 static njs_int_t njs_typed_array_property_query(njs_vm_t *vm,
17     njs_property_query_t *pq, njs_typed_array_t *array, uint32_t index);
18 static njs_int_t njs_string_property_query(njs_vm_t *vm,
19     njs_property_query_t *pq, njs_value_t *object, uint32_t index);
20 static njs_int_t njs_external_property_query(njs_vm_t *vm,
21     njs_property_query_t *pq, njs_value_t *value);
22 
23 
24 const njs_value_t  njs_value_null =         njs_value(NJS_NULL, 0, 0.0);
25 const njs_value_t  njs_value_undefined =    njs_value(NJS_UNDEFINED, 0, NAN);
26 const njs_value_t  njs_value_false =        njs_value(NJS_BOOLEAN, 0, 0.0);
27 const njs_value_t  njs_value_true =         njs_value(NJS_BOOLEAN, 1, 1.0);
28 const njs_value_t  njs_value_zero =         njs_value(NJS_NUMBER, 0, 0.0);
29 const njs_value_t  njs_value_nan =          njs_value(NJS_NUMBER, 0, NAN);
30 const njs_value_t  njs_value_invalid =      njs_value(NJS_INVALID, 0, 0.0);
31 
32 const njs_value_t  njs_string_empty =       njs_string("");
33 const njs_value_t  njs_string_empty_regexp =
34                                             njs_string("(?:)");
35 const njs_value_t  njs_string_comma =       njs_string(",");
36 const njs_value_t  njs_string_null =        njs_string("null");
37 const njs_value_t  njs_string_undefined =   njs_string("undefined");
38 const njs_value_t  njs_string_boolean =     njs_string("boolean");
39 const njs_value_t  njs_string_false =       njs_string("false");
40 const njs_value_t  njs_string_true =        njs_string("true");
41 const njs_value_t  njs_string_number =      njs_string("number");
42 const njs_value_t  njs_string_minus_zero =  njs_string("-0");
43 const njs_value_t  njs_string_minus_infinity =
44                                             njs_string("-Infinity");
45 const njs_value_t  njs_string_plus_infinity =
46                                             njs_string("Infinity");
47 const njs_value_t  njs_string_nan =         njs_string("NaN");
48 const njs_value_t  njs_string_symbol =      njs_string("symbol");
49 const njs_value_t  njs_string_string =      njs_string("string");
50 const njs_value_t  njs_string_name =        njs_string("name");
51 const njs_value_t  njs_string_data =        njs_string("data");
52 const njs_value_t  njs_string_type =        njs_string("type");
53 const njs_value_t  njs_string_external =    njs_string("external");
54 const njs_value_t  njs_string_invalid =     njs_string("invalid");
55 const njs_value_t  njs_string_object =      njs_string("object");
56 const njs_value_t  njs_string_function =    njs_string("function");
57 const njs_value_t  njs_string_memory_error = njs_string("MemoryError");
58 
59 
60 void
njs_value_retain(njs_value_t * value)61 njs_value_retain(njs_value_t *value)
62 {
63     njs_string_t  *string;
64 
65     if (njs_is_string(value)) {
66 
67         if (value->long_string.external != 0xff) {
68             string = value->long_string.data;
69 
70             njs_thread_log_debug("retain:%uxD \"%*s\"", string->retain,
71                                  value->long_string.size, string->start);
72 
73             if (string->retain != 0xffff) {
74                 string->retain++;
75             }
76         }
77     }
78 }
79 
80 
81 void
njs_value_release(njs_vm_t * vm,njs_value_t * value)82 njs_value_release(njs_vm_t *vm, njs_value_t *value)
83 {
84     njs_string_t  *string;
85 
86     if (njs_is_string(value)) {
87 
88         if (value->long_string.external != 0xff) {
89             string = value->long_string.data;
90 
91             njs_thread_log_debug("release:%uxD \"%*s\"", string->retain,
92                                  value->long_string.size, string->start);
93 
94             if (string->retain != 0xffff) {
95                 string->retain--;
96 
97 #if 0
98                 if (string->retain == 0) {
99                     if ((u_char *) string + sizeof(njs_string_t)
100                         != string->start)
101                     {
102                         njs_memcache_pool_free(vm->mem_pool,
103                                                string->start);
104                     }
105 
106                     njs_memcache_pool_free(vm->mem_pool, string);
107                 }
108 #endif
109             }
110         }
111     }
112 }
113 
114 
115 /*
116  * A hint value is 0 for numbers and 1 for strings.  The value chooses
117  * method calls order specified by ECMAScript 5.1: "valueOf", "toString"
118  * for numbers and "toString", "valueOf" for strings.
119  */
120 
121 njs_int_t
njs_value_to_primitive(njs_vm_t * vm,njs_value_t * dst,njs_value_t * value,njs_uint_t hint)122 njs_value_to_primitive(njs_vm_t *vm, njs_value_t *dst, njs_value_t *value,
123     njs_uint_t hint)
124 {
125     njs_int_t           ret;
126     njs_uint_t          tries;
127     njs_value_t         method, retval;
128     njs_lvlhsh_query_t  lhq;
129 
130     static const uint32_t  hashes[] = {
131         NJS_VALUE_OF_HASH,
132         NJS_TO_STRING_HASH,
133     };
134 
135     static const njs_str_t  names[] = {
136         njs_str("valueOf"),
137         njs_str("toString"),
138     };
139 
140 
141     if (njs_is_primitive(value)) {
142         /* GC */
143         *dst = *value;
144         return NJS_OK;
145     }
146 
147     tries = 0;
148     lhq.proto = &njs_object_hash_proto;
149 
150     for ( ;; ) {
151         ret = NJS_ERROR;
152 
153         if (njs_is_object(value) && tries < 2) {
154             hint ^= tries++;
155 
156             lhq.key_hash = hashes[hint];
157             lhq.key = names[hint];
158 
159             ret = njs_object_property(vm, value, &lhq, &method);
160 
161             if (njs_slow_path(ret == NJS_ERROR)) {
162                 return ret;
163             }
164 
165             if (njs_is_function(&method)) {
166                 ret = njs_function_apply(vm, njs_function(&method), value, 1,
167                                          &retval);
168 
169                 if (njs_slow_path(ret != NJS_OK)) {
170                     return ret;
171                 }
172 
173                 if (njs_is_primitive(&retval)) {
174                     break;
175                 }
176             }
177 
178             /* Try the second method. */
179             continue;
180          }
181 
182         njs_type_error(vm, "Cannot convert object to primitive value");
183 
184         return ret;
185     }
186 
187     *dst = retval;
188 
189     return NJS_OK;
190 }
191 
192 
193 njs_array_t *
njs_value_enumerate(njs_vm_t * vm,njs_value_t * value,njs_object_enum_t kind,njs_object_enum_type_t type,njs_bool_t all)194 njs_value_enumerate(njs_vm_t *vm, njs_value_t *value,
195     njs_object_enum_t kind, njs_object_enum_type_t type, njs_bool_t all)
196 {
197     njs_int_t           ret;
198     njs_value_t         keys;
199     njs_object_value_t  obj_val;
200     njs_exotic_slots_t  *slots;
201 
202     if (njs_is_object(value)) {
203         if (kind == NJS_ENUM_KEYS && (type & NJS_ENUM_STRING)) {
204             slots = njs_object_slots(value);
205             if (slots != NULL && slots->keys != NULL) {
206                 ret = slots->keys(vm, value, &keys);
207                 if (njs_slow_path(ret != NJS_OK)) {
208                     return NULL;
209                 }
210 
211                 return njs_array(&keys);
212             }
213         }
214 
215         return njs_object_enumerate(vm, njs_object(value), kind, type, all);
216     }
217 
218     if (value->type != NJS_STRING) {
219         return njs_array_alloc(vm, 1, 0, NJS_ARRAY_SPARE);
220     }
221 
222     obj_val.object = vm->string_object;
223     obj_val.value = *value;
224 
225     return njs_object_enumerate(vm, (njs_object_t *) &obj_val, kind, type, all);
226 }
227 
228 
229 njs_array_t *
njs_value_own_enumerate(njs_vm_t * vm,njs_value_t * value,njs_object_enum_t kind,njs_object_enum_type_t type,njs_bool_t all)230 njs_value_own_enumerate(njs_vm_t *vm, njs_value_t *value,
231     njs_object_enum_t kind, njs_object_enum_type_t type, njs_bool_t all)
232 {
233     njs_int_t           ret;
234     njs_value_t         keys;
235     njs_object_value_t  obj_val;
236     njs_exotic_slots_t  *slots;
237 
238     if (njs_is_object(value)) {
239         if (kind == NJS_ENUM_KEYS && (type & NJS_ENUM_STRING)) {
240             slots = njs_object_slots(value);
241             if (slots != NULL && slots->keys != NULL) {
242                 ret = slots->keys(vm, value, &keys);
243                 if (njs_slow_path(ret != NJS_OK)) {
244                     return NULL;
245                 }
246 
247                 return njs_array(&keys);
248             }
249         }
250 
251         return njs_object_own_enumerate(vm, njs_object(value), kind, type, all);
252     }
253 
254     if (value->type != NJS_STRING) {
255         return njs_array_alloc(vm, 1, 0, NJS_ARRAY_SPARE);
256     }
257 
258     obj_val.object = vm->string_object;
259     obj_val.value = *value;
260 
261     return njs_object_own_enumerate(vm, (njs_object_t *) &obj_val, kind,
262                                     type, all);
263 }
264 
265 
266 njs_int_t
njs_value_of(njs_vm_t * vm,njs_value_t * value,njs_value_t * retval)267 njs_value_of(njs_vm_t *vm, njs_value_t *value, njs_value_t *retval)
268 {
269 
270     njs_int_t  ret;
271 
272     static const njs_value_t  value_of = njs_string("valueOf");
273 
274     if (njs_slow_path(!njs_is_object(value))) {
275         return NJS_DECLINED;
276     }
277 
278     ret = njs_value_property(vm, value, njs_value_arg(&value_of),
279                              retval);
280     if (njs_slow_path(ret != NJS_OK)) {
281         return ret;
282     }
283 
284     if (!njs_is_function(retval)) {
285         njs_type_error(vm, "object.valueOf is not a function");
286         return NJS_ERROR;
287     }
288 
289     return njs_function_apply(vm, njs_function(retval), value, 1, retval);
290 }
291 
292 
293 njs_int_t
njs_value_length(njs_vm_t * vm,njs_value_t * value,int64_t * length)294 njs_value_length(njs_vm_t *vm, njs_value_t *value, int64_t *length)
295 {
296     njs_string_prop_t  string_prop;
297 
298     if (njs_is_string(value)) {
299         *length = njs_string_prop(&string_prop, value);
300 
301     } else if (njs_is_primitive(value)) {
302         *length = 0;
303 
304     } else if (njs_is_fast_array(value)) {
305         *length = njs_array_len(value);
306 
307     } else {
308         return njs_object_length(vm, value, length);
309     }
310 
311     return NJS_OK;
312 }
313 
314 
315 const char *
njs_type_string(njs_value_type_t type)316 njs_type_string(njs_value_type_t type)
317 {
318     switch (type) {
319     case NJS_NULL:
320         return "null";
321 
322     case NJS_UNDEFINED:
323         return "undefined";
324 
325     case NJS_BOOLEAN:
326         return "boolean";
327 
328     case NJS_NUMBER:
329         return "number";
330 
331     case NJS_SYMBOL:
332         return "symbol";
333 
334     case NJS_STRING:
335         return "string";
336 
337     case NJS_INVALID:
338         return "invalid";
339 
340     case NJS_OBJECT:
341     case NJS_OBJECT_VALUE:
342         return "object";
343 
344     case NJS_ARRAY:
345         return "array";
346 
347     case NJS_ARRAY_BUFFER:
348         return "array buffer";
349 
350     case NJS_TYPED_ARRAY:
351         return "typed array";
352 
353     case NJS_FUNCTION:
354         return "function";
355 
356     case NJS_REGEXP:
357         return "regexp";
358 
359     case NJS_DATE:
360         return "date";
361 
362     case NJS_PROMISE:
363         return "promise";
364 
365     default:
366         return NULL;
367     }
368 }
369 
370 
371 void
njs_value_undefined_set(njs_value_t * value)372 njs_value_undefined_set(njs_value_t *value)
373 {
374     njs_set_undefined(value);
375 }
376 
377 
378 void
njs_value_null_set(njs_value_t * value)379 njs_value_null_set(njs_value_t *value)
380 {
381     njs_set_null(value);
382 }
383 
384 
385 void
njs_value_invalid_set(njs_value_t * value)386 njs_value_invalid_set(njs_value_t *value)
387 {
388     njs_set_invalid(value);
389 }
390 
391 
392 void
njs_value_boolean_set(njs_value_t * value,int yn)393 njs_value_boolean_set(njs_value_t *value, int yn)
394 {
395     njs_set_boolean(value, yn);
396 }
397 
398 
399 void
njs_value_number_set(njs_value_t * value,double num)400 njs_value_number_set(njs_value_t *value, double num)
401 {
402     njs_set_number(value, num);
403 }
404 
405 
406 uint8_t
njs_value_bool(const njs_value_t * value)407 njs_value_bool(const njs_value_t *value)
408 {
409     return njs_bool(value);
410 }
411 
412 
413 double
njs_value_number(const njs_value_t * value)414 njs_value_number(const njs_value_t *value)
415 {
416     return njs_number(value);
417 }
418 
419 
420 njs_function_t *
njs_value_function(const njs_value_t * value)421 njs_value_function(const njs_value_t *value)
422 {
423     return njs_function(value);
424 }
425 
426 
427 njs_int_t
njs_value_is_null(const njs_value_t * value)428 njs_value_is_null(const njs_value_t *value)
429 {
430     return njs_is_null(value);
431 }
432 
433 
434 njs_int_t
njs_value_is_undefined(const njs_value_t * value)435 njs_value_is_undefined(const njs_value_t *value)
436 {
437     return njs_is_undefined(value);
438 }
439 
440 
441 njs_int_t
njs_value_is_null_or_undefined(const njs_value_t * value)442 njs_value_is_null_or_undefined(const njs_value_t *value)
443 {
444     return njs_is_null_or_undefined(value);
445 }
446 
447 
448 njs_int_t
njs_value_is_valid(const njs_value_t * value)449 njs_value_is_valid(const njs_value_t *value)
450 {
451     return njs_is_valid(value);
452 }
453 
454 
455 njs_int_t
njs_value_is_boolean(const njs_value_t * value)456 njs_value_is_boolean(const njs_value_t *value)
457 {
458     return njs_is_boolean(value);
459 }
460 
461 
462 njs_int_t
njs_value_is_number(const njs_value_t * value)463 njs_value_is_number(const njs_value_t *value)
464 {
465     return njs_is_number(value);
466 }
467 
468 
469 njs_int_t
njs_value_is_valid_number(const njs_value_t * value)470 njs_value_is_valid_number(const njs_value_t *value)
471 {
472     return njs_is_number(value)
473            && !isnan(njs_number(value))
474            && !isinf(njs_number(value));
475 }
476 
477 
478 njs_int_t
njs_value_is_string(const njs_value_t * value)479 njs_value_is_string(const njs_value_t *value)
480 {
481     return njs_is_string(value);
482 }
483 
484 
485 njs_int_t
njs_value_is_object(const njs_value_t * value)486 njs_value_is_object(const njs_value_t *value)
487 {
488     return njs_is_object(value);
489 }
490 
491 
492 njs_int_t
njs_value_is_array(const njs_value_t * value)493 njs_value_is_array(const njs_value_t *value)
494 {
495     return njs_is_array(value);
496 }
497 
498 
499 njs_int_t
njs_value_is_function(const njs_value_t * value)500 njs_value_is_function(const njs_value_t *value)
501 {
502     return njs_is_function(value);
503 }
504 
505 
506 njs_int_t
njs_value_is_buffer(const njs_value_t * value)507 njs_value_is_buffer(const njs_value_t *value)
508 {
509     return njs_is_typed_array(value);
510 }
511 
512 
513 /*
514  * ES5.1, 8.12.1: [[GetOwnProperty]], [[GetProperty]].
515  * The njs_property_query() returns values
516  *   NJS_OK               property has been found in object,
517  *     retval of type njs_object_prop_t * is in pq->lhq.value.
518  *     in NJS_PROPERTY_QUERY_GET
519  *       prop->type is NJS_PROPERTY or NJS_PROPERTY_HANDLER.
520  *     in NJS_PROPERTY_QUERY_SET, NJS_PROPERTY_QUERY_DELETE
521  *       prop->type is NJS_PROPERTY, NJS_PROPERTY_REF,
522  *       NJS_PROPERTY_TYPED_ARRAY_REF or
523  *       NJS_PROPERTY_HANDLER.
524  *   NJS_DECLINED         property was not found in object,
525  *     if pq->lhq.value != NULL it contains retval of type
526  *     njs_object_prop_t * where prop->type is NJS_WHITEOUT
527  *   NJS_ERROR            exception has been thrown.
528  */
529 
530 njs_int_t
njs_property_query(njs_vm_t * vm,njs_property_query_t * pq,njs_value_t * value,njs_value_t * key)531 njs_property_query(njs_vm_t *vm, njs_property_query_t *pq, njs_value_t *value,
532     njs_value_t *key)
533 {
534     double          num;
535     uint32_t        index;
536     njs_int_t       ret;
537     njs_object_t    *obj;
538     njs_value_t     prop;
539     njs_function_t  *function;
540 
541     if (njs_slow_path(!njs_is_primitive(key))) {
542         ret = njs_value_to_string(vm, &prop, key);
543         if (ret != NJS_OK) {
544             return ret;
545         }
546 
547         key = &prop;
548     }
549 
550     switch (value->type) {
551 
552     case NJS_BOOLEAN:
553     case NJS_NUMBER:
554     case NJS_SYMBOL:
555         index = njs_primitive_prototype_index(value->type);
556         obj = &vm->prototypes[index].object;
557         break;
558 
559     case NJS_STRING:
560         if (njs_fast_path(!njs_is_null_or_undefined_or_boolean(key))) {
561             num = njs_key_to_index(key);
562             if (njs_fast_path(njs_key_is_integer_index(num, key))) {
563                 return njs_string_property_query(vm, pq, value, num);
564             }
565         }
566 
567         obj = &vm->string_object;
568         break;
569 
570     case NJS_OBJECT:
571     case NJS_ARRAY:
572     case NJS_ARRAY_BUFFER:
573     case NJS_DATA_VIEW:
574     case NJS_TYPED_ARRAY:
575     case NJS_REGEXP:
576     case NJS_DATE:
577     case NJS_PROMISE:
578     case NJS_OBJECT_VALUE:
579         obj = njs_object(value);
580         break;
581 
582     case NJS_FUNCTION:
583         function = njs_function_value_copy(vm, value);
584         if (njs_slow_path(function == NULL)) {
585             return NJS_ERROR;
586         }
587 
588         obj = &function->object;
589         break;
590 
591     case NJS_UNDEFINED:
592     case NJS_NULL:
593     default:
594         ret = njs_primitive_value_to_string(vm, &pq->key, key);
595 
596         if (njs_fast_path(ret == NJS_OK)) {
597             njs_string_get(&pq->key, &pq->lhq.key);
598             njs_type_error(vm, "cannot get property \"%V\" of undefined",
599                            &pq->lhq.key);
600             return NJS_ERROR;
601         }
602 
603         njs_type_error(vm, "cannot get property \"unknown\" of undefined");
604 
605         return NJS_ERROR;
606     }
607 
608     ret = njs_primitive_value_to_key(vm, &pq->key, key);
609 
610     if (njs_fast_path(ret == NJS_OK)) {
611 
612         if (njs_is_symbol(key)) {
613             pq->lhq.key_hash = njs_symbol_key(key);
614             pq->lhq.key.start = NULL;
615 
616         } else {
617             njs_string_get(&pq->key, &pq->lhq.key);
618             pq->lhq.key_hash = njs_djb_hash(pq->lhq.key.start,
619                                             pq->lhq.key.length);
620         }
621 
622         ret = njs_object_property_query(vm, pq, obj, key);
623 
624         if (njs_slow_path(ret == NJS_DECLINED && obj->slots != NULL)) {
625             return njs_external_property_query(vm, pq, value);
626         }
627     }
628 
629     return ret;
630 }
631 
632 
633 static njs_int_t
njs_object_property_query(njs_vm_t * vm,njs_property_query_t * pq,njs_object_t * object,const njs_value_t * key)634 njs_object_property_query(njs_vm_t *vm, njs_property_query_t *pq,
635     njs_object_t *object, const njs_value_t *key)
636 {
637     double              num;
638     njs_int_t           ret;
639     njs_bool_t          own;
640     njs_array_t         *array;
641     njs_object_t        *proto;
642     njs_object_prop_t   *prop;
643     njs_typed_array_t   *tarray;
644     njs_object_value_t  *ov;
645 
646     pq->lhq.proto = &njs_object_hash_proto;
647 
648     own = pq->own;
649     pq->own = 1;
650 
651     proto = object;
652 
653     do {
654         pq->prototype = proto;
655 
656         if (!njs_is_null_or_undefined_or_boolean(key)) {
657             switch (proto->type) {
658             case NJS_ARRAY:
659                 array = (njs_array_t *) proto;
660                 num = njs_key_to_index(key);
661 
662                 if (njs_fast_path(njs_key_is_integer_index(num, key))) {
663                     ret = njs_array_property_query(vm, pq, array, num);
664                     if (njs_fast_path(ret != NJS_DECLINED)) {
665                         return (ret == NJS_DONE) ? NJS_DECLINED : ret;
666                     }
667                 }
668 
669                 break;
670 
671             case NJS_TYPED_ARRAY:
672                 num = njs_key_to_index(key);
673                 if (njs_fast_path(njs_key_is_integer_index(num, key))) {
674                     tarray = (njs_typed_array_t *) proto;
675                     return njs_typed_array_property_query(vm, pq, tarray, num);
676                 }
677 
678                 if (!isnan(num)) {
679                     return NJS_DECLINED;
680                 }
681 
682                 break;
683 
684             case NJS_OBJECT_VALUE:
685                 ov = (njs_object_value_t *) proto;
686                 if (!njs_is_string(&ov->value)) {
687                     break;
688                 }
689 
690                 num = njs_key_to_index(key);
691                 if (njs_fast_path(njs_key_is_integer_index(num, key))) {
692                     ov = (njs_object_value_t *) proto;
693                     ret = njs_string_property_query(vm, pq, &ov->value, num);
694                     if (njs_fast_path(ret != NJS_DECLINED)) {
695                         return ret;
696                     }
697                 }
698 
699                 break;
700 
701             default:
702                 break;
703             }
704         }
705 
706         ret = njs_lvlhsh_find(&proto->hash, &pq->lhq);
707 
708         if (ret == NJS_OK) {
709             prop = pq->lhq.value;
710 
711             if (prop->type != NJS_WHITEOUT) {
712                 return ret;
713             }
714 
715             if (pq->own) {
716                 pq->own_whiteout = prop;
717             }
718 
719         } else {
720             ret = njs_lvlhsh_find(&proto->shared_hash, &pq->lhq);
721 
722             if (ret == NJS_OK) {
723                 return njs_prop_private_copy(vm, pq);
724             }
725         }
726 
727         if (own) {
728             return NJS_DECLINED;
729         }
730 
731         pq->own = 0;
732         proto = proto->__proto__;
733 
734     } while (proto != NULL);
735 
736     return NJS_DECLINED;
737 }
738 
739 
740 static njs_int_t
njs_array_property_query(njs_vm_t * vm,njs_property_query_t * pq,njs_array_t * array,uint32_t index)741 njs_array_property_query(njs_vm_t *vm, njs_property_query_t *pq,
742     njs_array_t *array, uint32_t index)
743 {
744     int64_t            length;
745     uint64_t           size;
746     njs_int_t          ret;
747     njs_value_t        *setval, value;
748     njs_object_prop_t  *prop;
749 
750     if (pq->query == NJS_PROPERTY_QUERY_SET) {
751         if (!array->object.extensible) {
752             return NJS_DECLINED;
753         }
754 
755         if (njs_fast_path(array->object.fast_array)) {
756             if (njs_fast_path(index < NJS_ARRAY_LARGE_OBJECT_LENGTH)) {
757                 if (index >= array->length) {
758                     size = index - array->length + 1;
759 
760                     ret = njs_array_expand(vm, array, 0, size);
761                     if (njs_slow_path(ret != NJS_OK)) {
762                         return ret;
763                     }
764 
765                     setval = &array->start[array->length];
766 
767                     while (size != 0) {
768                         njs_set_invalid(setval);
769                         setval++;
770                         size--;
771                     }
772 
773                     array->length = index + 1;
774                 }
775 
776                 goto prop;
777             }
778 
779             ret = njs_array_convert_to_slow_array(vm, array);
780             if (njs_slow_path(ret != NJS_OK)) {
781                 return ret;
782             }
783         }
784 
785         njs_set_array(&value, array);
786 
787         ret = njs_object_length(vm, &value, &length);
788         if (njs_slow_path(ret != NJS_OK)) {
789             return ret;
790         }
791 
792         if ((index + 1) > length) {
793             ret = njs_array_length_redefine(vm, &value, index + 1);
794             if (njs_slow_path(ret != NJS_OK)) {
795                 return ret;
796             }
797         }
798 
799         ret = njs_lvlhsh_find(&array->object.hash, &pq->lhq);
800         if (ret == NJS_OK) {
801             prop = pq->lhq.value;
802 
803             if (prop->type != NJS_WHITEOUT) {
804                 return NJS_OK;
805             }
806 
807             if (pq->own) {
808                 pq->own_whiteout = prop;
809             }
810 
811             return NJS_DECLINED;
812         }
813 
814         return NJS_DONE;
815     }
816 
817     if (njs_slow_path(!array->object.fast_array)) {
818         return NJS_DECLINED;
819     }
820 
821     if (index >= array->length) {
822         return NJS_DECLINED;
823     }
824 
825 prop:
826 
827     prop = &pq->scratch;
828 
829     if (pq->query == NJS_PROPERTY_QUERY_GET) {
830         if (!njs_is_valid(&array->start[index])) {
831             return NJS_DECLINED;
832         }
833 
834         prop->value = array->start[index];
835         prop->type = NJS_PROPERTY;
836 
837     } else {
838         prop->value.data.u.value = &array->start[index];
839         prop->type = NJS_PROPERTY_REF;
840     }
841 
842     njs_set_number(&prop->name, index);
843 
844     prop->writable = 1;
845     prop->enumerable = 1;
846     prop->configurable = 1;
847 
848     pq->lhq.value = prop;
849 
850     return NJS_OK;
851 }
852 
853 
854 static njs_int_t
njs_typed_array_property_query(njs_vm_t * vm,njs_property_query_t * pq,njs_typed_array_t * array,uint32_t index)855 njs_typed_array_property_query(njs_vm_t *vm, njs_property_query_t *pq,
856     njs_typed_array_t *array, uint32_t index)
857 {
858     njs_object_prop_t  *prop;
859 
860     if (njs_slow_path(njs_is_detached_buffer(array->buffer))) {
861         njs_type_error(vm, "detached buffer");
862         return NJS_ERROR;
863     }
864 
865     if (index >= njs_typed_array_length(array)) {
866         return NJS_DECLINED;
867     }
868 
869     prop = &pq->scratch;
870 
871     if (pq->query == NJS_PROPERTY_QUERY_GET) {
872         njs_set_number(&prop->value, njs_typed_array_prop(array, index));
873         prop->type = NJS_PROPERTY;
874 
875     } else {
876         prop->value.data.u.typed_array = array;
877         prop->value.data.magic32 = index;
878         prop->type = NJS_PROPERTY_TYPED_ARRAY_REF;
879     }
880 
881     prop->writable = 1;
882     prop->enumerable = 1;
883     prop->configurable = 0;
884 
885     pq->lhq.value = prop;
886 
887     return NJS_OK;
888 }
889 
890 
891 static njs_int_t
njs_string_property_query(njs_vm_t * vm,njs_property_query_t * pq,njs_value_t * object,uint32_t index)892 njs_string_property_query(njs_vm_t *vm, njs_property_query_t *pq,
893     njs_value_t *object, uint32_t index)
894 {
895     njs_slice_prop_t   slice;
896     njs_object_prop_t  *prop;
897     njs_string_prop_t  string;
898 
899     prop = &pq->scratch;
900 
901     slice.start = index;
902     slice.length = 1;
903     slice.string_length = njs_string_prop(&string, object);
904 
905     if (slice.start < slice.string_length) {
906         /*
907          * A single codepoint string fits in retval
908          * so the function cannot fail.
909          */
910         (void) njs_string_slice(vm, &prop->value, &string, &slice);
911 
912         prop->type = NJS_PROPERTY;
913         prop->writable = 0;
914         prop->enumerable = 1;
915         prop->configurable = 0;
916 
917         pq->lhq.value = prop;
918 
919         if (pq->query != NJS_PROPERTY_QUERY_GET) {
920             /* pq->lhq.key is used by NJS_VMCODE_PROPERTY_SET for TypeError */
921             njs_uint32_to_string(&pq->key, index);
922             njs_string_get(&pq->key, &pq->lhq.key);
923         }
924 
925         return NJS_OK;
926     }
927 
928     return NJS_DECLINED;
929 }
930 
931 
932 static njs_int_t
njs_external_property_query(njs_vm_t * vm,njs_property_query_t * pq,njs_value_t * value)933 njs_external_property_query(njs_vm_t *vm, njs_property_query_t *pq,
934     njs_value_t *value)
935 {
936     njs_object_prop_t   *prop;
937     njs_exotic_slots_t  *slots;
938 
939     slots = njs_object_slots(value);
940 
941     if (njs_slow_path(slots->prop_handler == NULL)) {
942         return NJS_DECLINED;
943     }
944 
945     prop = &pq->scratch;
946 
947     njs_memzero(prop, sizeof(njs_object_prop_t));
948 
949     /*
950      * njs_memzero() does also:
951      *   prop->type = NJS_PROPERTY;
952      *   prop->writable = 0;
953      *   prop->configurable = 0;
954      *   njs_set_null(&prop->getter);
955      *   njs_set_null(&prop->setter);
956      */
957 
958     prop->value.data.magic32 = slots->magic32;
959     prop->name = pq->key;
960 
961     pq->lhq.value = prop;
962 
963     prop->writable = slots->writable;
964     prop->configurable = slots->configurable;
965     prop->enumerable = slots->enumerable;
966 
967     switch (pq->query) {
968 
969     case NJS_PROPERTY_QUERY_GET:
970         return slots->prop_handler(vm, prop, value, NULL, &prop->value);
971 
972     case NJS_PROPERTY_QUERY_SET:
973         if (slots->writable == 0) {
974             return NJS_OK;
975         }
976 
977         break;
978 
979     case NJS_PROPERTY_QUERY_DELETE:
980         if (slots->configurable == 0) {
981             return NJS_OK;
982         }
983 
984         break;
985     }
986 
987     prop->type = NJS_PROPERTY_HANDLER;
988     prop->value.data.u.prop_handler = slots->prop_handler;
989 
990     return NJS_OK;
991 }
992 
993 
994 njs_int_t
njs_value_property(njs_vm_t * vm,njs_value_t * value,njs_value_t * key,njs_value_t * retval)995 njs_value_property(njs_vm_t *vm, njs_value_t *value, njs_value_t *key,
996     njs_value_t *retval)
997 {
998     double                num;
999     uint32_t              index;
1000     njs_int_t             ret;
1001     njs_array_t          *array;
1002     njs_object_prop_t     *prop;
1003     njs_typed_array_t     *tarray;
1004     njs_property_query_t  pq;
1005 
1006     if (njs_fast_path(njs_is_number(key))) {
1007         num = njs_number(key);
1008 
1009         if (njs_slow_path(!njs_number_is_integer_index(num))) {
1010             goto slow_path;
1011         }
1012 
1013         index = (uint32_t) num;
1014 
1015         if (njs_is_typed_array(value)) {
1016             tarray = njs_typed_array(value);
1017 
1018             if (njs_slow_path(njs_is_detached_buffer(tarray->buffer))) {
1019                 njs_type_error(vm, "detached buffer");
1020                 return NJS_ERROR;
1021             }
1022 
1023             if (njs_slow_path(index >= njs_typed_array_length(tarray))) {
1024                 goto slow_path;
1025             }
1026 
1027             njs_set_number(retval, njs_typed_array_prop(tarray, index));
1028 
1029             return NJS_OK;
1030         }
1031 
1032         if (njs_slow_path(!(njs_is_object(value)
1033                             && njs_object(value)->fast_array)))
1034         {
1035             goto slow_path;
1036         }
1037 
1038         /* njs_is_fast_array() */
1039 
1040         array = njs_array(value);
1041 
1042         if (njs_slow_path(index >= array->length
1043                           || !njs_is_valid(&array->start[index])))
1044         {
1045             goto slow_path;
1046         }
1047 
1048         *retval = array->start[index];
1049 
1050         return NJS_OK;
1051     }
1052 
1053 slow_path:
1054 
1055     njs_property_query_init(&pq, NJS_PROPERTY_QUERY_GET, 0);
1056 
1057     ret = njs_property_query(vm, &pq, value, key);
1058 
1059     switch (ret) {
1060 
1061     case NJS_OK:
1062         prop = pq.lhq.value;
1063 
1064         switch (prop->type) {
1065 
1066         case NJS_PROPERTY:
1067             if (njs_is_data_descriptor(prop)) {
1068                 *retval = prop->value;
1069                 break;
1070             }
1071 
1072             if (njs_is_undefined(&prop->getter)) {
1073                 njs_set_undefined(retval);
1074                 break;
1075             }
1076 
1077             return njs_function_apply(vm, njs_function(&prop->getter), value,
1078                                       1, retval);
1079 
1080         case NJS_PROPERTY_HANDLER:
1081             pq.scratch = *prop;
1082             prop = &pq.scratch;
1083             ret = prop->value.data.u.prop_handler(vm, prop, value, NULL,
1084                                                   &prop->value);
1085 
1086             if (njs_slow_path(ret != NJS_OK)) {
1087                 if (ret == NJS_ERROR) {
1088                     return ret;
1089                 }
1090 
1091                 njs_set_undefined(&prop->value);
1092             }
1093 
1094             *retval = prop->value;
1095 
1096             break;
1097 
1098         default:
1099             njs_internal_error(vm, "unexpected property type \"%s\" "
1100                                "while getting",
1101                                njs_prop_type_string(prop->type));
1102 
1103             return NJS_ERROR;
1104         }
1105 
1106         break;
1107 
1108     case NJS_DECLINED:
1109         njs_set_undefined(retval);
1110 
1111         return NJS_DECLINED;
1112 
1113     case NJS_ERROR:
1114     default:
1115 
1116         return ret;
1117     }
1118 
1119     return NJS_OK;
1120 }
1121 
1122 
1123 njs_int_t
njs_value_property_set(njs_vm_t * vm,njs_value_t * value,njs_value_t * key,njs_value_t * setval)1124 njs_value_property_set(njs_vm_t *vm, njs_value_t *value, njs_value_t *key,
1125     njs_value_t *setval)
1126 {
1127     double                num;
1128     uint32_t              index;
1129     njs_int_t             ret;
1130     njs_array_t           *array;
1131     njs_object_prop_t     *prop;
1132     njs_typed_array_t     *tarray;
1133     njs_property_query_t  pq;
1134 
1135     static const njs_str_t  length_key = njs_str("length");
1136 
1137     if (njs_fast_path(njs_is_number(key))) {
1138         num = njs_number(key);
1139 
1140         if (njs_slow_path(!njs_number_is_integer_index(num))) {
1141             goto slow_path;
1142         }
1143 
1144         index = (uint32_t) num;
1145 
1146         if (njs_is_typed_array(value)) {
1147             tarray = njs_typed_array(value);
1148 
1149             if (njs_fast_path(index < njs_typed_array_length(tarray))) {
1150                 return njs_typed_array_set_value(vm, tarray, index, setval);
1151             }
1152 
1153             return NJS_OK;
1154         }
1155 
1156         if (njs_slow_path(!(njs_is_object(value)
1157                             && njs_object(value)->fast_array)))
1158         {
1159             goto slow_path;
1160         }
1161 
1162         /* NJS_ARRAY */
1163 
1164         array = njs_array(value);
1165 
1166         if (njs_slow_path(index >= array->length)) {
1167             goto slow_path;
1168         }
1169 
1170         array->start[index] = *setval;
1171 
1172         return NJS_OK;
1173     }
1174 
1175 slow_path:
1176 
1177     if (njs_is_primitive(value)) {
1178         njs_type_error(vm, "property set on primitive %s type",
1179                        njs_type_string(value->type));
1180         return NJS_ERROR;
1181     }
1182 
1183     njs_property_query_init(&pq, NJS_PROPERTY_QUERY_SET, 0);
1184 
1185     ret = njs_property_query(vm, &pq, value, key);
1186 
1187     switch (ret) {
1188 
1189     case NJS_OK:
1190         prop = pq.lhq.value;
1191 
1192         if (njs_is_data_descriptor(prop)) {
1193             if (!prop->writable) {
1194                 njs_key_string_get(vm, &pq.key,  &pq.lhq.key);
1195                 njs_type_error(vm,
1196                              "Cannot assign to read-only property \"%V\" of %s",
1197                                &pq.lhq.key, njs_type_string(value->type));
1198                 return NJS_ERROR;
1199             }
1200 
1201         } else {
1202             if (njs_is_function(&prop->setter)) {
1203                 return njs_function_call(vm, njs_function(&prop->setter),
1204                                          value, setval, 1, &vm->retval);
1205             }
1206 
1207             njs_key_string_get(vm, &pq.key,  &pq.lhq.key);
1208             njs_type_error(vm,
1209                      "Cannot set property \"%V\" of %s which has only a getter",
1210                            &pq.lhq.key, njs_type_string(value->type));
1211             return NJS_ERROR;
1212         }
1213 
1214         if (prop->type == NJS_PROPERTY_HANDLER) {
1215             ret = prop->value.data.u.prop_handler(vm, prop, value, setval,
1216                                                   &vm->retval);
1217             if (njs_slow_path(ret != NJS_DECLINED)) {
1218                 return ret;
1219             }
1220         }
1221 
1222         if (pq.own) {
1223             switch (prop->type) {
1224             case NJS_PROPERTY:
1225                 if (njs_slow_path(pq.lhq.key_hash == NJS_LENGTH_HASH)) {
1226                     if (njs_strstr_eq(&pq.lhq.key, &length_key)) {
1227                         ret = njs_array_length_set(vm, value, prop, setval);
1228                         if (ret != NJS_DECLINED) {
1229                             return ret;
1230                         }
1231                     }
1232                 }
1233 
1234                 goto found;
1235 
1236             case NJS_PROPERTY_REF:
1237                 *prop->value.data.u.value = *setval;
1238                 return NJS_OK;
1239 
1240             case NJS_PROPERTY_TYPED_ARRAY_REF:
1241                 return njs_typed_array_set_value(vm,
1242                                                  njs_typed_array(&prop->value),
1243                                                  prop->value.data.magic32,
1244                                                  setval);
1245 
1246             default:
1247                 njs_internal_error(vm, "unexpected property type \"%s\" "
1248                                    "while setting",
1249                                    njs_prop_type_string(prop->type));
1250 
1251                 return NJS_ERROR;
1252             }
1253 
1254             break;
1255         }
1256 
1257         /* Fall through. */
1258 
1259     case NJS_DECLINED:
1260         if (njs_slow_path(pq.own_whiteout != NULL)) {
1261             /* Previously deleted property. */
1262             if (!njs_object(value)->extensible) {
1263                 goto fail;
1264             }
1265 
1266             prop = pq.own_whiteout;
1267 
1268             prop->type = NJS_PROPERTY;
1269             prop->enumerable = 1;
1270             prop->configurable = 1;
1271             prop->writable = 1;
1272 
1273             goto found;
1274         }
1275 
1276         if (njs_slow_path(pq.own && njs_is_typed_array(value)
1277                           && njs_is_string(key)))
1278         {
1279             /* Integer-Indexed Exotic Objects [[DefineOwnProperty]]. */
1280             if (!isnan(njs_string_to_index(key))) {
1281                 return NJS_OK;
1282             }
1283         }
1284 
1285         break;
1286 
1287     case NJS_ERROR:
1288     default:
1289 
1290         return ret;
1291     }
1292 
1293     if (njs_slow_path(!njs_object(value)->extensible)) {
1294         goto fail;
1295     }
1296 
1297     prop = njs_object_prop_alloc(vm, &pq.key, &njs_value_undefined, 1);
1298     if (njs_slow_path(prop == NULL)) {
1299         return NJS_ERROR;
1300     }
1301 
1302     pq.lhq.replace = 0;
1303     pq.lhq.value = prop;
1304     pq.lhq.pool = vm->mem_pool;
1305 
1306     ret = njs_lvlhsh_insert(njs_object_hash(value), &pq.lhq);
1307     if (njs_slow_path(ret != NJS_OK)) {
1308         njs_internal_error(vm, "lvlhsh insert failed");
1309         return NJS_ERROR;
1310     }
1311 
1312 found:
1313 
1314     prop->value = *setval;
1315 
1316     return NJS_OK;
1317 
1318 fail:
1319 
1320     njs_key_string_get(vm, &pq.key, &pq.lhq.key);
1321     njs_type_error(vm, "Cannot add property \"%V\", object is not extensible",
1322                    &pq.lhq.key);
1323 
1324     return NJS_ERROR;
1325 }
1326 
1327 
1328 njs_int_t
njs_value_property_delete(njs_vm_t * vm,njs_value_t * value,njs_value_t * key,njs_value_t * removed)1329 njs_value_property_delete(njs_vm_t *vm, njs_value_t *value, njs_value_t *key,
1330     njs_value_t *removed)
1331 {
1332     njs_int_t             ret;
1333     njs_object_prop_t     *prop;
1334     njs_property_query_t  pq;
1335 
1336     njs_property_query_init(&pq, NJS_PROPERTY_QUERY_DELETE, 1);
1337 
1338     ret = njs_property_query(vm, &pq, value, key);
1339     if (njs_slow_path(ret != NJS_OK)) {
1340         return ret;
1341     }
1342 
1343     prop = pq.lhq.value;
1344 
1345     if (njs_slow_path(!prop->configurable)) {
1346         njs_key_string_get(vm, &pq.key,  &pq.lhq.key);
1347         njs_type_error(vm, "Cannot delete property \"%V\" of %s",
1348                        &pq.lhq.key, njs_type_string(value->type));
1349         return NJS_ERROR;
1350     }
1351 
1352     switch (prop->type) {
1353     case NJS_PROPERTY_HANDLER:
1354         if (njs_is_object(value) && njs_object_slots(value) != NULL) {
1355             ret = prop->value.data.u.prop_handler(vm, prop, value, NULL, NULL);
1356             if (njs_slow_path(ret != NJS_DECLINED)) {
1357                 return ret;
1358             }
1359         }
1360 
1361         /* Fall through. */
1362 
1363     case NJS_PROPERTY:
1364         if (njs_is_data_descriptor(prop) || removed == NULL) {
1365             break;
1366         }
1367 
1368         if (njs_is_undefined(&prop->getter)) {
1369             njs_set_undefined(removed);
1370             break;
1371         }
1372 
1373         return njs_function_apply(vm, njs_function(&prop->getter), value,
1374                                   1, removed);
1375 
1376     case NJS_PROPERTY_REF:
1377         if (removed != NULL) {
1378             *removed = *prop->value.data.u.value;
1379         }
1380 
1381         njs_set_invalid(prop->value.data.u.value);
1382         return NJS_OK;
1383 
1384     default:
1385         njs_internal_error(vm, "unexpected property type \"%s\" "
1386                            "while deleting", njs_prop_type_string(prop->type));
1387         return NJS_ERROR;
1388     }
1389 
1390     /* GC: release value. */
1391     if (removed != NULL) {
1392         *removed = prop->value;
1393     }
1394 
1395     prop->type = NJS_WHITEOUT;
1396     njs_set_invalid(&prop->value);
1397 
1398     return NJS_OK;
1399 }
1400 
1401 
1402 njs_int_t
njs_primitive_value_to_string(njs_vm_t * vm,njs_value_t * dst,const njs_value_t * src)1403 njs_primitive_value_to_string(njs_vm_t *vm, njs_value_t *dst,
1404     const njs_value_t *src)
1405 {
1406     const njs_value_t  *value;
1407 
1408     switch (src->type) {
1409 
1410     case NJS_NULL:
1411         value = &njs_string_null;
1412         break;
1413 
1414     case NJS_UNDEFINED:
1415         value = &njs_string_undefined;
1416         break;
1417 
1418     case NJS_BOOLEAN:
1419         value = njs_is_true(src) ? &njs_string_true : &njs_string_false;
1420         break;
1421 
1422     case NJS_NUMBER:
1423         return njs_number_to_string(vm, dst, src);
1424 
1425     case NJS_SYMBOL:
1426         njs_symbol_conversion_failed(vm, 1);
1427         return NJS_ERROR;
1428 
1429     case NJS_STRING:
1430         /* GC: njs_retain(src); */
1431         value = src;
1432         break;
1433 
1434     default:
1435         return NJS_ERROR;
1436     }
1437 
1438     *dst = *value;
1439 
1440     return NJS_OK;
1441 }
1442 
1443 
1444 njs_int_t
njs_primitive_value_to_chain(njs_vm_t * vm,njs_chb_t * chain,const njs_value_t * src)1445 njs_primitive_value_to_chain(njs_vm_t *vm, njs_chb_t *chain,
1446     const njs_value_t *src)
1447 {
1448     njs_string_prop_t  string;
1449 
1450     switch (src->type) {
1451 
1452     case NJS_NULL:
1453         njs_chb_append_literal(chain, "null");
1454         return njs_length("null");
1455 
1456     case NJS_UNDEFINED:
1457         njs_chb_append_literal(chain, "undefined");
1458         return njs_length("undefined");
1459 
1460     case NJS_BOOLEAN:
1461         if (njs_is_true(src)) {
1462             njs_chb_append_literal(chain, "true");
1463             return njs_length("true");
1464 
1465         } else {
1466             njs_chb_append_literal(chain, "false");
1467             return njs_length("false");
1468         }
1469 
1470     case NJS_NUMBER:
1471         return njs_number_to_chain(vm, chain, njs_number(src));
1472 
1473     case NJS_SYMBOL:
1474         njs_symbol_conversion_failed(vm, 1);
1475         return NJS_ERROR;
1476 
1477     case NJS_STRING:
1478         (void) njs_string_prop(&string, src);
1479         njs_chb_append(chain, string.start, string.size);
1480         return string.length;
1481 
1482     default:
1483         return NJS_ERROR;
1484     }
1485 }
1486 
1487 
1488 njs_int_t
njs_value_to_object(njs_vm_t * vm,njs_value_t * value)1489 njs_value_to_object(njs_vm_t *vm, njs_value_t *value)
1490 {
1491     njs_uint_t          index;
1492     njs_object_value_t  *object;
1493 
1494     if (njs_slow_path(njs_is_null_or_undefined(value))) {
1495         njs_type_error(vm, "cannot convert null or undefined to object");
1496         return NJS_ERROR;
1497     }
1498 
1499     if (njs_fast_path(njs_is_object(value))) {
1500         return NJS_OK;
1501     }
1502 
1503     if (njs_is_primitive(value)) {
1504         index = njs_primitive_prototype_index(value->type);
1505         object = njs_object_value_alloc(vm, index, 0, value);
1506         if (njs_slow_path(object == NULL)) {
1507             return NJS_ERROR;
1508         }
1509 
1510         njs_set_object_value(value, object);
1511 
1512         return NJS_OK;
1513     }
1514 
1515     njs_type_error(vm, "cannot convert %s to object",
1516                    njs_type_string(value->type));
1517 
1518     return NJS_ERROR;
1519 }
1520 
1521 
1522 void
njs_symbol_conversion_failed(njs_vm_t * vm,njs_bool_t to_string)1523 njs_symbol_conversion_failed(njs_vm_t *vm, njs_bool_t to_string)
1524 {
1525     njs_type_error(vm, to_string
1526         ? "Cannot convert a Symbol value to a string"
1527         : "Cannot convert a Symbol value to a number");
1528 }
1529 
1530 
1531 njs_int_t
njs_value_species_constructor(njs_vm_t * vm,njs_value_t * object,njs_value_t * default_constructor,njs_value_t * dst)1532 njs_value_species_constructor(njs_vm_t *vm, njs_value_t *object,
1533     njs_value_t *default_constructor, njs_value_t *dst)
1534 {
1535     njs_int_t    ret;
1536     njs_value_t  constructor, retval;
1537 
1538     static const njs_value_t  string_constructor = njs_string("constructor");
1539     static const njs_value_t  string_species =
1540                                 njs_wellknown_symbol(NJS_SYMBOL_SPECIES);
1541 
1542     ret = njs_value_property(vm, object, njs_value_arg(&string_constructor),
1543                              &constructor);
1544     if (njs_slow_path(ret == NJS_ERROR)) {
1545         return NJS_ERROR;
1546     }
1547 
1548     if (njs_is_undefined(&constructor)) {
1549         goto default_constructor;
1550     }
1551 
1552     if (njs_slow_path(!njs_is_object(&constructor))) {
1553         njs_type_error(vm, "constructor is not object");
1554         return NJS_ERROR;
1555     }
1556 
1557     ret = njs_value_property(vm, &constructor, njs_value_arg(&string_species),
1558                              &retval);
1559     if (njs_slow_path(ret == NJS_ERROR)) {
1560         return NJS_ERROR;
1561     }
1562 
1563     if (njs_value_is_null_or_undefined(&retval)) {
1564         goto default_constructor;
1565     }
1566 
1567     if (!njs_is_function(&retval)) {
1568         njs_type_error(vm, "object does not contain a constructor");
1569         return NJS_ERROR;
1570     }
1571 
1572     *dst = retval;
1573 
1574     return NJS_OK;
1575 
1576 default_constructor:
1577 
1578     *dst = *default_constructor;
1579 
1580     return NJS_OK;
1581 }
1582 
1583 
1584 njs_int_t
njs_value_method(njs_vm_t * vm,njs_value_t * value,njs_value_t * key,njs_value_t * retval)1585 njs_value_method(njs_vm_t *vm, njs_value_t *value, njs_value_t *key,
1586     njs_value_t *retval)
1587 {
1588     njs_int_t  ret;
1589 
1590     ret = njs_value_to_object(vm, value);
1591     if (njs_slow_path(ret != NJS_OK)) {
1592         return ret;
1593     }
1594 
1595     ret = njs_value_property(vm, value, key, retval);
1596     if (njs_slow_path(ret != NJS_OK)) {
1597         return (ret == NJS_DECLINED) ? NJS_OK : ret;
1598     }
1599 
1600     if (njs_slow_path(!njs_is_function(retval))) {
1601         njs_type_error(vm, "method is not callable");
1602         return NJS_ERROR;
1603     }
1604 
1605     return NJS_OK;
1606 }
1607