[njs] Introduced line level backtrace.

Dmitry Volyntsev xeioex at nginx.com
Thu Jun 18 18:56:34 UTC 2020


details:   https://hg.nginx.org/njs/rev/ea2ec4c3ed7d
branches:  
changeset: 1435:ea2ec4c3ed7d
user:      Dmitry Volyntsev <xeioex at nginx.com>
date:      Thu Jun 18 18:56:24 2020 +0000
description:
Introduced line level backtrace.

This closes #140 issue on Github.

diffstat:

 src/njs.h                |    1 -
 src/njs_disassembler.c   |  119 +++++++------
 src/njs_error.c          |  135 ++++++++++++++++-
 src/njs_function.c       |   17 +-
 src/njs_function.h       |    1 +
 src/njs_generator.c      |  383 ++++++++++++++++++++++++++--------------------
 src/njs_generator.h      |    5 +-
 src/njs_vm.c             |  151 +-----------------
 src/njs_vm.h             |   32 +--
 src/njs_vmcode.c         |    5 +-
 src/test/njs_unit_test.c |   72 +++++--
 test/njs_expect_test.exp |   16 +-
 12 files changed, 503 insertions(+), 434 deletions(-)

diffs (truncated from 2138 to 1000 lines):

diff -r 02634f76f6d0 -r ea2ec4c3ed7d src/njs.h
--- a/src/njs.h	Wed Jun 17 14:38:23 2020 +0000
+++ b/src/njs.h	Thu Jun 18 18:56:24 2020 +0000
@@ -293,7 +293,6 @@ NJS_EXPORT njs_function_t *njs_vm_functi
     njs_function_native_t native);
 
 NJS_EXPORT void njs_disassembler(njs_vm_t *vm);
-NJS_EXPORT void njs_disassemble(u_char *start, u_char *end);
 
 NJS_EXPORT 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);
diff -r 02634f76f6d0 -r ea2ec4c3ed7d src/njs_disassembler.c
--- a/src/njs_disassembler.c	Wed Jun 17 14:38:23 2020 +0000
+++ b/src/njs_disassembler.c	Thu Jun 18 18:56:24 2020 +0000
@@ -15,6 +15,9 @@ typedef struct {
 } njs_code_name_t;
 
 
