Patch 8.2.2996

8 views
Skip to first unread message

Bram Moolenaar

unread,
Jun 14, 2021, 2:41:10 PM6/14/21
to vim...@googlegroups.com

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 <SNR>\.\*_CallAFunction[1]',
\ '\V 1 <SNR>\.\*_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 <SNR>\.\*_CallAFunction[1]',
\ '\V 1 <SNR>\.\*_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 <SNR>\.\*_CallAFunction[1]',
\ '\V 1 <SNR>\.\*_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 <SNR>\.\*_CallAFunction[1]',
\ '\V 1 <SNR>\.\*_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 -- Br...@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 ///

John Marriott

unread,
Jun 14, 2021, 4:01:48 PM6/14/21
to vim...@googlegroups.com

On 15-Jun-2021 04:41, Bram Moolenaar wrote:
> 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
>
>
After this patch, mingw64 (gcc 11.1.0) spits out this warning:
<snip>
gcc -c -I. -Iproto -DWIN32 -DWINVER=0x0603 -D_WIN32_WINNT=0x0603
-DHAVE_PATHDEF -DFEAT_NORMAL -DHAVE_STDINT_H -D__USE_MINGW_ANSI_STDIO
-pipe -march=native -Wall -O3 -fomit-frame-pointer -freg-struct-return
-fpie -fPIE -DFEAT_GUI_MSWIN -DFEAT_CLIPBOARD vim9compile.c -o
gobjnative/vim9compile.o
vim9compile.c: In function 'compile_def_function':
vim9compile.c:9065:12: warning: 'instr_dest' may be used uninitialized
in this function [-Wmaybe-uninitialized]
 9065 |         if (instr_dest != NULL)
      |            ^
</snip>

The attached patch tries to fix it.
Cheers
John
vim9compile.c.8.2.2996

Bram Moolenaar

unread,
Jun 15, 2021, 4:23:14 AM6/15/21
to vim...@googlegroups.com, John Marriott
Thanks. The compiler isn't clever enough to see that all enum values
are handled. Oh well.

--
"You're fired." (1980)
"You're laid off." (1985)
"You're downsized." (1990)
"You're rightsized." (1992)
Reply all
Reply to author
Forward
0 new messages