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 = ∝
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