]> granicus.if.org Git - vim/commitdiff
patch 8.2.2996: Vim9: when debugging cannot inspect local variables v8.2.2996
authorBram Moolenaar <Bram@vim.org>
Mon, 14 Jun 2021 18:40:37 +0000 (20:40 +0200)
committerBram Moolenaar <Bram@vim.org>
Mon, 14 Jun 2021 18:40:37 +0000 (20:40 +0200)
Problem:    Vim9: when debugging cannot inspect local variables.
Solution:   Make local variables available when debugging.

src/debugger.c
src/proto/vim9execute.pro
src/testdir/test_debugger.vim
src/version.c
src/vim9.h
src/vim9compile.c
src/vim9execute.c

index 9c3c4a178c2ffd20f92ddb84fd1779b7cf95a2ed..b96846310ab848aff36dcc72ed8805cd4baad840 100644 (file)
@@ -218,7 +218,7 @@ do_debug(char_u *cmd)
 
            if (last_cmd != 0)
            {
-               // Execute debug command: decided where to break next and
+               // Execute debug command: decide where to break next and
                // return.
                switch (last_cmd)
                {
index 5712fcf76f36362ad99e4d3278dd70ec30b508e7..8b4b6eaafcdd32456e27dbc7c321358f0bfde69c 100644 (file)
@@ -4,6 +4,7 @@ void funcstack_check_refcount(funcstack_T *funcstack);
 char_u *char_from_string(char_u *str, varnumber_T index);
 char_u *string_slice(char_u *str, varnumber_T first, varnumber_T last, int exclusive);
 int fill_partial_and_closure(partial_T *pt, ufunc_T *ufunc, ectx_T *ectx);
+typval_T *lookup_debug_var(char_u *name);
 int exe_typval_instr(typval_T *tv, typval_T *rettv);
 char_u *exe_substitute_instr(void);
 int call_def_function(ufunc_T *ufunc, int argc_arg, typval_T *argv, partial_T *partial, typval_T *rettv);
index ed4baccf11d9b7665c8d48e5a649f9a95aef56e1..6ee71c3848dd17c4a588508f060d490ff178651a 100644 (file)
@@ -853,6 +853,7 @@ func Test_Backtrace_DefFunction()
     enddef
 
     def g:GlobalFunction()
+      var some = "some var"
       CallAFunction()
     enddef
 
@@ -884,19 +885,21 @@ func Test_Backtrace_DefFunction()
                 \ ':debug call GlobalFunction()',
                 \ ['cmd: call GlobalFunction()'])
 
-  call RunDbgCmd(buf, 'step', ['line 1:   CallAFunction()'])
+  call RunDbgCmd(buf, 'step', ['line 1:   var some = "some var"'])
+  call RunDbgCmd(buf, 'step', ['line 2:   CallAFunction()'])
+  call RunDbgCmd(buf, 'echo some', ['some var'])
 
-  " FIXME: not quite right
   call RunDbgCmd(buf, 'backtrace', [
         \ '\V>backtrace',
         \ '\V->0 function GlobalFunction',
-        \ '\Vline 1:   CallAFunction()',
+        \ '\Vline 2:   CallAFunction()',
         \ ],
         \ #{match: 'pattern'})
 
   call RunDbgCmd(buf, 'step', ['line 1:   SourceAnotherFile()'])
   call RunDbgCmd(buf, 'step', ['line 1:   source Xtest2.vim'])
-  " FIXME: repeated line
+  " Repeated line, because we fist are in the compiled function before the
+  " EXEC and then in do_cmdline() before the :source command.
   call RunDbgCmd(buf, 'step', ['line 1: source Xtest2.vim'])
   call RunDbgCmd(buf, 'step', ['line 1: vim9script'])
   call RunDbgCmd(buf, 'step', ['line 3: def DoAThing(): number'])
@@ -906,7 +909,7 @@ func Test_Backtrace_DefFunction()
   call RunDbgCmd(buf, 'step', ['line 14: File2Function()'])
   call RunDbgCmd(buf, 'backtrace', [
         \ '\V>backtrace',
-        \ '\V  3 function GlobalFunction[1]',
+        \ '\V  3 function GlobalFunction[2]',
         \ '\V  2 <SNR>\.\*_CallAFunction[1]',
         \ '\V  1 <SNR>\.\*_SourceAnotherFile[1]',
         \ '\V->0 script ' .. getcwd() .. '/Xtest2.vim',
@@ -917,7 +920,7 @@ func Test_Backtrace_DefFunction()
   call RunDbgCmd(buf, 'next', ['line 15: End of sourced file'])
   call RunDbgCmd(buf, 'backtrace', [
         \ '\V>backtrace',
-        \ '\V  3 function GlobalFunction[1]',
+        \ '\V  3 function GlobalFunction[2]',
         \ '\V  2 <SNR>\.\*_CallAFunction[1]',
         \ '\V  1 <SNR>\.\*_SourceAnotherFile[1]',
         \ '\V->0 script ' .. getcwd() .. '/Xtest2.vim',
index b146efb69abc9d88ff1b12766b874ad06f7088b0..29018a8f79518337f06d5ed3d9a4c9aaa48f9305 100644 (file)
@@ -750,6 +750,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    2996,
 /**/
     2995,
 /**/
index dd390716b5dd4b7ea34b4137cb6f9a0a4f89b478..04972dcecf9c1b40da42d578c04c5dfd85978519 100644 (file)
@@ -168,7 +168,8 @@ typedef enum {
     ISN_PROF_START, // start a line for profiling
     ISN_PROF_END,   // end a line for profiling
 
-    ISN_DEBUG,     // check for debug breakpoint
+    ISN_DEBUG,     // check for debug breakpoint, isn_arg.number is current
+                   // number of local variables
 
     ISN_UNPACK,            // unpack list into items, uses isn_arg.unpack
     ISN_SHUFFLE,    // move item on stack up or down
@@ -447,6 +448,7 @@ struct dfunc_S {
                                    // was compiled.
 
     garray_T   df_def_args_isn;    // default argument instructions
+    garray_T   df_var_names;       // names of local vars
 
     // After compiling "df_instr" and/or "df_instr_prof" is not NULL.
     isn_T      *df_instr;          // function body to be executed
index 12f41f1259c8ae717f2bd7c11b21557f1f55875e..dbad31bd55611bc6b54533c5b953b4116fb55ce8 100644 (file)
@@ -177,7 +177,6 @@ struct cctx_S {
     compiletype_T ctx_compile_type;
 
     garray_T   ctx_locals;         // currently visible local variables
-    int                ctx_locals_count;   // total number of local variables
 
     int                ctx_has_closure;    // set to one if a closures was created in
                                    // the function
@@ -574,6 +573,22 @@ generate_instr_type(cctx_T *cctx, isntype_T isn_type, type_T *type)
     return isn;
 }
 
+/*
+ * Generate an ISN_DEBUG instruction.
+ */
+    static isn_T *
+generate_instr_debug(cctx_T *cctx)
+{
+    isn_T      *isn;
+    dfunc_T    *dfunc = ((dfunc_T *)def_functions.ga_data)
+                                              + cctx->ctx_ufunc->uf_dfunc_idx;
+
+    if ((isn = generate_instr(cctx, ISN_DEBUG)) == NULL)
+       return NULL;
+    isn->isn_arg.number = dfunc->df_var_names.ga_len;
+    return isn;
+}
+
 /*
  * If type at "offset" isn't already VAR_STRING then generate ISN_2STRING.
  * But only for simple types.
@@ -2365,6 +2380,7 @@ reserve_local(
        type_T  *type)
 {
     lvar_T  *lvar;
+    dfunc_T *dfunc;
 
     if (arg_exists(name, len, NULL, NULL, NULL, cctx) == OK)
     {
@@ -2381,12 +2397,20 @@ reserve_local(
     // the last ones when leaving a scope, but then variables used in a closure
     // might get overwritten.  To keep things simple do not re-use stack
     // entries.  This is less efficient, but memory is cheap these days.
-    lvar->lv_idx = cctx->ctx_locals_count++;
+    dfunc = ((dfunc_T *)def_functions.ga_data) + cctx->ctx_ufunc->uf_dfunc_idx;
+    lvar->lv_idx = dfunc->df_var_names.ga_len;
 
     lvar->lv_name = vim_strnsave(name, len == 0 ? STRLEN(name) : len);
     lvar->lv_const = isConst;
     lvar->lv_type = type;
 
+    // Remember the name for debugging.
+    if (ga_grow(&dfunc->df_var_names, 1) == FAIL)
+       return NULL;
+    ((char_u **)dfunc->df_var_names.ga_data)[lvar->lv_idx] =
+                                                   vim_strsave(lvar->lv_name);
+    ++dfunc->df_var_names.ga_len;
+
     return lvar;
 }
 
@@ -7486,7 +7510,7 @@ compile_elseif(char_u *arg, cctx_T *cctx)
        if (cctx->ctx_compile_type == CT_DEBUG)
        {
            // the previous block was skipped, may want to debug this line
-           generate_instr(cctx, ISN_DEBUG);
+           generate_instr_debug(cctx);
            instr_count = instr->ga_len;
        }
     }
@@ -8239,7 +8263,7 @@ compile_catch(char_u *arg, cctx_T *cctx UNUSED)
        }
 #endif
        if (cctx->ctx_compile_type == CT_DEBUG)
-           generate_instr(cctx, ISN_DEBUG);
+           generate_instr_debug(cctx);
     }
 
     p = skipwhite(arg);
@@ -8976,6 +9000,7 @@ add_def_function(ufunc_T *ufunc)
     ufunc->uf_dfunc_idx = dfunc->df_idx;
     dfunc->df_ufunc = ufunc;
     dfunc->df_name = vim_strsave(ufunc->uf_name);
+    ga_init2(&dfunc->df_var_names, sizeof(char_u *), 10);
     ++dfunc->df_refcount;
     ++def_functions.ga_len;
     return OK;
@@ -9026,7 +9051,21 @@ compile_def_function(
     {
        dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data)
                                                         + ufunc->uf_dfunc_idx;
-       delete_def_function_contents(dfunc, FALSE);
+       isn_T   *instr_dest;
+
+       switch (compile_type)
+       {
+           case CT_PROFILE:
+#ifdef FEAT_PROFILE
+                           instr_dest = dfunc->df_instr_prof; break;
+#endif
+           case CT_NONE:   instr_dest = dfunc->df_instr; break;
+           case CT_DEBUG:  instr_dest = dfunc->df_instr_debug; break;
+       }
+       if (instr_dest != NULL)
+           // Was compiled in this mode before: Free old instructions.
+           delete_def_function_contents(dfunc, FALSE);
+       ga_clear_strings(&dfunc->df_var_names);
     }
     else
     {
@@ -9202,7 +9241,7 @@ compile_def_function(
                                                  && cctx.ctx_skip != SKIP_YES)
        {
            debug_lnum = cctx.ctx_lnum;
-           generate_instr(&cctx, ISN_DEBUG);
+           generate_instr_debug(&cctx);
        }
 
        // Some things can be recognized by the first character.
@@ -9670,7 +9709,7 @@ nextline:
            dfunc->df_instr = instr->ga_data;
            dfunc->df_instr_count = instr->ga_len;
        }
-       dfunc->df_varcount = cctx.ctx_locals_count;
+       dfunc->df_varcount = dfunc->df_var_names.ga_len;
        dfunc->df_has_closure = cctx.ctx_has_closure;
        if (cctx.ctx_outer_used)
            ufunc->uf_flags |= FC_CLOSURE;
@@ -10037,6 +10076,7 @@ delete_def_function_contents(dfunc_T *dfunc, int mark_deleted)
     int idx;
 
     ga_clear(&dfunc->df_def_args_isn);
+    ga_clear_strings(&dfunc->df_var_names);
 
     if (dfunc->df_instr != NULL)
     {
index d55fa0c9d3aaf9c3694608e5016261c3a27908b0..b8ecf9b3784becd3e88e59f73517ab9a85b7caa3 100644 (file)
@@ -172,6 +172,7 @@ call_dfunc(
     int                argcount = argcount_arg;
     dfunc_T    *dfunc = ((dfunc_T *)def_functions.ga_data) + cdf_idx;
     ufunc_T    *ufunc = dfunc->df_ufunc;
+    int                did_emsg_before = did_emsg_cumul + did_emsg;
     int                arg_to_add;
     int                vararg_count = 0;
     int                varcount;
@@ -211,6 +212,19 @@ call_dfunc(
     }
 #endif
 
+    // When debugging and using "cont" switches to the not-debugged
+    // instructions, may need to still compile them.
+    if ((func_needs_compiling(ufunc, COMPILE_TYPE(ufunc))
+              && compile_def_function(ufunc, FALSE, COMPILE_TYPE(ufunc), NULL)
+                                                                     == FAIL)
+           || INSTRUCTIONS(dfunc) == NULL)
+    {
+       if (did_emsg_cumul + did_emsg == did_emsg_before)
+           semsg(_(e_function_is_not_compiled_str),
+                                                  printable_func_name(ufunc));
+       return FAIL;
+    }
+
     if (ufunc->uf_va_name != NULL)
     {
        // Need to make a list out of the vararg arguments.
@@ -1382,6 +1396,36 @@ typedef struct subs_expr_S {
 // Get pointer to a local variable on the stack.  Negative for arguments.
 #define STACK_TV_VAR(idx) (((typval_T *)ectx->ec_stack.ga_data) + ectx->ec_frame_idx + STACK_FRAME_SIZE + idx)
 
+// Set when calling do_debug().
+static ectx_T  *debug_context = NULL;
+static int     debug_arg_count;
+
+/*
+ * When debugging lookup "name" and return the typeval.
+ * When not found return NULL.
+ */
+    typval_T *
+lookup_debug_var(char_u *name)
+{
+    int                    idx;
+    dfunc_T        *dfunc;
+    ectx_T         *ectx = debug_context;
+
+    if (ectx == NULL)
+       return NULL;
+    dfunc = ((dfunc_T *)def_functions.ga_data) + ectx->ec_dfunc_idx;
+
+    // Go through the local variable names, from last to first.
+    for (idx = debug_arg_count - 1; idx >= 0; --idx)
+    {
+       char_u *s = ((char_u **)dfunc->df_var_names.ga_data)[idx];
+       if (STRCMP(s, name) == 0)
+           return STACK_TV_VAR(idx);
+    }
+
+    return NULL;
+}
+
 /*
  * Execute instructions in execution context "ectx".
  * Return OK or FAIL;
@@ -4087,7 +4131,7 @@ exec_instructions(ectx_T *ectx)
                    funccall_T cookie;
                    ufunc_T         *cur_ufunc =
                                    (((dfunc_T *)def_functions.ga_data)
-                                                + ectx->ec_dfunc_idx)->df_ufunc;
+                                              + ectx->ec_dfunc_idx)->df_ufunc;
 
                    cookie.func = cur_ufunc;
                    if (iptr->isn_type == ISN_PROF_START)
@@ -4110,11 +4154,14 @@ exec_instructions(ectx_T *ectx)
                                               + ectx->ec_dfunc_idx)->df_ufunc;
 
                    SOURCING_LNUM = iptr->isn_lnum;
+                   debug_context = ectx;
+                   debug_arg_count = iptr->isn_arg.number;
                    line = ((char_u **)ufunc->uf_lines.ga_data)[
                                                           iptr->isn_lnum - 1];
                    if (line == NULL)
                        line = (char_u *)"[empty]";
                    do_debug(line);
+                   debug_context = NULL;
                }
                break;
 
@@ -5346,7 +5393,8 @@ list_instructions(char *pfx, isn_T *instr, int instr_count, ufunc_T *ufunc)
                break;
 
            case ISN_DEBUG:
-               smsg("%s%4d DEBUG line %d", pfx, current, iptr->isn_lnum);
+               smsg("%s%4d DEBUG line %d varcount %lld", pfx, current,
+                                        iptr->isn_lnum, iptr->isn_arg.number);
                break;
 
            case ISN_UNPACK: smsg("%s%4d UNPACK %d%s", pfx, current,