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