To: vim_dev@googlegroups.com Subject: Patch 8.2.2996 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 8.2.2996 Problem: Vim9: when debugging cannot inspect local variables. Solution: Make local variables available when debugging. Files: src/vim9execute.c, src/proto/vim9execute.pro, src/vim9compile.c, src/vim9.h, src/debugger.c, src/testdir/test_debugger.vim *** ../vim-8.2.2995/src/vim9execute.c 2021-06-13 18:38:44.688673497 +0200 --- src/vim9execute.c 2021-06-14 19:46:43.291876427 +0200 *************** *** 172,177 **** --- 172,178 ---- 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,216 **** --- 212,230 ---- } #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,1387 **** --- 1396,1431 ---- // 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,4093 **** funccall_T cookie; ufunc_T *cur_ufunc = (((dfunc_T *)def_functions.ga_data) ! + ectx->ec_dfunc_idx)->df_ufunc; cookie.func = cur_ufunc; if (iptr->isn_type == ISN_PROF_START) --- 4131,4137 ---- funccall_T cookie; ufunc_T *cur_ufunc = (((dfunc_T *)def_functions.ga_data) ! + ectx->ec_dfunc_idx)->df_ufunc; cookie.func = cur_ufunc; if (iptr->isn_type == ISN_PROF_START) *************** *** 4110,4120 **** --- 4154,4167 ---- + 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,5352 **** break; case ISN_DEBUG: ! smsg("%s%4d DEBUG line %d", pfx, current, iptr->isn_lnum); break; case ISN_UNPACK: smsg("%s%4d UNPACK %d%s", pfx, current, --- 5393,5400 ---- break; case ISN_DEBUG: ! 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, *** ../vim-8.2.2995/src/proto/vim9execute.pro 2021-06-13 18:38:44.688673497 +0200 --- src/proto/vim9execute.pro 2021-06-14 16:21:13.469944772 +0200 *************** *** 4,9 **** --- 4,10 ---- 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); *** ../vim-8.2.2995/src/vim9compile.c 2021-06-13 21:52:42.525260056 +0200 --- src/vim9compile.c 2021-06-14 19:49:58.575466644 +0200 *************** *** 177,183 **** 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 --- 177,182 ---- *************** *** 575,580 **** --- 574,595 ---- } /* + * 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. * When "tolerant" is TRUE convert most types to string, e.g. a List. *************** *** 2365,2370 **** --- 2380,2386 ---- type_T *type) { lvar_T *lvar; + dfunc_T *dfunc; if (arg_exists(name, len, NULL, NULL, NULL, cctx) == OK) { *************** *** 2381,2392 **** // 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++; lvar->lv_name = vim_strnsave(name, len == 0 ? STRLEN(name) : len); lvar->lv_const = isConst; lvar->lv_type = type; return lvar; } --- 2397,2416 ---- // 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. ! 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,7492 **** if (cctx->ctx_compile_type == CT_DEBUG) { // the previous block was skipped, may want to debug this line ! generate_instr(cctx, ISN_DEBUG); instr_count = instr->ga_len; } } --- 7510,7516 ---- if (cctx->ctx_compile_type == CT_DEBUG) { // the previous block was skipped, may want to debug this line ! generate_instr_debug(cctx); instr_count = instr->ga_len; } } *************** *** 8239,8245 **** } #endif if (cctx->ctx_compile_type == CT_DEBUG) ! generate_instr(cctx, ISN_DEBUG); } p = skipwhite(arg); --- 8263,8269 ---- } #endif if (cctx->ctx_compile_type == CT_DEBUG) ! generate_instr_debug(cctx); } p = skipwhite(arg); *************** *** 8976,8981 **** --- 9000,9006 ---- 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,9032 **** { dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data) + ufunc->uf_dfunc_idx; ! delete_def_function_contents(dfunc, FALSE); } else { --- 9051,9071 ---- { dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data) + ufunc->uf_dfunc_idx; ! 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,9208 **** && cctx.ctx_skip != SKIP_YES) { debug_lnum = cctx.ctx_lnum; ! generate_instr(&cctx, ISN_DEBUG); } // Some things can be recognized by the first character. --- 9241,9247 ---- && cctx.ctx_skip != SKIP_YES) { debug_lnum = cctx.ctx_lnum; ! generate_instr_debug(&cctx); } // Some things can be recognized by the first character. *************** *** 9670,9676 **** dfunc->df_instr = instr->ga_data; dfunc->df_instr_count = instr->ga_len; } ! dfunc->df_varcount = cctx.ctx_locals_count; dfunc->df_has_closure = cctx.ctx_has_closure; if (cctx.ctx_outer_used) ufunc->uf_flags |= FC_CLOSURE; --- 9709,9715 ---- dfunc->df_instr = instr->ga_data; dfunc->df_instr_count = instr->ga_len; } ! 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,10042 **** --- 10076,10082 ---- int idx; ga_clear(&dfunc->df_def_args_isn); + ga_clear_strings(&dfunc->df_var_names); if (dfunc->df_instr != NULL) { *** ../vim-8.2.2995/src/vim9.h 2021-06-13 15:15:58.412822225 +0200 --- src/vim9.h 2021-06-14 19:11:44.587949612 +0200 *************** *** 168,174 **** ISN_PROF_START, // start a line for profiling ISN_PROF_END, // end a line for profiling ! ISN_DEBUG, // check for debug breakpoint ISN_UNPACK, // unpack list into items, uses isn_arg.unpack ISN_SHUFFLE, // move item on stack up or down --- 168,175 ---- ISN_PROF_START, // start a line for profiling ISN_PROF_END, // end a line for profiling ! 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,452 **** --- 448,454 ---- // 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 *** ../vim-8.2.2995/src/debugger.c 2021-06-07 22:04:48.406620074 +0200 --- src/debugger.c 2021-06-12 16:50:32.238322962 +0200 *************** *** 218,224 **** if (last_cmd != 0) { ! // Execute debug command: decided where to break next and // return. switch (last_cmd) { --- 218,224 ---- if (last_cmd != 0) { ! // Execute debug command: decide where to break next and // return. switch (last_cmd) { *** ../vim-8.2.2995/src/testdir/test_debugger.vim 2021-06-13 14:01:22.760396977 +0200 --- src/testdir/test_debugger.vim 2021-06-14 20:31:47.040141296 +0200 *************** *** 853,858 **** --- 853,859 ---- enddef def g:GlobalFunction() + var some = "some var" CallAFunction() enddef *************** *** 884,902 **** \ ':debug call GlobalFunction()', \ ['cmd: call GlobalFunction()']) ! call RunDbgCmd(buf, 'step', ['line 1: CallAFunction()']) - " FIXME: not quite right call RunDbgCmd(buf, 'backtrace', [ \ '\V>backtrace', \ '\V->0 function GlobalFunction', ! \ '\Vline 1: CallAFunction()', \ ], \ #{match: 'pattern'}) call RunDbgCmd(buf, 'step', ['line 1: SourceAnotherFile()']) call RunDbgCmd(buf, 'step', ['line 1: source Xtest2.vim']) ! " FIXME: repeated line 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']) --- 885,905 ---- \ ':debug call GlobalFunction()', \ ['cmd: call GlobalFunction()']) ! call RunDbgCmd(buf, 'step', ['line 1: var some = "some var"']) ! call RunDbgCmd(buf, 'step', ['line 2: CallAFunction()']) ! call RunDbgCmd(buf, 'echo some', ['some var']) call RunDbgCmd(buf, 'backtrace', [ \ '\V>backtrace', \ '\V->0 function GlobalFunction', ! \ '\Vline 2: CallAFunction()', \ ], \ #{match: 'pattern'}) call RunDbgCmd(buf, 'step', ['line 1: SourceAnotherFile()']) call RunDbgCmd(buf, 'step', ['line 1: source Xtest2.vim']) ! " 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,912 **** call RunDbgCmd(buf, 'step', ['line 14: File2Function()']) call RunDbgCmd(buf, 'backtrace', [ \ '\V>backtrace', ! \ '\V 3 function GlobalFunction[1]', \ '\V 2 \.\*_CallAFunction[1]', \ '\V 1 \.\*_SourceAnotherFile[1]', \ '\V->0 script ' .. getcwd() .. '/Xtest2.vim', --- 909,915 ---- call RunDbgCmd(buf, 'step', ['line 14: File2Function()']) call RunDbgCmd(buf, 'backtrace', [ \ '\V>backtrace', ! \ '\V 3 function GlobalFunction[2]', \ '\V 2 \.\*_CallAFunction[1]', \ '\V 1 \.\*_SourceAnotherFile[1]', \ '\V->0 script ' .. getcwd() .. '/Xtest2.vim', *************** *** 917,923 **** call RunDbgCmd(buf, 'next', ['line 15: End of sourced file']) call RunDbgCmd(buf, 'backtrace', [ \ '\V>backtrace', ! \ '\V 3 function GlobalFunction[1]', \ '\V 2 \.\*_CallAFunction[1]', \ '\V 1 \.\*_SourceAnotherFile[1]', \ '\V->0 script ' .. getcwd() .. '/Xtest2.vim', --- 920,926 ---- call RunDbgCmd(buf, 'next', ['line 15: End of sourced file']) call RunDbgCmd(buf, 'backtrace', [ \ '\V>backtrace', ! \ '\V 3 function GlobalFunction[2]', \ '\V 2 \.\*_CallAFunction[1]', \ '\V 1 \.\*_SourceAnotherFile[1]', \ '\V->0 script ' .. getcwd() .. '/Xtest2.vim', *** ../vim-8.2.2995/src/version.c 2021-06-14 15:08:26.331250889 +0200 --- src/version.c 2021-06-14 16:12:49.891434923 +0200 *************** *** 752,753 **** --- 752,755 ---- { /* Add new patch number below this line */ + /**/ + 2996, /**/ -- I once paid $12 to peer at the box that held King Tutankhamen's little bandage-covered midget corpse at the De Young Museum in San Francisco. I remember thinking how pleased he'd be about the way things turned out in his afterlife. (Scott Adams - The Dilbert principle) /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// \\\ \\\ sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ /// \\\ help me help AIDS victims -- http://ICCF-Holland.org ///