1 
2 /*
3  * Copyright (C) Igor Sysoev
4  * Copyright (C) NGINX, Inc.
5  */
6 
7 
8 #include <njs_main.h>
9 
10 
11 static njs_int_t njs_vm_init(njs_vm_t *vm);
12 static njs_int_t njs_vm_handle_events(njs_vm_t *vm);
13 
14 
15 const njs_str_t  njs_entry_main =           njs_str("main");
16 const njs_str_t  njs_entry_module =         njs_str("module");
17 const njs_str_t  njs_entry_native =         njs_str("native");
18 const njs_str_t  njs_entry_unknown =        njs_str("unknown");
19 const njs_str_t  njs_entry_anonymous =      njs_str("anonymous");
20 
21 
22 void
njs_vm_opt_init(njs_vm_opt_t * options)23 njs_vm_opt_init(njs_vm_opt_t *options)
24 {
25     njs_memzero(options, sizeof(njs_vm_opt_t));
26 }
27 
28 
29 njs_vm_t *
njs_vm_create(njs_vm_opt_t * options)30 njs_vm_create(njs_vm_opt_t *options)
31 {
32     njs_mp_t      *mp;
33     njs_vm_t      *vm;
34     njs_int_t     ret;
35     njs_uint_t    i;
36     njs_module_t  **addons;
37 
38     mp = njs_mp_fast_create(2 * njs_pagesize(), 128, 512, 16);
39     if (njs_slow_path(mp == NULL)) {
40         return NULL;
41     }
42 
43     vm = njs_mp_zalign(mp, sizeof(njs_value_t), sizeof(njs_vm_t));
44     if (njs_slow_path(vm == NULL)) {
45         return NULL;
46     }
47 
48     vm->mem_pool = mp;
49 
50     ret = njs_regexp_init(vm);
51     if (njs_slow_path(ret != NJS_OK)) {
52         return NULL;
53     }
54 
55     njs_lvlhsh_init(&vm->values_hash);
56 
57     vm->options = *options;
58 
59     if (options->shared != NULL) {
60         vm->shared = options->shared;
61 
62     } else {
63         ret = njs_builtin_objects_create(vm);
64         if (njs_slow_path(ret != NJS_OK)) {
65             return NULL;
66         }
67     }
68 
69     vm->external = options->external;
70 
71     vm->trace.level = NJS_LEVEL_TRACE;
72     vm->trace.size = 2048;
73     vm->trace.data = vm;
74 
75     njs_set_undefined(&vm->retval);
76 
77     if (options->init) {
78         ret = njs_vm_init(vm);
79         if (njs_slow_path(ret != NJS_OK)) {
80             return NULL;
81         }
82     }
83 
84     for (i = 0; njs_modules[i] != NULL; i++) {
85         ret = njs_modules[i]->init(vm);
86         if (njs_slow_path(ret != NJS_OK)) {
87             return NULL;
88         }
89     }
90 
91     if (options->addons != NULL) {
92         addons = options->addons;
93         for (i = 0; addons[i] != NULL; i++) {
94             ret = addons[i]->init(vm);
95             if (njs_slow_path(ret != NJS_OK)) {
96                 return NULL;
97             }
98         }
99     }
100 
101     vm->symbol_generator = NJS_SYMBOL_KNOWN_MAX;
102 
103     if (njs_scope_undefined_index(vm, 0) == NJS_INDEX_ERROR) {
104         return NULL;
105     }
106 
107     return vm;
108 }
109 
110 
111 void
njs_vm_destroy(njs_vm_t * vm)112 njs_vm_destroy(njs_vm_t *vm)
113 {
114     njs_event_t        *event;
115     njs_lvlhsh_each_t  lhe;
116 
117     if (vm->hooks[NJS_HOOK_EXIT] != NULL) {
118         (void) njs_vm_call(vm, vm->hooks[NJS_HOOK_EXIT], NULL, 0);
119     }
120 
121     if (njs_waiting_events(vm)) {
122         njs_lvlhsh_each_init(&lhe, &njs_event_hash_proto);
123 
124         for ( ;; ) {
125             event = njs_lvlhsh_each(&vm->events_hash, &lhe);
126 
127             if (event == NULL) {
128                 break;
129             }
130 
131             njs_del_event(vm, event, NJS_EVENT_RELEASE);
132         }
133     }
134 
135     njs_mp_destroy(vm->mem_pool);
136 }
137 
138 
139 njs_int_t
njs_vm_compile(njs_vm_t * vm,u_char ** start,u_char * end)140 njs_vm_compile(njs_vm_t *vm, u_char **start, u_char *end)
141 {
142     njs_int_t           ret;
143     njs_str_t           ast;
144     njs_chb_t           chain;
145     njs_value_t         **global, **new;
146     njs_lexer_t         lexer;
147     njs_parser_t        parser;
148     njs_vm_code_t       *code;
149     njs_generator_t     generator;
150     njs_parser_scope_t  *scope;
151 
152     njs_memzero(&parser, sizeof(njs_parser_t));
153 
154     parser.scope = vm->global_scope;
155 
156     if (parser.scope != NULL && vm->modules != NULL) {
157         njs_module_reset(vm);
158     }
159 
160     ret = njs_lexer_init(vm, &lexer, &vm->options.file, *start, end, 0);
161     if (njs_slow_path(ret != NJS_OK)) {
162         return NJS_ERROR;
163     }
164 
165     parser.lexer = &lexer;
166 
167     ret = njs_parser(vm, &parser);
168     if (njs_slow_path(ret != NJS_OK)) {
169         return NJS_ERROR;
170     }
171 
172     if (njs_slow_path(vm->options.ast)) {
173         njs_chb_init(&chain, vm->mem_pool);
174         ret = njs_parser_serialize_ast(parser.node, &chain);
175         if (njs_slow_path(ret == NJS_ERROR)) {
176             return ret;
177         }
178 
179         if (njs_slow_path(njs_chb_join(&chain, &ast) != NJS_OK)) {
180             return NJS_ERROR;
181         }
182 
183         njs_print(ast.start, ast.length);
184 
185         njs_chb_destroy(&chain);
186         njs_mp_free(vm->mem_pool, ast.start);
187     }
188 
189     *start = lexer.start;
190     scope = parser.scope;
191 
192     ret = njs_generator_init(&generator, 0, 0);
193     if (njs_slow_path(ret != NJS_OK)) {
194         njs_internal_error(vm, "njs_generator_init() failed");
195         return NJS_ERROR;
196     }
197 
198     code = njs_generate_scope(vm, &generator, scope, &njs_entry_main);
199     if (njs_slow_path(code == NULL)) {
200         if (!njs_is_error(&vm->retval)) {
201             njs_internal_error(vm, "njs_generate_scope() failed");
202         }
203 
204         return NJS_ERROR;
205     }
206 
207     vm->global_scope = scope;
208 
209     if (scope->items > vm->global_items) {
210         global = vm->levels[NJS_LEVEL_GLOBAL];
211 
212         new = njs_scope_make(vm, scope->items);
213         if (njs_slow_path(new == NULL)) {
214             return ret;
215         }
216 
217         vm->levels[NJS_LEVEL_GLOBAL] = new;
218 
219         if (global != NULL) {
220             while (vm->global_items != 0) {
221                 vm->global_items--;
222 
223                 *new++ = *global++;
224             }
225 
226             njs_mp_free(vm->mem_pool, global);
227         }
228     }
229 
230     /* globalThis and this */
231     njs_scope_value_set(vm, njs_scope_global_this_index(), &vm->global_value);
232 
233     vm->start = generator.code_start;
234     vm->variables_hash = &scope->variables;
235     vm->global_items = scope->items;
236 
237     vm->levels[NJS_LEVEL_TEMP] = NULL;
238 
239     if (scope->temp != 0) {
240         new = njs_scope_make(vm, scope->temp);
241         if (njs_slow_path(new == NULL)) {
242             return ret;
243         }
244 
245         vm->levels[NJS_LEVEL_TEMP] = new;
246     }
247 
248     if (vm->options.disassemble) {
249         njs_disassembler(vm);
250     }
251 
252     return NJS_OK;
253 }
254 
255 
256 njs_vm_t *
njs_vm_clone(njs_vm_t * vm,njs_external_ptr_t external)257 njs_vm_clone(njs_vm_t *vm, njs_external_ptr_t external)
258 {
259     njs_mp_t     *nmp;
260     njs_vm_t     *nvm;
261     njs_int_t    ret;
262     njs_value_t  **global;
263 
264     njs_thread_log_debug("CLONE:");
265 
266     if (vm->options.interactive) {
267         return NULL;
268     }
269 
270     nmp = njs_mp_fast_create(2 * njs_pagesize(), 128, 512, 16);
271     if (njs_slow_path(nmp == NULL)) {
272         return NULL;
273     }
274 
275     nvm = njs_mp_align(nmp, sizeof(njs_value_t), sizeof(njs_vm_t));
276     if (njs_slow_path(nvm == NULL)) {
277         goto fail;
278     }
279 
280     *nvm = *vm;
281 
282     nvm->mem_pool = nmp;
283     nvm->trace.data = nvm;
284     nvm->external = external;
285 
286     ret = njs_vm_init(nvm);
287     if (njs_slow_path(ret != NJS_OK)) {
288         goto fail;
289     }
290 
291     global = njs_scope_make(nvm, nvm->global_items);
292     if (njs_slow_path(global == NULL)) {
293         goto fail;
294     }
295 
296     nvm->levels[NJS_LEVEL_GLOBAL] = global;
297 
298     njs_set_object(&nvm->global_value, &nvm->global_object);
299 
300     /* globalThis and this */
301     njs_scope_value_set(nvm, njs_scope_global_this_index(), &nvm->global_value);
302 
303     nvm->levels[NJS_LEVEL_LOCAL] = NULL;
304 
305     return nvm;
306 
307 fail:
308 
309     njs_mp_destroy(nmp);
310 
311     return NULL;
312 }
313 
314 
315 static njs_int_t
njs_vm_init(njs_vm_t * vm)316 njs_vm_init(njs_vm_t *vm)
317 {
318     njs_int_t    ret;
319     njs_frame_t  *frame;
320 
321     frame = (njs_frame_t *) njs_function_frame_alloc(vm, NJS_FRAME_SIZE);
322     if (njs_slow_path(frame == NULL)) {
323         njs_memory_error(vm);
324         return NJS_ERROR;
325     }
326 
327     frame->exception.catch = NULL;
328     frame->exception.next = NULL;
329     frame->previous_active_frame = NULL;
330 
331     vm->active_frame = frame;
332 
333     ret = njs_regexp_init(vm);
334     if (njs_slow_path(ret != NJS_OK)) {
335         return NJS_ERROR;
336     }
337 
338     ret = njs_builtin_objects_clone(vm, &vm->global_value);
339     if (njs_slow_path(ret != NJS_OK)) {
340         return NJS_ERROR;
341     }
342 
343     njs_lvlhsh_init(&vm->values_hash);
344     njs_lvlhsh_init(&vm->keywords_hash);
345     njs_lvlhsh_init(&vm->modules_hash);
346     njs_lvlhsh_init(&vm->events_hash);
347 
348     njs_queue_init(&vm->posted_events);
349     njs_queue_init(&vm->promise_events);
350 
351     return NJS_OK;
352 }
353 
354 
355 njs_int_t
njs_vm_call(njs_vm_t * vm,njs_function_t * function,const njs_value_t * args,njs_uint_t nargs)356 njs_vm_call(njs_vm_t *vm, njs_function_t *function, const njs_value_t *args,
357     njs_uint_t nargs)
358 {
359     return njs_vm_invoke(vm, function, args, nargs, &vm->retval);
360 }
361 
362 
363 njs_int_t
njs_vm_invoke(njs_vm_t * vm,njs_function_t * function,const njs_value_t * args,njs_uint_t nargs,njs_value_t * retval)364 njs_vm_invoke(njs_vm_t *vm, njs_function_t *function, const njs_value_t *args,
365     njs_uint_t nargs, njs_value_t *retval)
366 {
367     njs_int_t  ret;
368 
369     ret = njs_function_frame(vm, function, &njs_value_undefined, args, nargs,
370                              0);
371     if (njs_slow_path(ret != NJS_OK)) {
372         return ret;
373     }
374 
375     return njs_function_frame_invoke(vm, retval);
376 }
377 
378 
379 void
njs_vm_scopes_restore(njs_vm_t * vm,njs_native_frame_t * native,njs_native_frame_t * previous)380 njs_vm_scopes_restore(njs_vm_t *vm, njs_native_frame_t *native,
381     njs_native_frame_t *previous)
382 {
383     njs_frame_t  *frame;
384 
385     vm->top_frame = previous;
386 
387     if (native->function->native) {
388         return;
389     }
390 
391     frame = (njs_frame_t *) native;
392     frame = frame->previous_active_frame;
393     vm->active_frame = frame;
394 }
395 
396 
397 njs_vm_event_t
njs_vm_add_event(njs_vm_t * vm,njs_function_t * function,njs_uint_t once,njs_host_event_t host_ev,njs_event_destructor_t destructor)398 njs_vm_add_event(njs_vm_t *vm, njs_function_t *function, njs_uint_t once,
399     njs_host_event_t host_ev, njs_event_destructor_t destructor)
400 {
401     njs_event_t  *event;
402 
403     event = njs_mp_alloc(vm->mem_pool, sizeof(njs_event_t));
404     if (njs_slow_path(event == NULL)) {
405         return NULL;
406     }
407 
408     event->host_event = host_ev;
409     event->destructor = destructor;
410     event->function = function;
411     event->once = once;
412     event->posted = 0;
413     event->nargs = 0;
414     event->args = NULL;
415 
416     if (njs_add_event(vm, event) != NJS_OK) {
417         return NULL;
418     }
419 
420     return event;
421 }
422 
423 
424 void
njs_vm_del_event(njs_vm_t * vm,njs_vm_event_t vm_event)425 njs_vm_del_event(njs_vm_t *vm, njs_vm_event_t vm_event)
426 {
427     njs_event_t  *event;
428 
429     event = (njs_event_t *) vm_event;
430 
431     njs_del_event(vm, event, NJS_EVENT_RELEASE | NJS_EVENT_DELETE);
432 }
433 
434 
435 njs_int_t
njs_vm_waiting(njs_vm_t * vm)436 njs_vm_waiting(njs_vm_t *vm)
437 {
438     return njs_waiting_events(vm);
439 }
440 
441 
442 njs_int_t
njs_vm_posted(njs_vm_t * vm)443 njs_vm_posted(njs_vm_t *vm)
444 {
445     return njs_posted_events(vm) || njs_promise_events(vm);
446 }
447 
448 
449 njs_int_t
njs_vm_post_event(njs_vm_t * vm,njs_vm_event_t vm_event,const njs_value_t * args,njs_uint_t nargs)450 njs_vm_post_event(njs_vm_t *vm, njs_vm_event_t vm_event,
451     const njs_value_t *args, njs_uint_t nargs)
452 {
453     njs_event_t  *event;
454 
455     event = (njs_event_t *) vm_event;
456 
457     if (nargs != 0 && !event->posted) {
458         event->nargs = nargs;
459         event->args = njs_mp_alloc(vm->mem_pool, sizeof(njs_value_t) * nargs);
460         if (njs_slow_path(event->args == NULL)) {
461             return NJS_ERROR;
462         }
463 
464         memcpy(event->args, args, sizeof(njs_value_t) * nargs);
465     }
466 
467     if (!event->posted) {
468         event->posted = 1;
469         njs_queue_insert_tail(&vm->posted_events, &event->link);
470     }
471 
472     return NJS_OK;
473 }
474 
475 
476 njs_int_t
njs_vm_run(njs_vm_t * vm)477 njs_vm_run(njs_vm_t *vm)
478 {
479     return njs_vm_handle_events(vm);
480 }
481 
482 
483 njs_int_t
njs_vm_start(njs_vm_t * vm)484 njs_vm_start(njs_vm_t *vm)
485 {
486     njs_int_t  ret;
487 
488     ret = njs_module_load(vm);
489     if (njs_slow_path(ret != NJS_OK)) {
490         return ret;
491     }
492 
493     ret = njs_vmcode_interpreter(vm, vm->start);
494 
495     return (ret == NJS_ERROR) ? NJS_ERROR : NJS_OK;
496 }
497 
498 
499 static njs_int_t
njs_vm_handle_events(njs_vm_t * vm)500 njs_vm_handle_events(njs_vm_t *vm)
501 {
502     njs_int_t         ret;
503     njs_str_t         str;
504     njs_value_t       string;
505     njs_event_t       *ev;
506     njs_queue_t       *promise_events, *posted_events;
507     njs_queue_link_t  *link;
508 
509     promise_events = &vm->promise_events;
510     posted_events = &vm->posted_events;
511 
512     do {
513         for ( ;; ) {
514             link = njs_queue_first(promise_events);
515 
516             if (link == njs_queue_tail(promise_events)) {
517                 break;
518             }
519 
520             ev = njs_queue_link_data(link, njs_event_t, link);
521 
522             njs_queue_remove(&ev->link);
523 
524             ret = njs_vm_call(vm, ev->function, ev->args, ev->nargs);
525             if (njs_slow_path(ret == NJS_ERROR)) {
526                 return ret;
527             }
528         }
529 
530         if (njs_vm_unhandled_rejection(vm)) {
531             ret = njs_value_to_string(vm, &string,
532                                       &vm->promise_reason->start[0]);
533             if (njs_slow_path(ret != NJS_OK)) {
534                 return ret;
535             }
536 
537             njs_string_get(&string, &str);
538             njs_vm_error(vm, "unhandled promise rejection: %V", &str);
539 
540             njs_mp_free(vm->mem_pool, vm->promise_reason);
541             vm->promise_reason = NULL;
542 
543             return NJS_ERROR;
544         }
545 
546         for ( ;; ) {
547             link = njs_queue_first(posted_events);
548 
549             if (link == njs_queue_tail(posted_events)) {
550                 break;
551             }
552 
553             ev = njs_queue_link_data(link, njs_event_t, link);
554 
555             if (ev->once) {
556                 njs_del_event(vm, ev, NJS_EVENT_RELEASE | NJS_EVENT_DELETE);
557 
558             } else {
559                 ev->posted = 0;
560                 njs_queue_remove(&ev->link);
561             }
562 
563             ret = njs_vm_call(vm, ev->function, ev->args, ev->nargs);
564 
565             if (ret == NJS_ERROR) {
566                 return ret;
567             }
568         }
569 
570     } while (!njs_queue_is_empty(promise_events));
571 
572     return njs_vm_pending(vm) ? NJS_AGAIN : NJS_OK;
573 }
574 
575 
576 njs_int_t
njs_vm_add_path(njs_vm_t * vm,const njs_str_t * path)577 njs_vm_add_path(njs_vm_t *vm, const njs_str_t *path)
578 {
579     njs_str_t  *item;
580 
581     if (vm->paths == NULL) {
582         vm->paths = njs_arr_create(vm->mem_pool, 4, sizeof(njs_str_t));
583         if (njs_slow_path(vm->paths == NULL)) {
584             return NJS_ERROR;
585         }
586     }
587 
588     item = njs_arr_add(vm->paths);
589     if (njs_slow_path(item == NULL)) {
590         return NJS_ERROR;
591     }
592 
593     *item = *path;
594 
595     return NJS_OK;
596 }
597 
598 
599 njs_value_t *
njs_vm_retval(njs_vm_t * vm)600 njs_vm_retval(njs_vm_t *vm)
601 {
602     return &vm->retval;
603 }
604 
605 
606 njs_mp_t *
njs_vm_memory_pool(njs_vm_t * vm)607 njs_vm_memory_pool(njs_vm_t *vm)
608 {
609     return vm->mem_pool;
610 }
611 
612 
613 uintptr_t
njs_vm_meta(njs_vm_t * vm,njs_uint_t index)614 njs_vm_meta(njs_vm_t *vm, njs_uint_t index)
615 {
616     njs_vm_meta_t  *metas;
617 
618     metas = vm->options.metas;
619     if (njs_slow_path(metas == NULL || metas->size <= index)) {
620         return -1;
621     }
622 
623     return metas->values[index];
624 }
625 
626 
627 void
njs_vm_retval_set(njs_vm_t * vm,const njs_value_t * value)628 njs_vm_retval_set(njs_vm_t *vm, const njs_value_t *value)
629 {
630     vm->retval = *value;
631 }
632 
633 
634 njs_int_t
njs_vm_value(njs_vm_t * vm,const njs_str_t * path,njs_value_t * retval)635 njs_vm_value(njs_vm_t *vm, const njs_str_t *path, njs_value_t *retval)
636 {
637     u_char       *start, *p, *end;
638     size_t       size;
639     njs_int_t    ret;
640     njs_value_t  value, key;
641 
642     start = path->start;
643     end = start + path->length;
644 
645     njs_set_object(&value, &vm->global_object);
646 
647     for ( ;; ) {
648         p = njs_strlchr(start, end, '.');
649 
650         size = ((p != NULL) ? p : end) - start;
651         if (njs_slow_path(size == 0)) {
652             njs_type_error(vm, "empty path element");
653             return NJS_ERROR;
654         }
655 
656         ret = njs_string_set(vm, &key, start, size);
657         if (njs_slow_path(ret != NJS_OK)) {
658             return NJS_ERROR;
659         }
660 
661         ret = njs_value_property(vm, &value, &key, njs_value_arg(retval));
662         if (njs_slow_path(ret != NJS_OK)) {
663             return ret;
664         }
665 
666         if (p == NULL) {
667             break;
668         }
669 
670         start = p + 1;
671         value = *retval;
672     }
673 
674     return NJS_OK;
675 }
676 
677 
678 njs_int_t
njs_vm_bind(njs_vm_t * vm,const njs_str_t * var_name,const njs_value_t * value,njs_bool_t shared)679 njs_vm_bind(njs_vm_t *vm, const njs_str_t *var_name, const njs_value_t *value,
680     njs_bool_t shared)
681 {
682     njs_int_t           ret;
683     njs_object_t        *global;
684     njs_lvlhsh_t        *hash;
685     njs_object_prop_t   *prop;
686     njs_lvlhsh_query_t  lhq;
687 
688     prop = njs_object_prop_alloc(vm, &njs_value_undefined, value, 1);
689     if (njs_slow_path(prop == NULL)) {
690         return NJS_ERROR;
691     }
692 
693     ret = njs_string_new(vm, &prop->name, var_name->start, var_name->length, 0);
694     if (njs_slow_path(ret != NJS_OK)) {
695         return NJS_ERROR;
696     }
697 
698     lhq.value = prop;
699     lhq.key = *var_name;
700     lhq.key_hash = njs_djb_hash(lhq.key.start, lhq.key.length);
701     lhq.replace = 1;
702     lhq.pool = vm->mem_pool;
703     lhq.proto = &njs_object_hash_proto;
704 
705     global = &vm->global_object;
706     hash = shared ? &global->shared_hash : &global->hash;
707 
708     ret = njs_lvlhsh_insert(hash, &lhq);
709     if (njs_slow_path(ret != NJS_OK)) {
710         njs_internal_error(vm, "lvlhsh insert failed");
711         return ret;
712     }
713 
714     return NJS_OK;
715 }
716 
717 
718 void
njs_value_string_get(njs_value_t * value,njs_str_t * dst)719 njs_value_string_get(njs_value_t *value, njs_str_t *dst)
720 {
721     njs_string_get(value, dst);
722 }
723 
724 
725 njs_int_t
njs_vm_value_string_set(njs_vm_t * vm,njs_value_t * value,const u_char * start,uint32_t size)726 njs_vm_value_string_set(njs_vm_t *vm, njs_value_t *value, const u_char *start,
727     uint32_t size)
728 {
729     return njs_string_set(vm, value, start, size);
730 }
731 
732 
733 njs_int_t
njs_vm_value_array_buffer_set(njs_vm_t * vm,njs_value_t * value,const u_char * start,uint32_t size)734 njs_vm_value_array_buffer_set(njs_vm_t *vm, njs_value_t *value,
735     const u_char *start, uint32_t size)
736 {
737     njs_array_buffer_t  *array;
738 
739     array = njs_array_buffer_alloc(vm, 0, 0);
740     if (njs_slow_path(array == NULL)) {
741         return NJS_ERROR;
742     }
743 
744     array->u.data = (u_char *) start;
745     array->size = size;
746 
747     njs_set_array_buffer(value, array);
748 
749     return NJS_OK;
750 }
751 
752 
753 njs_int_t
njs_vm_value_buffer_set(njs_vm_t * vm,njs_value_t * value,const u_char * start,uint32_t size)754 njs_vm_value_buffer_set(njs_vm_t *vm, njs_value_t *value, const u_char *start,
755     uint32_t size)
756 {
757     return njs_buffer_set(vm, value, start, size);
758 }
759 
760 
761 u_char *
njs_vm_value_string_alloc(njs_vm_t * vm,njs_value_t * value,uint32_t size)762 njs_vm_value_string_alloc(njs_vm_t *vm, njs_value_t *value, uint32_t size)
763 {
764     return njs_string_alloc(vm, value, size, 0);
765 }
766 
767 
768 njs_function_t *
njs_vm_function(njs_vm_t * vm,const njs_str_t * path)769 njs_vm_function(njs_vm_t *vm, const njs_str_t *path)
770 {
771     njs_int_t    ret;
772     njs_value_t  retval;
773 
774     ret = njs_vm_value(vm, path, &retval);
775     if (njs_slow_path(ret != NJS_OK || !njs_is_function(&retval))) {
776         return NULL;
777     }
778 
779     return njs_function(&retval);
780 }
781 
782 
783 uint16_t
njs_vm_prop_magic16(njs_object_prop_t * prop)784 njs_vm_prop_magic16(njs_object_prop_t *prop)
785 {
786     return prop->value.data.magic16;
787 }
788 
789 
790 uint32_t
njs_vm_prop_magic32(njs_object_prop_t * prop)791 njs_vm_prop_magic32(njs_object_prop_t *prop)
792 {
793     return prop->value.data.magic32;
794 }
795 
796 
797 njs_int_t
njs_vm_prop_name(njs_vm_t * vm,njs_object_prop_t * prop,njs_str_t * dst)798 njs_vm_prop_name(njs_vm_t *vm, njs_object_prop_t *prop, njs_str_t *dst)
799 {
800     if (njs_slow_path(!njs_is_string(&prop->name))) {
801         njs_type_error(vm, "property name is not a string");
802         return NJS_ERROR;
803     }
804 
805     njs_string_get(&prop->name, dst);
806 
807     return NJS_OK;
808 }
809 
810 
811 njs_noinline void
njs_vm_value_error_set(njs_vm_t * vm,njs_value_t * value,const char * fmt,...)812 njs_vm_value_error_set(njs_vm_t *vm, njs_value_t *value, const char *fmt, ...)
813 {
814     va_list  args;
815     u_char   buf[NJS_MAX_ERROR_STR], *p;
816 
817     p = buf;
818 
819     if (fmt != NULL) {
820         va_start(args, fmt);
821         p = njs_vsprintf(buf, buf + sizeof(buf), fmt, args);
822         va_end(args);
823     }
824 
825     njs_error_new(vm, value, NJS_OBJ_TYPE_ERROR, buf, p - buf);
826 }
827 
828 
829 njs_noinline void
njs_vm_memory_error(njs_vm_t * vm)830 njs_vm_memory_error(njs_vm_t *vm)
831 {
832     njs_memory_error_set(vm, &vm->retval);
833 }
834 
835 
836 njs_int_t
njs_vm_value_string(njs_vm_t * vm,njs_str_t * dst,njs_value_t * src)837 njs_vm_value_string(njs_vm_t *vm, njs_str_t *dst, njs_value_t *src)
838 {
839     njs_int_t    ret;
840     njs_uint_t   exception;
841 
842     if (njs_slow_path(src->type == NJS_NUMBER
843                       && njs_number(src) == 0
844                       && signbit(njs_number(src))))
845     {
846         njs_string_get(&njs_string_minus_zero, dst);
847         return NJS_OK;
848     }
849 
850     exception = 0;
851 
852 again:
853 
854     ret = njs_vm_value_to_string(vm, dst, src);
855     if (njs_fast_path(ret == NJS_OK)) {
856         return NJS_OK;
857     }
858 
859     if (!exception) {
860         exception = 1;
861 
862         /* value evaluation threw an exception. */
863 
864         src = &vm->retval;
865         goto again;
866     }
867 
868     dst->length = 0;
869     dst->start = NULL;
870 
871     return NJS_ERROR;
872 }
873 
874 
875 njs_int_t
njs_vm_retval_string(njs_vm_t * vm,njs_str_t * dst)876 njs_vm_retval_string(njs_vm_t *vm, njs_str_t *dst)
877 {
878     if (vm->top_frame == NULL) {
879         /* An exception was thrown during compilation. */
880 
881         njs_vm_init(vm);
882     }
883 
884     return njs_vm_value_string(vm, dst, &vm->retval);
885 }
886 
887 
888 njs_int_t
njs_vm_retval_dump(njs_vm_t * vm,njs_str_t * dst,njs_uint_t indent)889 njs_vm_retval_dump(njs_vm_t *vm, njs_str_t *dst, njs_uint_t indent)
890 {
891     if (vm->top_frame == NULL) {
892         /* An exception was thrown during compilation. */
893 
894         njs_vm_init(vm);
895     }
896 
897     return njs_vm_value_dump(vm, dst, &vm->retval, 0, 1);
898 }
899 
900 
901 njs_int_t
njs_vm_object_alloc(njs_vm_t * vm,njs_value_t * retval,...)902 njs_vm_object_alloc(njs_vm_t *vm, njs_value_t *retval, ...)
903 {
904     va_list             args;
905     njs_int_t           ret;
906     njs_value_t         *name, *value;
907     njs_object_t        *object;
908     njs_object_prop_t   *prop;
909     njs_lvlhsh_query_t  lhq;
910 
911     object = njs_object_alloc(vm);
912     if (njs_slow_path(object == NULL)) {
913         return NJS_ERROR;
914     }
915 
916     ret = NJS_ERROR;
917 
918     va_start(args, retval);
919 
920     for ( ;; ) {
921         name = va_arg(args, njs_value_t *);
922         if (name == NULL) {
923             break;
924         }
925 
926         value = va_arg(args, njs_value_t *);
927         if (value == NULL) {
928             njs_type_error(vm, "missed value for a key");
929             goto done;
930         }
931 
932         if (njs_slow_path(!njs_is_string(name))) {
933             njs_type_error(vm, "prop name is not a string");
934             goto done;
935         }
936 
937         lhq.replace = 0;
938         lhq.pool = vm->mem_pool;
939         lhq.proto = &njs_object_hash_proto;
940 
941         njs_string_get(name, &lhq.key);
942         lhq.key_hash = njs_djb_hash(lhq.key.start, lhq.key.length);
943 
944         prop = njs_object_prop_alloc(vm, name, value, 1);
945         if (njs_slow_path(prop == NULL)) {
946             goto done;
947         }
948 
949         lhq.value = prop;
950 
951         ret = njs_lvlhsh_insert(&object->hash, &lhq);
952         if (njs_slow_path(ret != NJS_OK)) {
953             njs_internal_error(vm, NULL);
954             goto done;
955         }
956     }
957 
958     ret = NJS_OK;
959 
960     njs_set_object(retval, object);
961 
962 done:
963 
964     va_end(args);
965 
966     return ret;
967 }
968 
969 
970 njs_value_t *
njs_vm_object_keys(njs_vm_t * vm,njs_value_t * value,njs_value_t * retval)971 njs_vm_object_keys(njs_vm_t *vm, njs_value_t *value, njs_value_t *retval)
972 {
973     njs_array_t  *keys;
974 
975     keys = njs_value_own_enumerate(vm, value, NJS_ENUM_KEYS,
976                                    NJS_ENUM_STRING, 0);
977     if (njs_slow_path(keys == NULL)) {
978         return NULL;
979     }
980 
981     njs_set_array(retval, keys);
982 
983     return retval;
984 }
985 
986 
987 njs_int_t
njs_vm_array_alloc(njs_vm_t * vm,njs_value_t * retval,uint32_t spare)988 njs_vm_array_alloc(njs_vm_t *vm, njs_value_t *retval, uint32_t spare)
989 {
990     njs_array_t  *array;
991 
992     array = njs_array_alloc(vm, 1, 0, spare);
993 
994     if (njs_slow_path(array == NULL)) {
995         return NJS_ERROR;
996     }
997 
998     njs_set_array(retval, array);
999 
1000     return NJS_OK;
1001 }
1002 
1003 
1004 njs_value_t *
njs_vm_array_push(njs_vm_t * vm,njs_value_t * value)1005 njs_vm_array_push(njs_vm_t *vm, njs_value_t *value)
1006 {
1007     if (njs_slow_path(!njs_is_array(value))) {
1008         njs_type_error(vm, "njs_vm_array_push() argument is not array");
1009         return NULL;
1010     }
1011 
1012     return njs_array_push(vm, njs_array(value));
1013 }
1014 
1015 
1016 njs_value_t *
njs_vm_object_prop(njs_vm_t * vm,njs_value_t * value,const njs_str_t * prop,njs_opaque_value_t * retval)1017 njs_vm_object_prop(njs_vm_t *vm, njs_value_t *value, const njs_str_t *prop,
1018     njs_opaque_value_t *retval)
1019 {
1020     njs_int_t    ret;
1021     njs_value_t  key;
1022 
1023     if (njs_slow_path(!njs_is_object(value))) {
1024         njs_type_error(vm, "njs_vm_object_prop() argument is not object");
1025         return NULL;
1026     }
1027 
1028     ret = njs_vm_value_string_set(vm, &key, prop->start, prop->length);
1029     if (njs_slow_path(ret != NJS_OK)) {
1030         return NULL;
1031     }
1032 
1033     ret = njs_value_property(vm, value, &key, njs_value_arg(retval));
1034     if (njs_slow_path(ret != NJS_OK)) {
1035         return NULL;
1036     }
1037 
1038     return njs_value_arg(retval);
1039 }
1040 
1041 
1042 njs_value_t *
njs_vm_array_prop(njs_vm_t * vm,njs_value_t * value,int64_t index,njs_opaque_value_t * retval)1043 njs_vm_array_prop(njs_vm_t *vm, njs_value_t *value, int64_t index,
1044     njs_opaque_value_t *retval)
1045 {
1046     njs_int_t    ret;
1047     njs_array_t  *array;
1048 
1049     if (njs_slow_path(!njs_is_object(value))) {
1050         njs_type_error(vm, "njs_vm_array_prop() argument is not object");
1051         return NULL;
1052     }
1053 
1054     if (njs_fast_path(njs_is_fast_array(value))) {
1055         array = njs_array(value);
1056 
1057         if (index >= 0 && index < array->length) {
1058             return &array->start[index];
1059         }
1060 
1061         return NULL;
1062     }
1063 
1064     ret = njs_value_property_i64(vm, value, index, njs_value_arg(retval));
1065     if (njs_slow_path(ret != NJS_OK)) {
1066         return NULL;
1067     }
1068 
1069     return njs_value_arg(retval);
1070 }
1071 
1072 
1073 njs_value_t *
njs_vm_array_start(njs_vm_t * vm,njs_value_t * value)1074 njs_vm_array_start(njs_vm_t *vm, njs_value_t *value)
1075 {
1076     if (njs_slow_path(!njs_is_fast_array(value))) {
1077         njs_type_error(vm, "njs_vm_array_start() argument is not a fast array");
1078         return NULL;
1079     }
1080 
1081     return njs_array(value)->start;
1082 }
1083 
1084 
1085 njs_int_t
njs_vm_array_length(njs_vm_t * vm,njs_value_t * value,int64_t * length)1086 njs_vm_array_length(njs_vm_t *vm, njs_value_t *value, int64_t *length)
1087 {
1088     if (njs_fast_path(njs_is_array(value))) {
1089         *length = njs_array(value)->length;
1090     }
1091 
1092     return njs_object_length(vm, value, length);
1093 }
1094 
1095 
1096 njs_int_t
njs_vm_value_to_string(njs_vm_t * vm,njs_str_t * dst,njs_value_t * src)1097 njs_vm_value_to_string(njs_vm_t *vm, njs_str_t *dst, njs_value_t *src)
1098 {
1099     u_char       *start;
1100     size_t       size;
1101     njs_int_t    ret;
1102     njs_value_t  value, stack;
1103 
1104     if (njs_slow_path(src == NULL)) {
1105         return NJS_ERROR;
1106     }
1107 
1108     if (njs_is_error(src)) {
1109         if (njs_is_memory_error(vm, src)) {
1110             njs_string_get(&njs_string_memory_error, dst);
1111             return NJS_OK;
1112         }
1113 
1114         ret = njs_error_stack(vm, src, &stack);
1115         if (njs_slow_path(ret == NJS_ERROR)) {
1116             return ret;
1117         }
1118 
1119         if (ret == NJS_OK) {
1120             src = &stack;
1121         }
1122     }
1123 
1124     value = *src;
1125 
1126     ret = njs_value_to_string(vm, &value, &value);
1127 
1128     if (njs_fast_path(ret == NJS_OK)) {
1129         size = value.short_string.size;
1130 
1131         if (size != NJS_STRING_LONG) {
1132             start = njs_mp_alloc(vm->mem_pool, size);
1133             if (njs_slow_path(start == NULL)) {
1134                 njs_memory_error(vm);
1135                 return NJS_ERROR;
1136             }
1137 
1138             memcpy(start, value.short_string.start, size);
1139 
1140         } else {
1141             size = value.long_string.size;
1142             start = value.long_string.data->start;
1143         }
1144 
1145         dst->length = size;
1146         dst->start = start;
1147     }
1148 
1149     return ret;
1150 }
1151 
1152 
1153 njs_int_t
njs_vm_value_to_bytes(njs_vm_t * vm,njs_str_t * dst,njs_value_t * src)1154 njs_vm_value_to_bytes(njs_vm_t *vm, njs_str_t *dst, njs_value_t *src)
1155 {
1156     u_char              *start;
1157     size_t              size, length, offset;
1158     njs_int_t           ret;
1159     njs_value_t         value;
1160     njs_typed_array_t   *array;
1161     njs_array_buffer_t  *buffer;
1162 
1163     if (njs_slow_path(src == NULL)) {
1164         return NJS_ERROR;
1165     }
1166 
1167     ret = NJS_OK;
1168     value = *src;
1169 
1170     switch (value.type) {
1171     case NJS_TYPED_ARRAY:
1172     case NJS_DATA_VIEW:
1173     case NJS_ARRAY_BUFFER:
1174 
1175         if (value.type != NJS_ARRAY_BUFFER) {
1176             array = njs_typed_array(&value);
1177             buffer = njs_typed_array_buffer(array);
1178             offset = array->offset;
1179             length = array->byte_length;
1180 
1181         } else {
1182             buffer = njs_array_buffer(&value);
1183             offset = 0;
1184             length = buffer->size;
1185         }
1186 
1187         if (njs_slow_path(njs_is_detached_buffer(buffer))) {
1188             njs_type_error(vm, "detached buffer");
1189             return NJS_ERROR;
1190         }
1191 
1192         dst->start = &buffer->u.u8[offset];
1193         dst->length = length;
1194         break;
1195 
1196     default:
1197         ret = njs_value_to_string(vm, &value, &value);
1198         if (njs_slow_path(ret != NJS_OK)) {
1199             return NJS_ERROR;
1200         }
1201 
1202         size = value.short_string.size;
1203 
1204         if (size != NJS_STRING_LONG) {
1205             start = njs_mp_alloc(vm->mem_pool, size);
1206             if (njs_slow_path(start == NULL)) {
1207                 njs_memory_error(vm);
1208                 return NJS_ERROR;
1209             }
1210 
1211             memcpy(start, value.short_string.start, size);
1212 
1213         } else {
1214             size = value.long_string.size;
1215             start = value.long_string.data->start;
1216         }
1217 
1218         dst->length = size;
1219         dst->start = start;
1220     }
1221 
1222     return ret;
1223 }
1224 
1225 
1226 njs_int_t
njs_vm_value_string_copy(njs_vm_t * vm,njs_str_t * retval,njs_value_t * value,uintptr_t * next)1227 njs_vm_value_string_copy(njs_vm_t *vm, njs_str_t *retval,
1228     njs_value_t *value, uintptr_t *next)
1229 {
1230     uintptr_t    n;
1231     njs_array_t  *array;
1232 
1233     switch (value->type) {
1234 
1235     case NJS_STRING:
1236         if (*next != 0) {
1237             return NJS_DECLINED;
1238         }
1239 
1240         *next = 1;
1241         break;
1242 
1243     case NJS_ARRAY:
1244         array = njs_array(value);
1245 
1246         do {
1247             n = (*next)++;
1248 
1249             if (n == array->length) {
1250                 return NJS_DECLINED;
1251             }
1252 
1253             value = &array->start[n];
1254 
1255         } while (!njs_is_valid(value));
1256 
1257         break;
1258 
1259     default:
1260         return NJS_ERROR;
1261     }
1262 
1263     return njs_vm_value_to_string(vm, retval, value);
1264 }
1265 
1266 
1267 void *
njs_lvlhsh_alloc(void * data,size_t size)1268 njs_lvlhsh_alloc(void *data, size_t size)
1269 {
1270     return njs_mp_align(data, size, size);
1271 }
1272 
1273 
1274 void
njs_lvlhsh_free(void * data,void * p,size_t size)1275 njs_lvlhsh_free(void *data, void *p, size_t size)
1276 {
1277     njs_mp_free(data, p);
1278 }
1279