+static void njs_disassemble(njs_vm_code_t *code);
+
+
 static njs_code_name_t  code_names[] = {
 
     { NJS_VMCODE_OBJECT, sizeof(njs_vmcode_object_t),
@@ -143,11 +146,12 @@ njs_disassembler(njs_vm_t *vm)
     njs_vm_code_t  *code;
 
     code = vm->codes->start;
-    n = vm->codes->items;
+    code += vm->main_index;
+    n = vm->codes->items - vm->main_index;
 
     while (n != 0) {
         njs_printf("%V:%V\n", &code->file, &code->name);
-        njs_disassemble(code->start, code->end);
+        njs_disassemble(code);
         code++;
         n--;
     }
@@ -156,10 +160,11 @@ njs_disassembler(njs_vm_t *vm)
 }
 
 
-void
-njs_disassemble(u_char *start, u_char *end)
+static void
+njs_disassemble(njs_vm_code_t *code)
 {
-    u_char                       *p;
+    u_char                       *p, *start, *end;
+    uint32_t                     line;
     njs_str_t                    *name;
     njs_uint_t                   n;
     njs_code_name_t              *code_name;
@@ -184,7 +189,8 @@ njs_disassemble(u_char *start, u_char *e
     njs_vmcode_try_trampoline_t  *try_tramp;
     njs_vmcode_function_frame_t  *function;
 
-    p = start;
+    start = code->start;
+    end = code->end;
 
     /*
      * On some 32-bit platform uintptr_t is int and compilers warn
@@ -192,14 +198,17 @@ njs_disassemble(u_char *start, u_char *e
      * there is no run-time overhead.
      */
 
+    p = start;
+
     while (p < end) {
         operation = *(njs_vmcode_operation_t *) p;
+        line = njs_lookup_line(code, p - start);
 
         if (operation == NJS_VMCODE_ARRAY) {
             array = (njs_vmcode_array_t *) p;
 
-            njs_printf("%05uz ARRAY             %04Xz %uz%s\n",
-                       p - start, (size_t) array->retval,
+            njs_printf("%5uD | %05uz ARRAY             %04Xz %uz%s\n",
+                       line, p - start, (size_t) array->retval,
                        (size_t) array->length, array->ctor ? " INIT" : "");
 
             p += sizeof(njs_vmcode_array_t);
@@ -210,8 +219,8 @@ njs_disassemble(u_char *start, u_char *e
         if (operation == NJS_VMCODE_IF_TRUE_JUMP) {
             cond_jump = (njs_vmcode_cond_jump_t *) p;
 
-            njs_printf("%05uz JUMP IF TRUE      %04Xz %z\n",
-                       p - start, (size_t) cond_jump->cond,
+            njs_printf("%5uD | %05uz JUMP IF TRUE      %04Xz %z\n",
+                       line, p - start, (size_t) cond_jump->cond,
                        (size_t) cond_jump->offset);
 
             p += sizeof(njs_vmcode_cond_jump_t);
@@ -222,8 +231,8 @@ njs_disassemble(u_char *start, u_char *e
         if (operation == NJS_VMCODE_IF_FALSE_JUMP) {
             cond_jump = (njs_vmcode_cond_jump_t *) p;
 
-            njs_printf("%05uz JUMP IF FALSE     %04Xz %z\n",
-                       p - start, (size_t) cond_jump->cond,
+            njs_printf("%5uD | %05uz JUMP IF FALSE     %04Xz %z\n",
+                       line, p - start, (size_t) cond_jump->cond,
                        (size_t) cond_jump->offset);
 
             p += sizeof(njs_vmcode_cond_jump_t);
@@ -234,8 +243,8 @@ njs_disassemble(u_char *start, u_char *e
         if (operation == NJS_VMCODE_JUMP) {
             jump = (njs_vmcode_jump_t *) p;
 
-            njs_printf("%05uz JUMP              %z\n",
-                       p - start, (size_t) jump->offset);
+            njs_printf("%5uD | %05uz JUMP              %z\n",
+                       line, p - start, (size_t) jump->offset);
 
             p += sizeof(njs_vmcode_jump_t);
 
@@ -245,8 +254,8 @@ njs_disassemble(u_char *start, u_char *e
         if (operation == NJS_VMCODE_IF_EQUAL_JUMP) {
             equal = (njs_vmcode_equal_jump_t *) p;
 
-            njs_printf("%05uz JUMP IF EQUAL     %04Xz %04Xz %z\n",
-                       p - start, (size_t) equal->value1,
+            njs_printf("%5uD | %05uz JUMP IF EQUAL     %04Xz %04Xz %z\n",
+                       line, p - start, (size_t) equal->value1,
                        (size_t) equal->value2, (size_t) equal->offset);
 
             p += sizeof(njs_vmcode_equal_jump_t);
@@ -257,8 +266,8 @@ njs_disassemble(u_char *start, u_char *e
         if (operation == NJS_VMCODE_TEST_IF_TRUE) {
             test_jump = (njs_vmcode_test_jump_t *) p;
 
-            njs_printf("%05uz TEST IF TRUE      %04Xz %04Xz %z\n",
-                       p - start, (size_t) test_jump->retval,
+            njs_printf("%5uD | %05uz TEST IF TRUE      %04Xz %04Xz %z\n",
+                       line, p - start, (size_t) test_jump->retval,
                        (size_t) test_jump->value, (size_t) test_jump->offset);
 
             p += sizeof(njs_vmcode_test_jump_t);
@@ -269,8 +278,8 @@ njs_disassemble(u_char *start, u_char *e
         if (operation == NJS_VMCODE_TEST_IF_FALSE) {
             test_jump = (njs_vmcode_test_jump_t *) p;
 
-            njs_printf("%05uz TEST IF FALSE     %04Xz %04Xz %z\n",
-                       p - start, (size_t) test_jump->retval,
+            njs_printf("%5uD | %05uz TEST IF FALSE     %04Xz %04Xz %z\n",
+                       line, p - start, (size_t) test_jump->retval,
                        (size_t) test_jump->value, (size_t) test_jump->offset);
 
             p += sizeof(njs_vmcode_test_jump_t);
@@ -281,8 +290,8 @@ njs_disassemble(u_char *start, u_char *e
         if (operation == NJS_VMCODE_COALESCE) {
             test_jump = (njs_vmcode_test_jump_t *) p;
 
-            njs_printf("%05uz COALESCE          %04Xz %04Xz %z\n",
-                       p - start, (size_t) test_jump->retval,
+            njs_printf("%5uD | %05uz COALESCE          %04Xz %04Xz %z\n",
+                       line, p - start, (size_t) test_jump->retval,
                        (size_t) test_jump->value, (size_t) test_jump->offset);
 
             p += sizeof(njs_vmcode_test_jump_t);
@@ -293,9 +302,9 @@ njs_disassemble(u_char *start, u_char *e
         if (operation == NJS_VMCODE_FUNCTION_FRAME) {
             function = (njs_vmcode_function_frame_t *) p;
 
-            njs_printf("%05uz FUNCTION FRAME    %04Xz %uz%s\n",
-                       p - start, (size_t) function->name, function->nargs,
-                       function->ctor ? " CTOR" : "");
+            njs_printf("%5uD | %05uz FUNCTION FRAME    %04Xz %uz%s\n",
+                       line, p - start, (size_t) function->name,
+                       function->nargs, function->ctor ? " CTOR" : "");
 
             p += sizeof(njs_vmcode_function_frame_t);
 
@@ -305,8 +314,8 @@ njs_disassemble(u_char *start, u_char *e
         if (operation == NJS_VMCODE_METHOD_FRAME) {
             method = (njs_vmcode_method_frame_t *) p;
 
-            njs_printf("%05uz METHOD FRAME      %04Xz %04Xz %uz%s\n",
-                       p - start, (size_t) method->object,
+            njs_printf("%5uD | %05uz METHOD FRAME      %04Xz %04Xz %uz%s\n",
+                       line, p - start, (size_t) method->object,
                        (size_t) method->method, method->nargs,
                        method->ctor ? " CTOR" : "");
 
@@ -317,8 +326,8 @@ njs_disassemble(u_char *start, u_char *e
         if (operation == NJS_VMCODE_PROPERTY_FOREACH) {
             prop_foreach = (njs_vmcode_prop_foreach_t *) p;
 
-            njs_printf("%05uz PROP FOREACH      %04Xz %04Xz %z\n",
-                       p - start, (size_t) prop_foreach->next,
+            njs_printf("%5uD | %05uz PROP FOREACH      %04Xz %04Xz %z\n",
+                       line, p - start, (size_t) prop_foreach->next,
                        (size_t) prop_foreach->object,
                        (size_t) prop_foreach->offset);
 
@@ -329,8 +338,8 @@ njs_disassemble(u_char *start, u_char *e
         if (operation == NJS_VMCODE_PROPERTY_NEXT) {
             prop_next = (njs_vmcode_prop_next_t *) p;
 
-            njs_printf("%05uz PROP NEXT         %04Xz %04Xz %04Xz %z\n",
-                       p - start, (size_t) prop_next->retval,
+            njs_printf("%5uD | %05uz PROP NEXT         %04Xz %04Xz %04Xz %z\n",
+                       line, p - start, (size_t) prop_next->retval,
                        (size_t) prop_next->object, (size_t) prop_next->next,
                        (size_t) prop_next->offset);
 
@@ -342,8 +351,8 @@ njs_disassemble(u_char *start, u_char *e
         if (operation == NJS_VMCODE_PROPERTY_ACCESSOR) {
             prop_accessor = (njs_vmcode_prop_accessor_t *) p;
 
-            njs_printf("%05uz PROP %s ACCESSOR %04Xz %04Xz %04Xz\n",
-                       p - start,
+            njs_printf("%5uD | %05uz PROP %s ACCESSOR %04Xz %04Xz %04Xz\n",
+                       line, p - start,
                        (prop_accessor->type == NJS_OBJECT_PROP_GETTER)
                            ? "GET" : "SET",
                        (size_t) prop_accessor->value,
@@ -358,8 +367,8 @@ njs_disassemble(u_char *start, u_char *e
         if (operation == NJS_VMCODE_TRY_START) {
             try_start = (njs_vmcode_try_start_t *) p;
 
-            njs_printf("%05uz TRY START         %04Xz %04Xz %z\n",
-                       p - start, (size_t) try_start->exception_value,
+            njs_printf("%5uD | %05uz TRY START         %04Xz %04Xz %z\n",
+                       line, p - start, (size_t) try_start->exception_value,
                        (size_t) try_start->exit_value,
                        (size_t) try_start->offset);
 
@@ -371,8 +380,8 @@ njs_disassemble(u_char *start, u_char *e
         if (operation == NJS_VMCODE_TRY_BREAK) {
             try_tramp = (njs_vmcode_try_trampoline_t *) p;
 
-            njs_printf("%05uz TRY BREAK         %04Xz %z\n",
-                       p - start, (size_t) try_tramp->exit_value,
+            njs_printf("%5uD | %05uz TRY BREAK         %04Xz %z\n",
+                       line, p - start, (size_t) try_tramp->exit_value,
                        (size_t) try_tramp->offset);
 
             p += sizeof(njs_vmcode_try_trampoline_t);
@@ -383,8 +392,8 @@ njs_disassemble(u_char *start, u_char *e
         if (operation == NJS_VMCODE_TRY_CONTINUE) {
             try_tramp = (njs_vmcode_try_trampoline_t *) p;
 
-            njs_printf("%05uz TRY CONTINUE      %04Xz %z\n",
-                       p - start, (size_t) try_tramp->exit_value,
+            njs_printf("%5uD | %05uz TRY CONTINUE      %04Xz %z\n",
+                       line, p - start, (size_t) try_tramp->exit_value,
                        (size_t) try_tramp->offset);
 
             p += sizeof(njs_vmcode_try_trampoline_t);
@@ -395,8 +404,8 @@ njs_disassemble(u_char *start, u_char *e
         if (operation == NJS_VMCODE_TRY_RETURN) {
             try_return = (njs_vmcode_try_return_t *) p;
 
-            njs_printf("%05uz TRY RETURN        %04Xz %04Xz %z\n",
-                       p - start, (size_t) try_return->save,
+            njs_printf("%5uD | %05uz TRY RETURN        %04Xz %04Xz %z\n",
+                       line, p - start, (size_t) try_return->save,
                        (size_t) try_return->retval,
                        (size_t) try_return->offset);
 
@@ -408,8 +417,8 @@ njs_disassemble(u_char *start, u_char *e
         if (operation == NJS_VMCODE_CATCH) {
             catch = (njs_vmcode_catch_t *) p;
 
-            njs_printf("%05uz CATCH             %04Xz %z\n",
-                       p - start, (size_t) catch->exception,
+            njs_printf("%5uD | %05uz CATCH             %04Xz %z\n",
+                       line, p - start, (size_t) catch->exception,
                        (size_t) catch->offset);
 
             p += sizeof(njs_vmcode_catch_t);
@@ -420,8 +429,8 @@ njs_disassemble(u_char *start, u_char *e
         if (operation == NJS_VMCODE_TRY_END) {
             try_end = (njs_vmcode_try_end_t *) p;
 
-            njs_printf("%05uz TRY END           %z\n",
-                       p - start, (size_t) try_end->offset);
+            njs_printf("%5uD | %05uz TRY END           %z\n",
+                       line, p - start, (size_t) try_end->offset);
 
             p += sizeof(njs_vmcode_try_end_t);
 
@@ -431,8 +440,8 @@ njs_disassemble(u_char *start, u_char *e
         if (operation == NJS_VMCODE_FINALLY) {
             finally = (njs_vmcode_finally_t *) p;
 
-            njs_printf("%05uz TRY FINALLY       %04Xz %04Xz %z %z\n",
-                       p - start, (size_t) finally->retval,
+            njs_printf("%5uD | %05uz TRY FINALLY       %04Xz %04Xz %z %z\n",
+                       line, p - start, (size_t) finally->retval,
                        (size_t) finally->exit_value,
                        (size_t) finally->continue_offset,
                        (size_t) finally->break_offset);
@@ -443,7 +452,7 @@ njs_disassemble(u_char *start, u_char *e
         }
 
         if (operation == NJS_VMCODE_REFERENCE_ERROR) {
-            njs_printf("%05uz REFERENCE ERROR\n", p - start);
+            njs_printf("%5uD | %05uz REFERENCE ERROR\n", line, p - start);
 
             p += sizeof(njs_vmcode_reference_error_t);
 
@@ -460,23 +469,23 @@ njs_disassemble(u_char *start, u_char *e
                 if (code_name->size == sizeof(njs_vmcode_3addr_t)) {
                     code3 = (njs_vmcode_3addr_t *) p;
 
-                    njs_printf("%05uz %*s  %04Xz %04Xz %04Xz\n",
-                               p - start, name->length, name->start,
+                    njs_printf("%5uD | %05uz %*s  %04Xz %04Xz %04Xz\n",
+                               line, p - start, name->length, name->start,
                                (size_t) code3->dst, (size_t) code3->src1,
                                (size_t) code3->src2);
 
                 } else if (code_name->size == sizeof(njs_vmcode_2addr_t)) {
                     code2 = (njs_vmcode_2addr_t *) p;
 
-                    njs_printf("%05uz %*s  %04Xz %04Xz\n",
-                               p - start, name->length, name->start,
+                    njs_printf("%5uD | %05uz %*s  %04Xz %04Xz\n",
+                               line, p - start, name->length, name->start,
                                (size_t) code2->dst, (size_t) code2->src);
 
                 } else if (code_name->size == sizeof(njs_vmcode_1addr_t)) {
                     code1 = (njs_vmcode_1addr_t *) p;
 
-                    njs_printf("%05uz %*s  %04Xz\n",
-                               p - start, name->length, name->start,
+                    njs_printf("%5uD | %05uz %*s  %04Xz\n",
+                               line, p - start, name->length, name->start,
                                (size_t) code1->index);
                 }
 
@@ -490,7 +499,7 @@ njs_disassemble(u_char *start, u_char *e
 
         } while (n != 0);
 
-        njs_printf("%05uz UNKNOWN           %04Xz\n",
+        njs_printf("%5uD | %05uz UNKNOWN           %04Xz\n", line,
                    p - start, (size_t) (uintptr_t) operation);
 
         p += sizeof(njs_vmcode_operation_t);
diff -r 02634f76f6d0 -r ea2ec4c3ed7d src/njs_error.c
--- a/src/njs_error.c	Wed Jun 17 14:38:23 2020 +0000
+++ b/src/njs_error.c	Thu Jun 18 18:56:24 2020 +0000
@@ -8,6 +8,19 @@
 #include <njs_main.h>
 
 
+typedef struct {
+    njs_str_t                         name;
+    njs_str_t                         file;
+    uint32_t                          line;
+} njs_backtrace_entry_t;
+
+
+static njs_int_t njs_add_backtrace_entry(njs_vm_t *vm, njs_arr_t *stack,
+    njs_native_frame_t *native_frame);
+static njs_int_t njs_backtrace_to_string(njs_vm_t *vm, njs_arr_t *backtrace,
+    njs_str_t *dst);
+
+
 static const njs_value_t  njs_error_message_string = njs_string("message");
 static const njs_value_t  njs_error_name_string = njs_string("name");
 static const njs_value_t  njs_error_stack_string = njs_string("stack");
@@ -84,7 +97,9 @@ njs_error_stack_new(njs_vm_t *vm, njs_ob
     frame = vm->top_frame;
 
     for ( ;; ) {
-        if (njs_vm_add_backtrace_entry(vm, stack, frame) != NJS_OK) {
+        if ((frame->native || frame->pc != NULL)
+            && njs_add_backtrace_entry(vm, stack, frame) != NJS_OK)
+        {
             break;
         }
 
@@ -97,7 +112,7 @@ njs_error_stack_new(njs_vm_t *vm, njs_ob
 
     njs_string_get(retval, &string);
 
-    ret = njs_vm_backtrace_to_string(vm, stack, &string);
+    ret = njs_backtrace_to_string(vm, stack, &string);
 
     njs_arr_destroy(stack);
 
@@ -121,7 +136,7 @@ njs_error_stack_attach(njs_vm_t *vm, njs
         return NJS_DECLINED;
     }
 
-    if (vm->debug == NULL || vm->start == NULL) {
+    if (njs_slow_path(!vm->options.backtrace || vm->start == NULL)) {
         return NJS_OK;
     }
 
@@ -1147,3 +1162,117 @@ const njs_object_type_init_t  njs_uri_er
     .prototype_props = &njs_uri_error_prototype_init,
     .prototype_value = { .object = { .type = NJS_OBJECT } },
 };
+
+
+static njs_int_t
+njs_add_backtrace_entry(njs_vm_t *vm, njs_arr_t *stack,
+    njs_native_frame_t *native_frame)
+{
+    njs_int_t              ret;
+    njs_uint_t             i;
+    njs_vm_code_t          *code;
+    njs_function_t         *function;
+    njs_backtrace_entry_t  *be;
+
+    function = native_frame->function;
+
+    be = njs_arr_add(stack);
+    if (njs_slow_path(be == NULL)) {
+        return NJS_ERROR;
+    }
+
+    be->line = 0;
+    be->file = njs_str_value("");
+
+    if (function != NULL && function->native) {
+        while (function->bound != NULL) {
+            function = function->u.bound_target;
+        }
+
+        ret = njs_builtin_match_native_function(vm, function, &be->name);
+        if (ret == NJS_OK) {
+            return NJS_OK;
+        }
+
+        be->name = njs_entry_native;
+
+        return NJS_OK;
+    }
+
+    code = vm->codes->start;
+
+    for (i = 0; i < vm->codes->items; i++, code++) {
+        if (code->start <= native_frame->pc
+            && native_frame->pc < code->end)
+        {
+            be->name = code->name;
+            be->line = njs_lookup_line(code, native_frame->pc - code->start);
+            if (!vm->options.quiet) {
+                be->file = code->file;
+            }
+
+            return NJS_OK;
+        }
+    }
+
+    be->name = njs_entry_unknown;
+
+    return NJS_OK;
+}
+
+
+static njs_int_t
+njs_backtrace_to_string(njs_vm_t *vm, njs_arr_t *backtrace, njs_str_t *dst)
+{
+    size_t                 count;
+    njs_chb_t              chain;
+    njs_uint_t             i;
+    njs_backtrace_entry_t  *be, *prev;
+
+    if (backtrace->items == 0) {
+        return NJS_OK;
+    }
+
+    njs_chb_init(&chain, vm->mem_pool);
+
+    njs_chb_append_str(&chain, dst);
+    njs_chb_append(&chain, "\n", 1);
+
+    count = 0;
+    prev = NULL;
+
+    be = backtrace->start;
+
+    for (i = 0; i < backtrace->items; i++) {
+        if (i != 0 && prev->name.start == be->name.start
+            && prev->line == be->line)
+        {
+            count++;
+
+        } else {
+            if (count != 0) {
+                njs_chb_sprintf(&chain, 64, "      repeats %uz times\n", count);
+                count = 0;
+            }
+
+            njs_chb_sprintf(&chain, 10 + be->name.length, "    at %V ",
+                            &be->name);
+
+            if (be->line != 0) {
+                njs_chb_sprintf(&chain, 12 + be->file.length,
+                                "(%V:%uD)\n", &be->file, be->line);
+
+            } else {
+                njs_chb_append(&chain, "(native)\n", 9);
+            }
+        }
+
+        prev = be;
+        be++;
+    }
+
+    njs_chb_join(&chain, dst);
+    njs_chb_destroy(&chain);
+
+    return NJS_OK;
+}
diff -r 02634f76f6d0 -r ea2ec4c3ed7d src/njs_function.c
--- a/src/njs_function.c	Wed Jun 17 14:38:23 2020 +0000
+++ b/src/njs_function.c	Thu Jun 18 18:56:24 2020 +0000
@@ -371,6 +371,7 @@ njs_function_native_frame(njs_vm_t *vm, 
     frame->nargs = function->args_offset + nargs;
     frame->ctor = ctor;
     frame->native = 1;
+    frame->pc = NULL;
 
     value = (njs_value_t *) ((u_char *) frame + NJS_NATIVE_FRAME_SIZE);
     frame->arguments = value;
@@ -454,6 +455,7 @@ njs_function_lambda_frame(njs_vm_t *vm, 
     native_frame->nargs = nargs;
     native_frame->ctor = ctor;
     native_frame->native = 0;
+    native_frame->pc = NULL;
 
     /* Function arguments. */
 
@@ -851,11 +853,11 @@ njs_function_constructor(njs_vm_t *vm, n
     njs_value_t            *body;
     njs_lexer_t            lexer;
     njs_parser_t           *parser;
+    njs_vm_code_t          *code;
     njs_function_t         *function;
     njs_generator_t        generator;
     njs_parser_scope_t     *scope;
     njs_function_lambda_t  *lambda;
-    njs_vmcode_function_t  *code;
 
     if (!vm->options.unsafe) {
         body = njs_argument(args, nargs - 1);
@@ -949,15 +951,18 @@ njs_function_constructor(njs_vm_t *vm, n
 
     njs_memzero(&generator, sizeof(njs_generator_t));
 
-    ret = njs_generate_scope(vm, &generator, scope, &njs_entry_anonymous);
-    if (njs_slow_path(ret != NJS_OK)) {
-        return ret;
+    code = njs_generate_scope(vm, &generator, scope, &njs_entry_anonymous);
+    if (njs_slow_path(code == NULL)) {
+        if (!njs_is_error(&vm->retval)) {
+            njs_internal_error(vm, "njs_generate_scope() failed");
+        }
+
+        return NJS_ERROR;
     }
 
     njs_chb_destroy(&chain);
 
-    code = (njs_vmcode_function_t *) generator.code_start;
-    lambda = code->lambda;
+    lambda = ((njs_vmcode_function_t *) generator.code_start)->lambda;
 
     function = njs_function_alloc(vm, lambda, NULL, 0);
     if (njs_slow_path(function == NULL)) {
diff -r 02634f76f6d0 -r ea2ec4c3ed7d src/njs_function.h
--- a/src/njs_function.h	Wed Jun 17 14:38:23 2020 +0000
+++ b/src/njs_function.h	Thu Jun 18 18:56:24 2020 +0000
@@ -44,6 +44,7 @@ struct njs_function_lambda_s {
 
 struct njs_native_frame_s {
     u_char                         *free;
+    u_char                         *pc;
 
     njs_function_t                 *function;
     njs_native_frame_t             *previous;
diff -r 02634f76f6d0 -r ea2ec4c3ed7d src/njs_generator.c
--- a/src/njs_generator.c	Wed Jun 17 14:38:23 2020 +0000
+++ b/src/njs_generator.c	Thu Jun 18 18:56:24 2020 +0000
@@ -57,6 +57,8 @@ static njs_int_t njs_generator(njs_vm_t 
     njs_parser_node_t *node);
 static u_char *njs_generate_reserve(njs_vm_t *vm, njs_generator_t *generator,
     size_t size);
+static njs_int_t njs_generate_code_map(njs_vm_t *vm, njs_generator_t *generator,
+    njs_parser_node_t *node, u_char *code);
 static njs_int_t njs_generate_name(njs_vm_t *vm, njs_generator_t *generator,
     njs_parser_node_t *node);
 static njs_int_t njs_generate_variable(njs_vm_t *vm, njs_generator_t *generator,
@@ -179,18 +181,20 @@ static njs_int_t njs_generate_global_ref
 static njs_int_t njs_generate_reference_error(njs_vm_t *vm,
     njs_generator_t *generator, njs_parser_node_t *node);
 
-static njs_int_t njs_generate_function_debug(njs_vm_t *vm,
-    const njs_str_t *name, njs_function_lambda_t *lambda,
-    njs_parser_node_t *node);
-
-
-#define njs_generate_code(generator, type, _code, _op, nargs)                 \
+
+#define njs_generate_code(generator, type, _code, _op, nargs, nd)             \
     do {                                                                      \
         _code = (type *) njs_generate_reserve(vm, generator, sizeof(type));   \
         if (njs_slow_path(_code == NULL)) {                                   \
             return NJS_ERROR;                                                 \
         }                                                                     \
                                                                               \
+        if (njs_generate_code_map(vm, generator, nd, (u_char *) _code)        \
+            != NJS_OK)                                                        \
+        {                                                                     \
+            return NJS_ERROR;                                                 \
+        }                                                                     \
+                                                                              \
         generator->code_end += sizeof(type);                                  \
                                                                               \
         _code->code.operation = _op;                                          \
@@ -201,15 +205,15 @@ static njs_int_t njs_generate_function_d
 #define njs_generate_code_jump(generator, _code, _offset)                     \
     do {                                                                      \
         njs_generate_code(generator, njs_vmcode_jump_t, _code,                \
-                          NJS_VMCODE_JUMP, 0);                                \
+                          NJS_VMCODE_JUMP, 0, NULL);                          \
         _code->offset = _offset;                                              \
     } while (0)
 
 
-#define njs_generate_code_move(generator, _code, _dst, _src)                  \
+#define njs_generate_code_move(generator, _code, _dst, _src, node)            \
     do {                                                                      \
         njs_generate_code(generator, njs_vmcode_move_t, _code,                \
-                          NJS_VMCODE_MOVE, 2);                                \
+                          NJS_VMCODE_MOVE, 2, node);                          \
         _code->dst = _dst;                                                    \
         _code->src = _src;                                                    \
     } while (0)
@@ -536,6 +540,54 @@ njs_generate_reserve(njs_vm_t *vm, njs_g
 
 
 static njs_int_t
+njs_generate_code_map(njs_vm_t *vm, njs_generator_t *generator,
+    njs_parser_node_t *node, u_char *code)
+{
+    njs_arr_t          *map;
+    njs_vm_line_num_t  *last;
+
+    map = generator->lines;
+
+    if (map != NULL && node != NULL) {
+        last = (map->items != 0) ? njs_arr_last(map) : NULL;
+        if (last == NULL || (node->token_line != last->line)) {
+            last = njs_arr_add(map);
+            if (njs_slow_path(last == NULL)) {
+                return NJS_ERROR;
+            }
+
+            last->line = node->token_line;
+            last->offset = njs_code_offset(generator, code);
+        }
+    }
+
+    return NJS_OK;
+}
+
+
+uint32_t
+njs_lookup_line(njs_vm_code_t *code, uint32_t offset)
+{
+    njs_uint_t         n;
+    njs_vm_line_num_t  *map;
+
+    n = (code->lines != NULL) ? code->lines->items : 0;
+    map = (njs_vm_line_num_t *) code->lines->start;
+
+    while (n != 0) {
+        if (offset >= map->offset && (n == 1 || offset < map[1].offset)) {
+            return map->line;
+        }
+
+        map++;
+        n--;
+    }
+
+    return 0;
+}
+
+
+static njs_int_t
 njs_generate_name(njs_vm_t *vm, njs_generator_t *generator,
     njs_parser_node_t *node)
 {
@@ -552,7 +604,7 @@ njs_generate_name(njs_vm_t *vm, njs_gene
         }
 
         njs_generate_code(generator, njs_vmcode_object_copy_t, copy,
-                          NJS_VMCODE_OBJECT_COPY, 2);
+                          NJS_VMCODE_OBJECT_COPY, 2, node);
         copy->retval = node->index;
         copy->object = var->index;
 
@@ -626,7 +678,8 @@ njs_generate_var_statement(njs_vm_t *vm,
      * empty object or expression result is stored directly in variable.
      */
     if (lvalue->index != expr->index) {
-        njs_generate_code_move(generator, move, lvalue->index, expr->index);
+        njs_generate_code_move(generator, move, lvalue->index, expr->index,
+                               lvalue);
     }
 
     node->index = expr->index;
@@ -653,7 +706,7 @@ njs_generate_if_statement(njs_vm_t *vm, 
     }
 
     njs_generate_code(generator, njs_vmcode_cond_jump_t, cond_jump,
-                      NJS_VMCODE_IF_FALSE_JUMP, 2);
+                      NJS_VMCODE_IF_FALSE_JUMP, 2, node);
     cond_jump->cond = node->left->index;
 
     ret = njs_generate_node_index_release(vm, generator, node->left);
@@ -728,7 +781,7 @@ njs_generate_cond_expression(njs_vm_t *v
     }
 
     njs_generate_code(generator, njs_vmcode_cond_jump_t, cond_jump,
-                      NJS_VMCODE_IF_FALSE_JUMP, 2);
+                      NJS_VMCODE_IF_FALSE_JUMP, 2, node);
 
     cond_jump_offset = njs_code_offset(generator, cond_jump);
     cond_jump->cond = node->left->index;
@@ -755,7 +808,7 @@ njs_generate_cond_expression(njs_vm_t *v
 
     if (node->index != branch->left->index) {
         njs_generate_code_move(generator, move, node->index,
-                               branch->left->index);
+                               branch->left->index, node);
     }
 
     ret = njs_generate_node_index_release(vm, generator, branch->left);
@@ -778,7 +831,7 @@ njs_generate_cond_expression(njs_vm_t *v
 
     if (node->index != branch->right->index) {
         njs_generate_code_move(generator, move, node->index,
-                               branch->right->index);
+                               branch->right->index, node);
     }
 
     njs_code_set_jump_offset(generator, njs_vmcode_cond_jump_t, jump_offset);
@@ -822,7 +875,7 @@ njs_generate_switch_statement(njs_vm_t *
             return NJS_ERROR;
         }
 
-        njs_generate_code_move(generator, move, index, expr->index);
+        njs_generate_code_move(generator, move, index, expr->index, swtch);
     }
 
     ret = njs_generate_start_block(vm, generator, NJS_GENERATOR_SWITCH,
@@ -848,7 +901,7 @@ njs_generate_switch_statement(njs_vm_t *
             }
 
             njs_generate_code(generator, njs_vmcode_equal_jump_t, equal,
-                              NJS_VMCODE_IF_EQUAL_JUMP, 3);
+                              NJS_VMCODE_IF_EQUAL_JUMP, 3, branch);
             equal->offset = offsetof(njs_vmcode_equal_jump_t, offset);
             equal->value1 = index;
             equal->value2 = node->left->index;
@@ -971,7 +1024,7 @@ njs_generate_while_statement(njs_vm_t *v
     }
 
     njs_generate_code(generator, njs_vmcode_cond_jump_t, cond_jump,
-                      NJS_VMCODE_IF_TRUE_JUMP, 2);
+                      NJS_VMCODE_IF_TRUE_JUMP, 2, condition);
     cond_jump->offset = loop_offset - njs_code_offset(generator, cond_jump);
     cond_jump->cond = condition->index;
 
@@ -1017,7 +1070,7 @@ njs_generate_do_while_statement(njs_vm_t
     }
 
     njs_generate_code(generator, njs_vmcode_cond_jump_t, cond_jump,
-                      NJS_VMCODE_IF_TRUE_JUMP, 2);
+                      NJS_VMCODE_IF_TRUE_JUMP, 2, condition);
     cond_jump->offset = loop_offset - njs_code_offset(generator, cond_jump);
     cond_jump->cond = condition->index;
 
@@ -1111,7 +1164,7 @@ njs_generate_for_statement(njs_vm_t *vm,
         }
 
         njs_generate_code(generator, njs_vmcode_cond_jump_t, cond_jump,
-                          NJS_VMCODE_IF_TRUE_JUMP, 2);
+                          NJS_VMCODE_IF_TRUE_JUMP, 2, condition);
         cond_jump->offset = loop_offset - njs_code_offset(generator, cond_jump);
         cond_jump->cond = condition->index;
 
@@ -1156,7 +1209,7 @@ njs_generate_for_in_statement(njs_vm_t *
     }
 
     njs_generate_code(generator, njs_vmcode_prop_foreach_t, prop_foreach,
-                      NJS_VMCODE_PROPERTY_FOREACH, 2);
+                      NJS_VMCODE_PROPERTY_FOREACH, 2, foreach);
     prop_offset = njs_code_offset(generator, prop_foreach);
     prop_foreach->object = foreach->right->index;
 
@@ -1188,7 +1241,7 @@ njs_generate_for_in_statement(njs_vm_t *
     }
 
     njs_generate_code(generator, njs_vmcode_prop_next_t, prop_next,
-                      NJS_VMCODE_PROPERTY_NEXT, 3);
+                      NJS_VMCODE_PROPERTY_NEXT, 3, node->left->left);
     prop_offset = njs_code_offset(generator, prop_next);
     prop_next->retval = foreach->left->index;
     prop_next->object = foreach->right->index;
@@ -1575,7 +1628,7 @@ njs_generate_stop_statement(njs_vm_t *vm
 
     if (njs_fast_path(ret == NJS_OK)) {
         njs_generate_code(generator, njs_vmcode_stop_t, stop,
-                          NJS_VMCODE_STOP, 1);
+                          NJS_VMCODE_STOP, 1, node);
 
         index = NJS_INDEX_NONE;
         node = node->right;
@@ -1645,7 +1698,8 @@ njs_generate_assignment(njs_vm_t *vm, nj
          * empty object or expression result is stored directly in variable.
          */
         if (lvalue->index != expr->index) {
-            njs_generate_code_move(generator, move, lvalue->index, expr->index);
+            njs_generate_code_move(generator, move, lvalue->index, expr->index,
+                                   expr);
         }
 
         node->index = expr->index;
@@ -1687,7 +1741,7 @@ njs_generate_assignment(njs_vm_t *vm, nj
                 return NJS_ERROR;
             }
 
-            njs_generate_code_move(generator, move, index, src);
+            njs_generate_code_move(generator, move, index, src, object);
         }
 
         if (property->token_type == NJS_TOKEN_NAME) {
@@ -1698,7 +1752,7 @@ njs_generate_assignment(njs_vm_t *vm, nj
                 return NJS_ERROR;
             }
 
-            njs_generate_code_move(generator, move, index, src);
+            njs_generate_code_move(generator, move, index, src, property);
         }
     }
 
@@ -1710,18 +1764,18 @@ njs_generate_assignment(njs_vm_t *vm, nj
     switch (lvalue->token_type) {
     case NJS_TOKEN_PROPERTY_INIT:
         njs_generate_code(generator, njs_vmcode_prop_set_t, prop_set,
-                          NJS_VMCODE_PROPERTY_INIT, 3);
+                          NJS_VMCODE_PROPERTY_INIT, 3, expr);
         break;
 
     case NJS_TOKEN_PROTO_INIT:
         njs_generate_code(generator, njs_vmcode_prop_set_t, prop_set,
-                          NJS_VMCODE_PROTO_INIT, 3);
+                          NJS_VMCODE_PROTO_INIT, 3, expr);
         break;
 
     default:
         /* NJS_VMCODE_PROPERTY_SET */
         njs_generate_code(generator, njs_vmcode_prop_set_t, prop_set,
-                          NJS_VMCODE_PROPERTY_SET, 3);
+                          NJS_VMCODE_PROPERTY_SET, 3, expr);
     }
 
     prop_set->value = expr->index;
@@ -1763,7 +1817,7 @@ njs_generate_operation_assignment(njs_vm
             /* Preserve variable value if it may be changed by expression. */
 
             njs_generate_code(generator, njs_vmcode_move_t, move,
-                              NJS_VMCODE_MOVE, 2);
+                              NJS_VMCODE_MOVE, 2, expr);
             move->src = lvalue->index;
 
             index = njs_generate_temp_index_get(vm, generator, expr);
@@ -1780,7 +1834,7 @@ njs_generate_operation_assignment(njs_vm
         }
 
         njs_generate_code(generator, njs_vmcode_3addr_t, code,
-                          node->u.operation, 3);
+                          node->u.operation, 3, expr);
         code->dst = lvalue->index;
         code->src1 = index;
         code->src2 = expr->index;
@@ -1823,7 +1877,7 @@ njs_generate_operation_assignment(njs_vm
     }
 
     njs_generate_code(generator, njs_vmcode_prop_get_t, prop_get,
-                      NJS_VMCODE_PROPERTY_GET, 3);
+                      NJS_VMCODE_PROPERTY_GET, 3, property);
     prop_get->value = index;
     prop_get->object = object->index;
     prop_get->property = property->index;
@@ -1836,13 +1890,13 @@ njs_generate_operation_assignment(njs_vm
     }
 
     njs_generate_code(generator, njs_vmcode_3addr_t, code,
-                      node->u.operation, 3);
+                      node->u.operation, 3, expr);
     code->dst = node->index;
     code->src1 = node->index;
     code->src2 = expr->index;
 
     njs_generate_code(generator, njs_vmcode_prop_set_t, prop_set,
-                      NJS_VMCODE_PROPERTY_SET, 3);
+                      NJS_VMCODE_PROPERTY_SET, 3, expr);
     prop_set->value = node->index;
     prop_set->object = object->index;
     prop_set->property = property->index;
@@ -1868,7 +1922,7 @@ njs_generate_object(njs_vm_t *vm, njs_ge
     }
 
     njs_generate_code(generator, njs_vmcode_object_t, object,
-                      NJS_VMCODE_OBJECT, 1);
+                      NJS_VMCODE_OBJECT, 1, node);
     object->retval = node->index;
 
     /* Initialize object. */
@@ -1907,7 +1961,7 @@ njs_generate_property_accessor(njs_vm_t 
     }
 
     njs_generate_code(generator, njs_vmcode_prop_accessor_t, accessor,
-                      NJS_VMCODE_PROPERTY_ACCESSOR, 3);
+                      NJS_VMCODE_PROPERTY_ACCESSOR, 3, function);
 
     accessor->value = function->index;
     accessor->object = object->index;
@@ -1931,7 +1985,7 @@ njs_generate_array(njs_vm_t *vm, njs_gen
     }
 
     njs_generate_code(generator, njs_vmcode_array_t, array,
-                      NJS_VMCODE_ARRAY, 1);
+                      NJS_VMCODE_ARRAY, 1, node);
     array->ctor = node->ctor;
     array->retval = node->index;
     array->length = node->u.length;
@@ -1957,21 +2011,12 @@ njs_generate_function(njs_vm_t *vm, njs_
     name = module ? &njs_entry_module : &njs_entry_anonymous;
 
     ret = njs_generate_function_scope(vm, lambda, node, name);
-
     if (njs_slow_path(ret != NJS_OK)) {
         return ret;
     }
 
-    if (vm->debug != NULL) {
-        ret = njs_generate_function_debug(vm, name, lambda,
-                                          module ? node->right : node);
-        if (njs_slow_path(ret != NJS_OK)) {
-            return ret;
-        }
-    }
-
     njs_generate_code(generator, njs_vmcode_function_t, function,
-                      NJS_VMCODE_FUNCTION, 1);
+                      NJS_VMCODE_FUNCTION, 1, node);
     function->lambda = lambda;
 
     node->index = njs_generate_object_dest_index(vm, generator, node);
@@ -1997,7 +2042,7 @@ njs_generate_regexp(njs_vm_t *vm, njs_ge
     }
 
     njs_generate_code(generator, njs_vmcode_regexp_t, regexp,
-                      NJS_VMCODE_REGEXP, 1);
+                      NJS_VMCODE_REGEXP, 1, node);
     regexp->retval = node->index;
     regexp->pattern = node->u.value.data.u.data;
 
@@ -2018,7 +2063,7 @@ njs_generate_template_literal(njs_vm_t *
     }
 
     njs_generate_code(generator, njs_vmcode_template_literal_t, code,
-                      NJS_VMCODE_TEMPLATE_LITERAL, 1);
+                      NJS_VMCODE_TEMPLATE_LITERAL, 1, node);
     code->retval = node->left->index;
 
     node->index = node->left->index;
@@ -2042,7 +2087,7 @@ njs_generate_test_jump_expression(njs_vm
     }
 
     njs_generate_code(generator, njs_vmcode_test_jump_t, test_jump,
-                      node->u.operation, 2);
+                      node->u.operation, 2, node);
     jump_offset = njs_code_offset(generator, test_jump);
     test_jump->value = node->left->index;
 
@@ -2066,7 +2111,7 @@ njs_generate_test_jump_expression(njs_vm
 
     if (node->index != node->right->index) {
         njs_generate_code_move(generator, move, node->index,
-                               node->right->index);
+                               node->right->index, node);
     }
 
     njs_code_set_jump_offset(generator, njs_vmcode_test_jump_t, jump_offset);
@@ -2098,7 +2143,7 @@ njs_generate_3addr_operation(njs_vm_t *v


More information about the nginx-devel mailing list