1
2 /*
3 * Copyright (C) Igor Sysoev
4 * Copyright (C) NGINX, Inc.
5 */
6
7
8 #include <njs_main.h>
9
10
11 njs_function_t *
njs_function_alloc(njs_vm_t * vm,njs_function_lambda_t * lambda,njs_bool_t async)12 njs_function_alloc(njs_vm_t *vm, njs_function_lambda_t *lambda,
13 njs_bool_t async)
14 {
15 size_t size;
16 njs_object_t *proto;
17 njs_function_t *function;
18
19 size = sizeof(njs_function_t) + lambda->nclosures * sizeof(njs_value_t *);
20
21 function = njs_mp_zalloc(vm->mem_pool, size);
22 if (njs_slow_path(function == NULL)) {
23 goto fail;
24 }
25
26 /*
27 * njs_mp_zalloc() does also:
28 * njs_lvlhsh_init(&function->object.hash);
29 * function->object.__proto__ = NULL;
30 */
31
32 function->ctor = lambda->ctor;
33 function->args_offset = 1;
34 function->u.lambda = lambda;
35
36 if (function->ctor) {
37 function->object.shared_hash = vm->shared->function_instance_hash;
38
39 } else if (async) {
40 function->object.shared_hash = vm->shared->async_function_instance_hash;
41
42 } else {
43 function->object.shared_hash = vm->shared->arrow_instance_hash;
44 }
45
46 if (async) {
47 proto = &vm->prototypes[NJS_OBJ_TYPE_ASYNC_FUNCTION].object;
48
49 } else {
50 proto = &vm->prototypes[NJS_OBJ_TYPE_FUNCTION].object;
51 }
52
53 function->object.__proto__ = proto;
54 function->object.type = NJS_FUNCTION;
55
56 function->object.extensible = 1;
57
58 return function;
59
60 fail:
61
62 njs_memory_error(vm);
63
64 return NULL;
65 }
66
67
68 njs_function_t *
njs_vm_function_alloc(njs_vm_t * vm,njs_function_native_t native)69 njs_vm_function_alloc(njs_vm_t *vm, njs_function_native_t native)
70 {
71 njs_function_t *function;
72
73 function = njs_mp_zalloc(vm->mem_pool, sizeof(njs_function_t));
74 if (njs_slow_path(function == NULL)) {
75 njs_memory_error(vm);
76 return NULL;
77 }
78
79 function->native = 1;
80 function->args_offset = 1;
81 function->u.native = native;
82
83 return function;
84 }
85
86
87 njs_function_t *
njs_function_value_copy(njs_vm_t * vm,njs_value_t * value)88 njs_function_value_copy(njs_vm_t *vm, njs_value_t *value)
89 {
90 njs_function_t *function, *copy;
91 njs_object_type_t type;
92
93 function = njs_function(value);
94
95 if (!function->object.shared) {
96 return function;
97 }
98
99 copy = njs_function_copy(vm, function);
100 if (njs_slow_path(copy == NULL)) {
101 njs_memory_error(vm);
102 return NULL;
103 }
104
105 type = njs_function_object_type(vm, function);
106
107 if (copy->ctor) {
108 copy->object.shared_hash = vm->shared->function_instance_hash;
109
110 } else if (type == NJS_OBJ_TYPE_ASYNC_FUNCTION) {
111 copy->object.shared_hash = vm->shared->async_function_instance_hash;
112
113 } else {
114 copy->object.shared_hash = vm->shared->arrow_instance_hash;
115 }
116
117 value->data.u.function = copy;
118
119 return copy;
120 }
121
122
123 njs_int_t
njs_function_name_set(njs_vm_t * vm,njs_function_t * function,njs_value_t * name,const char * prefix)124 njs_function_name_set(njs_vm_t *vm, njs_function_t *function,
125 njs_value_t *name, const char *prefix)
126 {
127 u_char *p;
128 size_t len, symbol;
129 njs_int_t ret;
130 njs_value_t value;
131 njs_string_prop_t string;
132 njs_object_prop_t *prop;
133 njs_lvlhsh_query_t lhq;
134
135 prop = njs_object_prop_alloc(vm, &njs_string_name, name, 0);
136 if (njs_slow_path(prop == NULL)) {
137 return NJS_ERROR;
138 }
139
140 symbol = 0;
141
142 if (njs_is_symbol(&prop->value)) {
143 symbol = 2;
144 prop->value = *njs_symbol_description(&prop->value);
145 }
146
147 if (prefix != NULL || symbol != 0) {
148 value = prop->value;
149 (void) njs_string_prop(&string, &value);
150
151 len = (prefix != NULL) ? njs_strlen(prefix) + 1: 0;
152 p = njs_string_alloc(vm, &prop->value, string.size + len + symbol,
153 string.length + len + symbol);
154 if (njs_slow_path(p == NULL)) {
155 return NJS_ERROR;
156 }
157
158 if (len != 0) {
159 p = njs_cpymem(p, prefix, len - 1);
160 *p++ = ' ';
161 }
162
163 if (symbol != 0) {
164 *p++ = '[';
165 }
166
167 p = njs_cpymem(p, string.start, string.size);
168
169 if (symbol != 0) {
170 *p++ = ']';
171 }
172 }
173
174 prop->configurable = 1;
175
176 lhq.key_hash = NJS_NAME_HASH;
177 lhq.key = njs_str_value("name");
178 lhq.replace = 0;
179 lhq.value = prop;
180 lhq.proto = &njs_object_hash_proto;
181 lhq.pool = vm->mem_pool;
182
183 ret = njs_lvlhsh_insert(&function->object.hash, &lhq);
184 if (njs_slow_path(ret != NJS_OK)) {
185 njs_internal_error(vm, "lvlhsh insert failed");
186 return NJS_ERROR;
187 }
188
189 return NJS_OK;
190 }
191
192
193 njs_function_t *
njs_function_copy(njs_vm_t * vm,njs_function_t * function)194 njs_function_copy(njs_vm_t *vm, njs_function_t *function)
195 {
196 size_t size, n;
197 njs_value_t **from, **to;
198 njs_function_t *copy;
199 njs_object_type_t type;
200
201 n = (function->native) ? 0 : function->u.lambda->nclosures;
202
203 size = sizeof(njs_function_t) + n * sizeof(njs_value_t *);
204
205 copy = njs_mp_alloc(vm->mem_pool, size);
206 if (njs_slow_path(copy == NULL)) {
207 return NULL;
208 }
209
210 *copy = *function;
211
212 type = njs_function_object_type(vm, function);
213
214 copy->object.__proto__ = &vm->prototypes[type].object;
215 copy->object.shared = 0;
216
217 if (n == 0) {
218 return copy;
219 }
220
221 from = njs_function_closures(function);
222 to = njs_function_closures(copy);
223
224 do {
225 n--;
226
227 to[n] = from[n];
228
229 } while (n != 0);
230
231 return copy;
232 }
233
234
235 njs_int_t
njs_function_arguments_object_init(njs_vm_t * vm,njs_native_frame_t * frame)236 njs_function_arguments_object_init(njs_vm_t *vm, njs_native_frame_t *frame)
237 {
238 njs_int_t ret;
239 njs_uint_t nargs, n;
240 njs_value_t value;
241 njs_object_t *arguments;
242 njs_object_prop_t *prop;
243 njs_lvlhsh_query_t lhq;
244
245 static const njs_value_t njs_string_length = njs_string("length");
246
247 arguments = njs_object_alloc(vm);
248 if (njs_slow_path(arguments == NULL)) {
249 return NJS_ERROR;
250 }
251
252 arguments->shared_hash = vm->shared->arguments_object_instance_hash;
253
254 nargs = frame->nargs;
255
256 njs_set_number(&value, nargs);
257
258 prop = njs_object_prop_alloc(vm, &njs_string_length, &value, 1);
259 if (njs_slow_path(prop == NULL)) {
260 return NJS_ERROR;
261 }
262
263 prop->enumerable = 0;
264
265 lhq.value = prop;
266 lhq.key_hash = NJS_LENGTH_HASH;
267 njs_string_get(&prop->name, &lhq.key);
268
269 lhq.replace = 0;
270 lhq.pool = vm->mem_pool;
271 lhq.proto = &njs_object_hash_proto;
272
273 ret = njs_lvlhsh_insert(&arguments->hash, &lhq);
274 if (njs_slow_path(ret != NJS_OK)) {
275 njs_internal_error(vm, "lvlhsh insert failed");
276 return NJS_ERROR;
277 }
278
279 for (n = 0; n < nargs; n++) {
280 njs_uint32_to_string(&value, n);
281
282 prop = njs_object_prop_alloc(vm, &value, &frame->arguments[n], 1);
283 if (njs_slow_path(prop == NULL)) {
284 return NJS_ERROR;
285 }
286
287 lhq.value = prop;
288 njs_string_get(&prop->name, &lhq.key);
289 lhq.key_hash = njs_djb_hash(lhq.key.start, lhq.key.length);
290
291 ret = njs_lvlhsh_insert(&arguments->hash, &lhq);
292 if (njs_slow_path(ret != NJS_OK)) {
293 njs_internal_error(vm, "lvlhsh insert failed");
294 return NJS_ERROR;
295 }
296 }
297
298 frame->arguments_object = arguments;
299
300 return NJS_OK;
301 }
302
303
304 njs_int_t
njs_function_rest_parameters_init(njs_vm_t * vm,njs_native_frame_t * frame)305 njs_function_rest_parameters_init(njs_vm_t *vm, njs_native_frame_t *frame)
306 {
307 uint32_t length;
308 njs_uint_t nargs, n, i;
309 njs_array_t *array;
310 njs_value_t *rest_arguments;
311
312 nargs = frame->nargs;
313 n = frame->function->u.lambda->nargs;
314 length = (nargs >= n) ? (nargs - n + 1) : 0;
315
316 array = njs_array_alloc(vm, 1, length, 0);
317 if (njs_slow_path(array == NULL)) {
318 return NJS_ERROR;
319 }
320
321 for (i = 0; i < length; i++) {
322 array->start[i] = frame->arguments[i + n - 1];
323 }
324
325 rest_arguments = njs_mp_alloc(vm->mem_pool, sizeof(njs_value_t));
326 if (njs_slow_path(rest_arguments == NULL)) {
327 return NJS_ERROR;
328 }
329
330 /* GC: retain. */
331 njs_set_array(rest_arguments, array);
332
333 vm->top_frame->local[n] = rest_arguments;
334
335 return NJS_OK;
336 }
337
338
339 static njs_int_t
njs_function_prototype_thrower(njs_vm_t * vm,njs_value_t * args,njs_uint_t nargs,njs_index_t unused)340 njs_function_prototype_thrower(njs_vm_t *vm, njs_value_t *args,
341 njs_uint_t nargs, njs_index_t unused)
342 {
343 njs_type_error(vm, "\"caller\", \"callee\", \"arguments\" "
344 "properties may not be accessed");
345 return NJS_ERROR;
346 }
347
348
349 const njs_object_prop_t njs_arguments_object_instance_properties[] =
350 {
351 {
352 .type = NJS_PROPERTY,
353 .name = njs_string("callee"),
354 .value = njs_value(NJS_INVALID, 1, NAN),
355 .getter = njs_native_function(njs_function_prototype_thrower, 0),
356 .setter = njs_native_function(njs_function_prototype_thrower, 0),
357 .writable = NJS_ATTRIBUTE_UNSET,
358 },
359 };
360
361
362 const njs_object_init_t njs_arguments_object_instance_init = {
363 njs_arguments_object_instance_properties,
364 njs_nitems(njs_arguments_object_instance_properties),
365 };
366
367
368 njs_int_t
njs_function_native_frame(njs_vm_t * vm,njs_function_t * function,const njs_value_t * this,const njs_value_t * args,njs_uint_t nargs,njs_bool_t ctor)369 njs_function_native_frame(njs_vm_t *vm, njs_function_t *function,
370 const njs_value_t *this, const njs_value_t *args, njs_uint_t nargs,
371 njs_bool_t ctor)
372 {
373 size_t size;
374 njs_uint_t n;
375 njs_value_t *value, *bound;
376 njs_native_frame_t *frame;
377
378 size = NJS_NATIVE_FRAME_SIZE
379 + (function->args_offset + nargs) * sizeof(njs_value_t);
380
381 frame = njs_function_frame_alloc(vm, size);
382 if (njs_slow_path(frame == NULL)) {
383 return NJS_ERROR;
384 }
385
386 frame->function = function;
387 frame->nargs = function->args_offset + nargs;
388 frame->ctor = ctor;
389 frame->native = 1;
390 frame->pc = NULL;
391
392 value = (njs_value_t *) ((u_char *) frame + NJS_NATIVE_FRAME_SIZE);
393
394 frame->arguments = value;
395 frame->arguments_offset = value + function->args_offset;
396
397 bound = function->bound;
398
399 if (bound == NULL) {
400 /* GC: njs_retain(this); */
401 *value++ = *this;
402
403 } else {
404 n = function->args_offset;
405
406 do {
407 /* GC: njs_retain(bound); */
408 *value++ = *bound++;
409 n--;
410 } while (n != 0);
411 }
412
413 if (args != NULL) {
414 memcpy(value, args, nargs * sizeof(njs_value_t));
415 }
416
417 return NJS_OK;
418 }
419
420
421 njs_int_t
njs_function_lambda_frame(njs_vm_t * vm,njs_function_t * function,const njs_value_t * this,const njs_value_t * args,njs_uint_t nargs,njs_bool_t ctor)422 njs_function_lambda_frame(njs_vm_t *vm, njs_function_t *function,
423 const njs_value_t *this, const njs_value_t *args, njs_uint_t nargs,
424 njs_bool_t ctor)
425 {
426 size_t n, frame_size;
427 uint32_t args_count, value_count, value_size, temp_size;
428 njs_value_t *value, *bound, **new, **temp;
429 njs_frame_t *frame;
430 njs_function_t *target;
431 njs_native_frame_t *native_frame;
432 njs_function_lambda_t *lambda;
433
434 bound = function->bound;
435
436 if (njs_fast_path(bound == NULL)) {
437 lambda = function->u.lambda;
438 target = function;
439
440 } else {
441 target = function->u.bound_target;
442
443 if (njs_slow_path(target->bound != NULL)) {
444
445 /*
446 * FIXME: bound functions should call target function with
447 * bound "this" and bound args.
448 */
449
450 njs_internal_error(vm, "chain of bound function are not supported");
451 return NJS_ERROR;
452 }
453
454 lambda = target->u.lambda;
455 }
456
457 args_count = function->args_offset + njs_max(nargs, lambda->nargs);
458 value_count = args_count + njs_max(args_count, lambda->nlocal);
459
460 value_size = value_count * sizeof(njs_value_t *);
461 temp_size = lambda->temp * sizeof(njs_value_t *);
462
463 frame_size = value_size + temp_size
464 + ((value_count + lambda->temp) * sizeof(njs_value_t));
465
466 native_frame = njs_function_frame_alloc(vm, NJS_FRAME_SIZE + frame_size);
467 if (njs_slow_path(native_frame == NULL)) {
468 return NJS_ERROR;
469 }
470
471 /* Local */
472
473 new = (njs_value_t **) ((u_char *) native_frame + NJS_FRAME_SIZE);
474 value = (njs_value_t *) ((u_char *) new + value_size + temp_size);
475
476 n = value_count + lambda->temp;
477
478 while (n != 0) {
479 n--;
480 new[n] = &value[n];
481 njs_set_invalid(new[n]);
482 }
483
484 /* Temp */
485
486 temp = (njs_value_t **) ((u_char *) native_frame + NJS_FRAME_SIZE
487 + value_size);
488
489 native_frame->arguments = value;
490 native_frame->arguments_offset = value + (function->args_offset - 1);
491 native_frame->local = new + args_count;
492 native_frame->temp = temp;
493 native_frame->function = target;
494 native_frame->nargs = nargs;
495 native_frame->ctor = ctor;
496 native_frame->native = 0;
497 native_frame->pc = NULL;
498
499 /* Set this and bound arguments. */
500 *native_frame->local[0] = *this;
501
502 if (njs_slow_path(function->global_this
503 && njs_is_null_or_undefined(this)))
504 {
505 njs_set_object(native_frame->local[0], &vm->global_object);
506 }
507
508 if (bound != NULL) {
509 n = function->args_offset;
510 native_frame->nargs += n - 1;
511
512 if (!ctor) {
513 *native_frame->local[0] = *bound;
514 }
515
516 bound++;
517 n--;
518
519 while (n != 0) {
520 *value++ = *bound++;
521 n--;
522 };
523 }
524
525 /* Copy arguments. */
526
527 if (args != NULL) {
528 while (nargs != 0) {
529 *value++ = *args++;
530 nargs--;
531 }
532 }
533
534 frame = (njs_frame_t *) native_frame;
535 frame->exception.catch = NULL;
536 frame->exception.next = NULL;
537 frame->previous_active_frame = vm->active_frame;
538
539 return NJS_OK;
540 }
541
542
543 njs_native_frame_t *
njs_function_frame_alloc(njs_vm_t * vm,size_t size)544 njs_function_frame_alloc(njs_vm_t *vm, size_t size)
545 {
546 size_t spare_size, chunk_size;
547 njs_native_frame_t *frame;
548
549 spare_size = vm->top_frame ? vm->top_frame->free_size : 0;
550
551 if (njs_fast_path(size <= spare_size)) {
552 frame = (njs_native_frame_t *) vm->top_frame->free;
553 chunk_size = 0;
554
555 } else {
556 spare_size = size + NJS_FRAME_SPARE_SIZE;
557 spare_size = njs_align_size(spare_size, NJS_FRAME_SPARE_SIZE);
558
559 if (vm->stack_size + spare_size > NJS_MAX_STACK_SIZE) {
560 njs_range_error(vm, "Maximum call stack size exceeded");
561 return NULL;
562 }
563
564 frame = njs_mp_align(vm->mem_pool, sizeof(njs_value_t), spare_size);
565 if (njs_slow_path(frame == NULL)) {
566 njs_memory_error(vm);
567 return NULL;
568 }
569
570 chunk_size = spare_size;
571 vm->stack_size += spare_size;
572 }
573
574 njs_memzero(frame, sizeof(njs_native_frame_t));
575
576 frame->size = chunk_size;
577 frame->free_size = spare_size - size;
578 frame->free = (u_char *) frame + size;
579
580 frame->previous = vm->top_frame;
581 vm->top_frame = frame;
582
583 return frame;
584 }
585
586
587 njs_int_t
njs_function_call2(njs_vm_t * vm,njs_function_t * function,const njs_value_t * this,const njs_value_t * args,njs_uint_t nargs,njs_value_t * retval,njs_bool_t ctor)588 njs_function_call2(njs_vm_t *vm, njs_function_t *function,
589 const njs_value_t *this, const njs_value_t *args,
590 njs_uint_t nargs, njs_value_t *retval, njs_bool_t ctor)
591 {
592 njs_int_t ret;
593 njs_value_t dst njs_aligned(16);
594
595 ret = njs_function_frame(vm, function, this, args, nargs, ctor);
596 if (njs_slow_path(ret != NJS_OK)) {
597 return ret;
598 }
599
600 ret = njs_function_frame_invoke(vm, &dst);
601
602 if (ret == NJS_OK) {
603 *retval = dst;
604 }
605
606 return ret;
607 }
608
609
610 njs_int_t
njs_function_lambda_call(njs_vm_t * vm)611 njs_function_lambda_call(njs_vm_t *vm)
612 {
613 uint32_t n;
614 njs_int_t ret;
615 njs_frame_t *frame;
616 njs_value_t *args, **local, *value;
617 njs_value_t **cur_local, **cur_closures, **cur_temp;
618 njs_function_t *function;
619 njs_declaration_t *declr;
620 njs_function_lambda_t *lambda;
621
622 frame = (njs_frame_t *) vm->top_frame;
623 function = frame->native.function;
624
625 if (function->global && !function->closure_copied) {
626 ret = njs_function_capture_global_closures(vm, function);
627 if (njs_slow_path(ret != NJS_OK)) {
628 return NJS_ERROR;
629 }
630 }
631
632 lambda = function->u.lambda;
633
634 args = vm->top_frame->arguments;
635 local = vm->top_frame->local + function->args_offset;
636
637 /* Move all arguments. */
638
639 for (n = 0; n < function->args_count; n++) {
640 if (!njs_is_valid(args)) {
641 njs_set_undefined(args);
642 }
643
644 *local++ = args++;
645 }
646
647 /* Store current level. */
648
649 cur_local = vm->levels[NJS_LEVEL_LOCAL];
650 cur_closures = vm->levels[NJS_LEVEL_CLOSURE];
651 cur_temp = vm->levels[NJS_LEVEL_TEMP];
652
653 /* Replace current level. */
654
655 vm->levels[NJS_LEVEL_LOCAL] = vm->top_frame->local;
656 vm->levels[NJS_LEVEL_CLOSURE] = njs_function_closures(function);
657 vm->levels[NJS_LEVEL_TEMP] = frame->native.temp;
658
659 if (lambda->rest_parameters) {
660 ret = njs_function_rest_parameters_init(vm, &frame->native);
661 if (njs_slow_path(ret != NJS_OK)) {
662 return NJS_ERROR;
663 }
664 }
665
666 /* Self */
667
668 if (lambda->self != NJS_INDEX_NONE) {
669 value = njs_scope_value(vm, lambda->self);
670
671 if (!njs_is_valid(value)) {
672 njs_set_function(value, function);
673 }
674 }
675
676 vm->active_frame = frame;
677
678 /* Closures */
679
680 n = lambda->ndeclarations;
681
682 while (n != 0) {
683 n--;
684
685 declr = &lambda->declarations[n];
686 value = njs_scope_value(vm, declr->index);
687
688 *value = *declr->value;
689
690 function = njs_function_value_copy(vm, value);
691 if (njs_slow_path(function == NULL)) {
692 return NJS_ERROR;
693 }
694
695 ret = njs_function_capture_closure(vm, function, function->u.lambda);
696 if (njs_slow_path(ret != NJS_OK)) {
697 return ret;
698 }
699 }
700
701 ret = njs_vmcode_interpreter(vm, lambda->start);
702
703 /* Restore current level. */
704 vm->levels[NJS_LEVEL_LOCAL] = cur_local;
705 vm->levels[NJS_LEVEL_CLOSURE] = cur_closures;
706 vm->levels[NJS_LEVEL_TEMP] = cur_temp;
707
708 return ret;
709 }
710
711
712 njs_int_t
njs_function_native_call(njs_vm_t * vm)713 njs_function_native_call(njs_vm_t *vm)
714 {
715 njs_int_t ret;
716 njs_function_t *function, *target;
717 njs_native_frame_t *native, *previous;
718 njs_function_native_t call;
719
720 native = vm->top_frame;
721 function = native->function;
722
723 if (njs_fast_path(function->bound == NULL)) {
724 call = function->u.native;
725
726 } else {
727 target = function->u.bound_target;
728
729 if (njs_slow_path(target->bound != NULL)) {
730 njs_internal_error(vm, "chain of bound function are not supported");
731 return NJS_ERROR;
732 }
733
734 call = target->u.native;
735 }
736
737 ret = call(vm, native->arguments, native->nargs, function->magic8);
738 if (njs_slow_path(ret == NJS_ERROR)) {
739 return ret;
740 }
741
742 if (ret == NJS_DECLINED) {
743 return NJS_OK;
744 }
745
746 previous = njs_function_previous_frame(native);
747
748 njs_vm_scopes_restore(vm, native, previous);
749
750 if (!native->skip) {
751 *native->retval = vm->retval;
752 }
753
754 njs_function_frame_free(vm, native);
755
756 return NJS_OK;
757 }
758
759
760 njs_int_t
njs_function_frame_invoke(njs_vm_t * vm,njs_value_t * retval)761 njs_function_frame_invoke(njs_vm_t *vm, njs_value_t *retval)
762 {
763 njs_native_frame_t *frame;
764
765 frame = vm->top_frame;
766 frame->retval = retval;
767
768 if (njs_function_object_type(vm, frame->function)
769 == NJS_OBJ_TYPE_ASYNC_FUNCTION)
770 {
771 return njs_async_function_frame_invoke(vm, retval);
772 }
773
774 if (frame->native) {
775 return njs_function_native_call(vm);
776
777 } else {
778 return njs_function_lambda_call(vm);
779 }
780 }
781
782
783 void
njs_function_frame_free(njs_vm_t * vm,njs_native_frame_t * native)784 njs_function_frame_free(njs_vm_t *vm, njs_native_frame_t *native)
785 {
786 njs_native_frame_t *previous;
787
788 do {
789 previous = native->previous;
790
791 /* GC: free frame->local, etc. */
792
793 if (native->size != 0) {
794 vm->stack_size -= native->size;
795 njs_mp_free(vm->mem_pool, native);
796 }
797
798 native = previous;
799 } while (native->skip);
800 }
801
802
803 njs_int_t
njs_function_frame_save(njs_vm_t * vm,njs_frame_t * frame,u_char * pc)804 njs_function_frame_save(njs_vm_t *vm, njs_frame_t *frame, u_char *pc)
805 {
806 size_t value_count, n;
807 njs_value_t *start, *end, *p, **new, *value, **local;
808 njs_function_t *function;
809 njs_native_frame_t *active, *native;
810
811 *frame = *vm->active_frame;
812 frame->previous_active_frame = NULL;
813
814 native = &frame->native;
815
816 active = &vm->active_frame->native;
817 value_count = njs_function_frame_value_count(active);
818
819 function = active->function;
820
821 new = (njs_value_t **) ((u_char *) native + NJS_FRAME_SIZE);
822 value = (njs_value_t *) (new + value_count
823 + function->u.lambda->temp);
824
825
826 native->arguments = value;
827 native->arguments_offset = value + (function->args_offset - 1);
828 native->local = new + njs_function_frame_args_count(active);
829 native->temp = new + value_count;
830 native->pc = pc;
831
832 start = njs_function_frame_values(active, &end);
833 p = native->arguments;
834
835 while (start < end) {
836 *p = *start++;
837 *new++ = p++;
838 }
839
840 /* Move all arguments. */
841
842 p = native->arguments;
843 local = native->local + function->args_offset;
844
845 for (n = 0; n < function->args_count; n++) {
846 if (!njs_is_valid(p)) {
847 njs_set_undefined(p);
848 }
849
850 *local++ = p++;
851 }
852
853 return NJS_OK;
854 }
855
856
857 njs_object_type_t
njs_function_object_type(njs_vm_t * vm,njs_function_t * function)858 njs_function_object_type(njs_vm_t *vm, njs_function_t *function)
859 {
860 if (function->object.shared_hash.slot
861 == vm->shared->async_function_instance_hash.slot)
862 {
863 return NJS_OBJ_TYPE_ASYNC_FUNCTION;
864 }
865
866 return NJS_OBJ_TYPE_FUNCTION;
867 }
868
869
870 njs_int_t
njs_function_capture_closure(njs_vm_t * vm,njs_function_t * function,njs_function_lambda_t * lambda)871 njs_function_capture_closure(njs_vm_t *vm, njs_function_t *function,
872 njs_function_lambda_t *lambda)
873 {
874 void *start, *end;
875 uint32_t n;
876 njs_value_t *value, **closure;
877 njs_native_frame_t *frame;
878
879 if (lambda->nclosures == 0) {
880 return NJS_OK;
881 }
882
883 frame = &vm->active_frame->native;
884
885 while (frame->native) {
886 frame = frame->previous;
887 }
888
889 start = frame;
890 end = frame->free;
891
892 closure = njs_function_closures(function);
893 n = lambda->nclosures;
894
895 do {
896 n--;
897
898 value = njs_scope_value(vm, lambda->closures[n]);
899
900 if (start <= (void *) value && (void *) value < end) {
901 value = njs_scope_value_clone(vm, lambda->closures[n], value);
902 if (njs_slow_path(value == NULL)) {
903 return NJS_ERROR;
904 }
905 }
906
907 closure[n] = value;
908
909 } while (n != 0);
910
911 return NJS_OK;
912 }
913
914
915 njs_inline njs_value_t *
njs_function_closure_value(njs_vm_t * vm,njs_value_t ** scope,njs_index_t index,void * start,void * end)916 njs_function_closure_value(njs_vm_t *vm, njs_value_t **scope, njs_index_t index,
917 void *start, void *end)
918 {
919 njs_value_t *value, *newval;
920
921 value = scope[njs_scope_index_value(index)];
922
923 if (start <= (void *) value && end > (void *) value) {
924 newval = njs_mp_alloc(vm->mem_pool, sizeof(njs_value_t));
925 if (njs_slow_path(newval == NULL)) {
926 njs_memory_error(vm);
927 return NULL;
928 }
929
930 *newval = *value;
931 value = newval;
932 }
933
934 scope[njs_scope_index_value(index)] = value;
935
936 return value;
937 }
938
939
940 njs_int_t
njs_function_capture_global_closures(njs_vm_t * vm,njs_function_t * function)941 njs_function_capture_global_closures(njs_vm_t *vm, njs_function_t *function)
942 {
943 void *start, *end;
944 uint32_t n;
945 njs_value_t *value, **refs, **global;
946 njs_index_t *indexes, index;
947 njs_native_frame_t *native;
948 njs_function_lambda_t *lambda;
949
950 lambda = function->u.lambda;
951
952 if (lambda->nclosures == 0) {
953 return NJS_OK;
954 }
955
956 native = vm->top_frame;
957
958 while (native->previous->function != NULL) {
959 native = native->previous;
960 }
961
962 start = native;
963 end = native->free;
964
965 indexes = lambda->closures;
966 refs = njs_function_closures(function);
967
968 global = vm->levels[NJS_LEVEL_GLOBAL];
969
970 n = lambda->nclosures;
971
972 while (n > 0) {
973 n--;
974
975 index = indexes[n];
976
977 switch (njs_scope_index_type(index)) {
978 case NJS_LEVEL_LOCAL:
979 value = njs_function_closure_value(vm, native->local, index,
980 start, end);
981 break;
982
983 case NJS_LEVEL_GLOBAL:
984 value = njs_function_closure_value(vm, global, index, start, end);
985 break;
986
987 default:
988 njs_type_error(vm, "unexpected value type for closure \"%uD\"",
989 njs_scope_index_type(index));
990 return NJS_ERROR;
991 }
992
993 if (njs_slow_path(value == NULL)) {
994 return NJS_ERROR;
995 }
996
997 refs[n] = value;
998 }
999
1000 function->closure_copied = 1;
1001
1002 return NJS_OK;
1003 }
1004
1005
1006 static njs_value_t *
njs_function_property_prototype_set(njs_vm_t * vm,njs_lvlhsh_t * hash,njs_value_t * prototype)1007 njs_function_property_prototype_set(njs_vm_t *vm, njs_lvlhsh_t *hash,
1008 njs_value_t *prototype)
1009 {
1010 njs_int_t ret;
1011 njs_object_prop_t *prop;
1012 njs_lvlhsh_query_t lhq;
1013
1014 const njs_value_t proto_string = njs_string("prototype");
1015
1016 prop = njs_object_prop_alloc(vm, &proto_string, prototype, 0);
1017 if (njs_slow_path(prop == NULL)) {
1018 return NULL;
1019 }
1020
1021 prop->writable = 1;
1022
1023 lhq.value = prop;
1024 lhq.key_hash = NJS_PROTOTYPE_HASH;
1025 lhq.key = njs_str_value("prototype");
1026 lhq.replace = 1;
1027 lhq.pool = vm->mem_pool;
1028 lhq.proto = &njs_object_hash_proto;
1029
1030 ret = njs_lvlhsh_insert(hash, &lhq);
1031
1032 if (njs_fast_path(ret == NJS_OK)) {
1033 return &prop->value;
1034 }
1035
1036 njs_internal_error(vm, "lvlhsh insert failed");
1037
1038 return NULL;
1039 }
1040
1041
1042 /*
1043 * The "prototype" property of user defined functions is created on
1044 * demand in private hash of the functions by the "prototype" getter.
1045 * The getter creates a copy of function which is private to nJSVM,
1046 * adds a "prototype" object property to the copy, and then adds a
1047 * "constructor" property in the prototype object. The "constructor"
1048 * property points to the copy of function:
1049 * "F.prototype.constructor === F"
1050 */
1051
1052 njs_int_t
njs_function_prototype_create(njs_vm_t * vm,njs_object_prop_t * prop,njs_value_t * value,njs_value_t * setval,njs_value_t * retval)1053 njs_function_prototype_create(njs_vm_t *vm, njs_object_prop_t *prop,
1054 njs_value_t *value, njs_value_t *setval, njs_value_t *retval)
1055 {
1056 njs_value_t *proto, proto_value, *cons;
1057 njs_object_t *prototype;
1058 njs_function_t *function;
1059
1060 if (setval == NULL) {
1061 prototype = njs_object_alloc(vm);
1062 if (njs_slow_path(prototype == NULL)) {
1063 return NJS_ERROR;
1064 }
1065
1066 njs_set_object(&proto_value, prototype);
1067
1068 setval = &proto_value;
1069 }
1070
1071 function = njs_function_value_copy(vm, value);
1072 if (njs_slow_path(function == NULL)) {
1073 return NJS_ERROR;
1074 }
1075
1076 proto = njs_function_property_prototype_set(vm, njs_object_hash(value),
1077 setval);
1078 if (njs_slow_path(proto == NULL)) {
1079 return NJS_ERROR;
1080 }
1081
1082 if (setval == &proto_value && njs_is_object(proto)) {
1083 /* Only in getter context. */
1084 cons = njs_property_constructor_set(vm, njs_object_hash(proto), value);
1085 if (njs_slow_path(cons == NULL)) {
1086 return NJS_ERROR;
1087 }
1088 }
1089
1090 *retval = *proto;
1091
1092 return NJS_OK;
1093 }
1094
1095
1096 njs_int_t
njs_function_constructor(njs_vm_t * vm,njs_value_t * args,njs_uint_t nargs,njs_index_t async)1097 njs_function_constructor(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
1098 njs_index_t async)
1099 {
1100 njs_chb_t chain;
1101 njs_int_t ret;
1102 njs_str_t str, file;
1103 njs_uint_t i;
1104 njs_lexer_t lexer;
1105 njs_parser_t parser;
1106 njs_vm_code_t *code;
1107 njs_function_t *function;
1108 njs_generator_t generator;
1109 njs_parser_node_t *node;
1110 njs_parser_scope_t *scope;
1111 njs_function_lambda_t *lambda;
1112 const njs_token_type_t *type;
1113
1114 static const njs_token_type_t safe_ast[] = {
1115 NJS_TOKEN_END,
1116 NJS_TOKEN_FUNCTION_EXPRESSION,
1117 NJS_TOKEN_STATEMENT,
1118 NJS_TOKEN_RETURN,
1119 NJS_TOKEN_THIS,
1120 NJS_TOKEN_ILLEGAL
1121 };
1122
1123 static const njs_token_type_t safe_ast_async[] = {
1124 NJS_TOKEN_END,
1125 NJS_TOKEN_ASYNC_FUNCTION_EXPRESSION,
1126 NJS_TOKEN_STATEMENT,
1127 NJS_TOKEN_RETURN,
1128 NJS_TOKEN_THIS,
1129 NJS_TOKEN_ILLEGAL
1130 };
1131
1132 if (!vm->options.unsafe && nargs != 2) {
1133 goto fail;
1134 }
1135
1136 njs_chb_init(&chain, vm->mem_pool);
1137
1138 if (async) {
1139 njs_chb_append_literal(&chain, "(async function(");
1140
1141 } else {
1142 njs_chb_append_literal(&chain, "(function(");
1143 }
1144
1145 for (i = 1; i < nargs - 1; i++) {
1146 ret = njs_value_to_chain(vm, &chain, njs_argument(args, i));
1147 if (njs_slow_path(ret < NJS_OK)) {
1148 return ret;
1149 }
1150
1151 if (i != (nargs - 2)) {
1152 njs_chb_append_literal(&chain, ",");
1153 }
1154 }
1155
1156 njs_chb_append_literal(&chain, "){");
1157
1158 ret = njs_value_to_chain(vm, &chain, njs_argument(args, nargs - 1));
1159 if (njs_slow_path(ret < NJS_OK)) {
1160 return ret;
1161 }
1162
1163 njs_chb_append_literal(&chain, "})");
1164
1165 ret = njs_chb_join(&chain, &str);
1166 if (njs_slow_path(ret != NJS_OK)) {
1167 njs_memory_error(vm);
1168 return NJS_ERROR;
1169 }
1170
1171 file = njs_str_value("runtime");
1172
1173 ret = njs_lexer_init(vm, &lexer, &file, str.start, str.start + str.length,
1174 1);
1175 if (njs_slow_path(ret != NJS_OK)) {
1176 return ret;
1177 }
1178
1179 njs_memzero(&parser, sizeof(njs_parser_t));
1180
1181 parser.lexer = &lexer;
1182
1183 ret = njs_parser(vm, &parser);
1184 if (njs_slow_path(ret != NJS_OK)) {
1185 return ret;
1186 }
1187
1188 if (!vm->options.unsafe) {
1189 /*
1190 * Safe mode exception:
1191 * "(new Function('return this'))" is often used to get
1192 * the global object in a portable way.
1193 */
1194
1195 node = parser.node;
1196 type = (async) ? &safe_ast_async[0] : &safe_ast[0];
1197
1198 for (; *type != NJS_TOKEN_ILLEGAL; type++, node = node->right) {
1199 if (node == NULL) {
1200 goto fail;
1201 }
1202
1203 if (node->left != NULL
1204 && node->token_type != NJS_TOKEN_FUNCTION_EXPRESSION
1205 && node->left->token_type != NJS_TOKEN_NAME)
1206 {
1207 goto fail;
1208 }
1209
1210 if (node->token_type != *type) {
1211 goto fail;
1212 }
1213 }
1214 }
1215
1216 scope = parser.scope;
1217
1218 ret = njs_variables_copy(vm, &scope->variables, vm->variables_hash);
1219 if (njs_slow_path(ret != NJS_OK)) {
1220 return ret;
1221 }
1222
1223 ret = njs_generator_init(&generator, 0, 1);
1224 if (njs_slow_path(ret != NJS_OK)) {
1225 njs_internal_error(vm, "njs_generator_init() failed");
1226 return NJS_ERROR;
1227 }
1228
1229 code = njs_generate_scope(vm, &generator, scope, &njs_entry_anonymous);
1230 if (njs_slow_path(code == NULL)) {
1231 if (!njs_is_error(&vm->retval)) {
1232 njs_internal_error(vm, "njs_generate_scope() failed");
1233 }
1234
1235 return NJS_ERROR;
1236 }
1237
1238 njs_chb_destroy(&chain);
1239
1240 lambda = ((njs_vmcode_function_t *) generator.code_start)->lambda;
1241
1242 function = njs_function_alloc(vm, lambda, (njs_bool_t) async);
1243 if (njs_slow_path(function == NULL)) {
1244 return NJS_ERROR;
1245 }
1246
1247 function->global = 1;
1248 function->global_this = 1;
1249 function->args_count = lambda->nargs - lambda->rest_parameters;
1250
1251 njs_set_function(&vm->retval, function);
1252
1253 return NJS_OK;
1254
1255 fail:
1256
1257 njs_type_error(vm, "function constructor is disabled in \"safe\" mode");
1258 return NJS_ERROR;
1259 }
1260
1261
1262 static const njs_object_prop_t njs_function_constructor_properties[] =
1263 {
1264 {
1265 .type = NJS_PROPERTY,
1266 .name = njs_string("name"),
1267 .value = njs_string("Function"),
1268 .configurable = 1,
1269 },
1270
1271 {
1272 .type = NJS_PROPERTY,
1273 .name = njs_string("length"),
1274 .value = njs_value(NJS_NUMBER, 1, 1.0),
1275 .configurable = 1,
1276 },
1277
1278 {
1279 .type = NJS_PROPERTY_HANDLER,
1280 .name = njs_string("prototype"),
1281 .value = njs_prop_handler(njs_object_prototype_create),
1282 },
1283 };
1284
1285
1286 const njs_object_init_t njs_function_constructor_init = {
1287 njs_function_constructor_properties,
1288 njs_nitems(njs_function_constructor_properties),
1289 };
1290
1291
1292 njs_int_t
njs_function_instance_length(njs_vm_t * vm,njs_object_prop_t * prop,njs_value_t * value,njs_value_t * setval,njs_value_t * retval)1293 njs_function_instance_length(njs_vm_t *vm, njs_object_prop_t *prop,
1294 njs_value_t *value, njs_value_t *setval, njs_value_t *retval)
1295 {
1296 njs_object_t *proto;
1297 njs_function_t *function;
1298
1299 proto = njs_object(value);
1300
1301 do {
1302 if (njs_fast_path(proto->type == NJS_FUNCTION)) {
1303 break;
1304 }
1305
1306 proto = proto->__proto__;
1307 } while (proto != NULL);
1308
1309 if (njs_slow_path(proto == NULL)) {
1310 njs_internal_error(vm, "no function in proto chain");
1311 return NJS_ERROR;
1312 }
1313
1314 function = (njs_function_t *) proto;
1315
1316 njs_set_number(retval, function->args_count);
1317
1318 return NJS_OK;
1319 }
1320
1321
1322 static njs_int_t
njs_function_prototype_call(njs_vm_t * vm,njs_value_t * args,njs_uint_t nargs,njs_index_t unused)1323 njs_function_prototype_call(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
1324 njs_index_t unused)
1325 {
1326 njs_int_t ret;
1327 njs_function_t *function;
1328 const njs_value_t *this;
1329 njs_native_frame_t *frame;
1330
1331 if (!njs_is_function(&args[0])) {
1332 njs_type_error(vm, "\"this\" argument is not a function");
1333 return NJS_ERROR;
1334 }
1335
1336 if (nargs > 1) {
1337 this = &args[1];
1338 nargs -= 2;
1339
1340 } else {
1341 this = (njs_value_t *) &njs_value_undefined;
1342 nargs = 0;
1343 }
1344
1345 frame = vm->top_frame;
1346
1347 /* Skip the "call" method frame. */
1348 frame->skip = 1;
1349
1350 function = njs_function(&args[0]);
1351
1352 ret = njs_function_frame(vm, function, this, &args[2], nargs, 0);
1353 if (njs_slow_path(ret != NJS_OK)) {
1354 return ret;
1355 }
1356
1357 ret = njs_function_frame_invoke(vm, frame->retval);
1358 if (njs_slow_path(ret != NJS_OK)) {
1359 return ret;
1360 }
1361
1362 return NJS_DECLINED;
1363 }
1364
1365
1366 static njs_int_t
njs_function_prototype_apply(njs_vm_t * vm,njs_value_t * args,njs_uint_t nargs,njs_index_t unused)1367 njs_function_prototype_apply(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
1368 njs_index_t unused)
1369 {
1370 int64_t i, length;
1371 njs_int_t ret;
1372 njs_frame_t *frame;
1373 njs_value_t *this, *arr_like;
1374 njs_array_t *arr;
1375 njs_function_t *func;
1376
1377 if (!njs_is_function(njs_arg(args, nargs, 0))) {
1378 njs_type_error(vm, "\"this\" argument is not a function");
1379 return NJS_ERROR;
1380 }
1381
1382 func = njs_function(njs_argument(args, 0));
1383 this = njs_arg(args, nargs, 1);
1384 arr_like = njs_arg(args, nargs, 2);
1385
1386 if (njs_is_null_or_undefined(arr_like)) {
1387 length = 0;
1388
1389 goto activate;
1390
1391 } else if (njs_is_array(arr_like)) {
1392 arr = arr_like->data.u.array;
1393
1394 args = arr->start;
1395 length = arr->length;
1396
1397 goto activate;
1398
1399 } else if (njs_slow_path(!njs_is_object(arr_like))) {
1400 njs_type_error(vm, "second argument is not an array-like object");
1401 return NJS_ERROR;
1402 }
1403
1404 ret = njs_object_length(vm, arr_like, &length);
1405 if (njs_slow_path(ret != NJS_OK)) {
1406 return ret;
1407 }
1408
1409 arr = njs_array_alloc(vm, 1, length, NJS_ARRAY_SPARE);
1410 if (njs_slow_path(arr == NULL)) {
1411 return NJS_ERROR;
1412 }
1413
1414 args = arr->start;
1415
1416 for (i = 0; i < length; i++) {
1417 ret = njs_value_property_i64(vm, arr_like, i, &args[i]);
1418 if (njs_slow_path(ret == NJS_ERROR)) {
1419 return ret;
1420 }
1421 }
1422
1423 activate:
1424
1425 /* Skip the "apply" method frame. */
1426 vm->top_frame->skip = 1;
1427
1428 frame = (njs_frame_t *) vm->top_frame;
1429
1430 ret = njs_function_frame(vm, func, this, args, length, 0);
1431 if (njs_slow_path(ret != NJS_OK)) {
1432 return ret;
1433 }
1434
1435 ret = njs_function_frame_invoke(vm, frame->native.retval);
1436 if (njs_slow_path(ret != NJS_OK)) {
1437 return ret;
1438 }
1439
1440 return NJS_DECLINED;
1441 }
1442
1443
1444 static njs_int_t
njs_function_prototype_bind(njs_vm_t * vm,njs_value_t * args,njs_uint_t nargs,njs_index_t unused)1445 njs_function_prototype_bind(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
1446 njs_index_t unused)
1447 {
1448 size_t size;
1449 njs_int_t ret;
1450 njs_value_t *values, name;
1451 njs_function_t *function;
1452 njs_lvlhsh_query_t lhq;
1453
1454 if (!njs_is_function(&args[0])) {
1455 njs_type_error(vm, "\"this\" argument is not a function");
1456 return NJS_ERROR;
1457 }
1458
1459 function = njs_mp_alloc(vm->mem_pool, sizeof(njs_function_t));
1460 if (njs_slow_path(function == NULL)) {
1461 njs_memory_error(vm);
1462 return NJS_ERROR;
1463 }
1464
1465 *function = *njs_function(&args[0]);
1466
1467 njs_lvlhsh_init(&function->object.hash);
1468
1469 /* Bound functions have no "prototype" property. */
1470 function->object.shared_hash = vm->shared->arrow_instance_hash;
1471
1472 function->object.__proto__ = &vm->prototypes[NJS_OBJ_TYPE_FUNCTION].object;
1473 function->object.shared = 0;
1474
1475 function->u.bound_target = njs_function(&args[0]);
1476
1477 njs_object_property_init(&lhq, &njs_string_name, NJS_NAME_HASH);
1478
1479 ret = njs_object_property(vm, &args[0], &lhq, &name);
1480 if (njs_slow_path(ret == NJS_ERROR)) {
1481 return ret;
1482 }
1483
1484 if (!njs_is_string(&name)) {
1485 name = njs_string_empty;
1486 }
1487
1488 ret = njs_function_name_set(vm, function, &name, "bound");
1489 if (njs_slow_path(ret == NJS_ERROR)) {
1490 return ret;
1491 }
1492
1493 if (nargs == 1) {
1494 args = njs_value_arg(&njs_value_undefined);
1495
1496 } else {
1497 nargs--;
1498 args++;
1499 }
1500
1501 if (nargs > function->args_count) {
1502 function->args_count = 0;
1503
1504 } else {
1505 function->args_count -= nargs - 1;
1506 }
1507
1508 function->args_offset = nargs;
1509 size = nargs * sizeof(njs_value_t);
1510
1511 values = njs_mp_alloc(vm->mem_pool, size);
1512 if (njs_slow_path(values == NULL)) {
1513 njs_memory_error(vm);
1514 njs_mp_free(vm->mem_pool, function);
1515 return NJS_ERROR;
1516 }
1517
1518 function->bound = values;
1519
1520 /* GC: ? retain args. */
1521
1522 memcpy(values, args, size);
1523
1524 njs_set_function(&vm->retval, function);
1525
1526 return NJS_OK;
1527 }
1528
1529
1530 static const njs_object_prop_t njs_function_prototype_properties[] =
1531 {
1532 {
1533 .type = NJS_PROPERTY,
1534 .name = njs_string("name"),
1535 .value = njs_string(""),
1536 .configurable = 1,
1537 },
1538
1539 {
1540 .type = NJS_PROPERTY,
1541 .name = njs_string("length"),
1542 .value = njs_value(NJS_NUMBER, 0, 0.0),
1543 .configurable = 1,
1544 },
1545
1546 {
1547 .type = NJS_PROPERTY_HANDLER,
1548 .name = njs_string("constructor"),
1549 .value = njs_prop_handler(njs_object_prototype_create_constructor),
1550 .writable = 1,
1551 .configurable = 1,
1552 },
1553
1554 {
1555 .type = NJS_PROPERTY,
1556 .name = njs_string("call"),
1557 .value = njs_native_function(njs_function_prototype_call, 1),
1558 .writable = 1,
1559 .configurable = 1,
1560 },
1561
1562 {
1563 .type = NJS_PROPERTY,
1564 .name = njs_string("apply"),
1565 .value = njs_native_function(njs_function_prototype_apply, 2),
1566 .writable = 1,
1567 .configurable = 1,
1568 },
1569
1570 {
1571 .type = NJS_PROPERTY,
1572 .name = njs_string("bind"),
1573 .value = njs_native_function(njs_function_prototype_bind, 1),
1574 .writable = 1,
1575 .configurable = 1,
1576 },
1577
1578 {
1579 .type = NJS_PROPERTY,
1580 .name = njs_string("caller"),
1581 .value = njs_value(NJS_INVALID, 1, NAN),
1582 .getter = njs_native_function(njs_function_prototype_thrower, 0),
1583 .setter = njs_native_function(njs_function_prototype_thrower, 0),
1584 .writable = NJS_ATTRIBUTE_UNSET,
1585 .configurable = 1,
1586 },
1587
1588 {
1589 .type = NJS_PROPERTY,
1590 .name = njs_string("arguments"),
1591 .value = njs_value(NJS_INVALID, 1, NAN),
1592 .getter = njs_native_function(njs_function_prototype_thrower, 0),
1593 .setter = njs_native_function(njs_function_prototype_thrower, 0),
1594 .writable = NJS_ATTRIBUTE_UNSET,
1595 .configurable = 1,
1596 },
1597 };
1598
1599
1600 const njs_object_init_t njs_function_prototype_init = {
1601 njs_function_prototype_properties,
1602 njs_nitems(njs_function_prototype_properties),
1603 };
1604
1605
1606 const njs_object_prop_t njs_function_instance_properties[] =
1607 {
1608 {
1609 .type = NJS_PROPERTY_HANDLER,
1610 .name = njs_string("length"),
1611 .value = njs_prop_handler(njs_function_instance_length),
1612 .configurable = 1,
1613 },
1614
1615 {
1616 .type = NJS_PROPERTY_HANDLER,
1617 .name = njs_string("prototype"),
1618 .value = njs_prop_handler(njs_function_prototype_create),
1619 .writable = 1
1620 },
1621 };
1622
1623
1624 const njs_object_init_t njs_function_instance_init = {
1625 njs_function_instance_properties,
1626 njs_nitems(njs_function_instance_properties),
1627 };
1628
1629
1630 const njs_object_prop_t njs_arrow_instance_properties[] =
1631 {
1632 {
1633 .type = NJS_PROPERTY_HANDLER,
1634 .name = njs_string("length"),
1635 .value = njs_prop_handler(njs_function_instance_length),
1636 .configurable = 1,
1637 },
1638 };
1639
1640
1641 const njs_object_init_t njs_arrow_instance_init = {
1642 njs_arrow_instance_properties,
1643 njs_nitems(njs_arrow_instance_properties),
1644 };
1645
1646
1647 njs_int_t
njs_eval_function(njs_vm_t * vm,njs_value_t * args,njs_uint_t nargs,njs_index_t unused)1648 njs_eval_function(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
1649 njs_index_t unused)
1650 {
1651 njs_internal_error(vm, "Not implemented");
1652
1653 return NJS_ERROR;
1654 }
1655
1656
1657 static njs_int_t
njs_prototype_function(njs_vm_t * vm,njs_value_t * args,njs_uint_t nargs,njs_index_t unused)1658 njs_prototype_function(njs_vm_t *vm, njs_value_t *args, njs_uint_t nargs,
1659 njs_index_t unused)
1660 {
1661 njs_set_undefined(&vm->retval);
1662
1663 return NJS_OK;
1664 }
1665
1666
1667 const njs_object_type_init_t njs_function_type_init = {
1668 .constructor = njs_native_ctor(njs_function_constructor, 1, 0),
1669 .constructor_props = &njs_function_constructor_init,
1670 .prototype_props = &njs_function_prototype_init,
1671 .prototype_value = { .function = { .native = 1,
1672 .args_offset = 1,
1673 .u.native = njs_prototype_function,
1674 .object = { .type = NJS_FUNCTION } } },
1675 };
1676