Problem: Vim9: when debugging cannot inspect local variables.
Solution: Make local variables available when debugging.
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)
{
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);
enddef
def g:GlobalFunction()
+ var some = "some var"
CallAFunction()
enddef
\ ':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'])
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',
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',
static int included_patches[] =
{ /* Add new patch number below this line */
+/**/
+ 2996,
/**/
2995,
/**/
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
// 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
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
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.
type_T *type)
{
lvar_T *lvar;
+ dfunc_T *dfunc;
if (arg_exists(name, len, NULL, NULL, NULL, cctx) == OK)
{
// 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;
}
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;
}
}
}
#endif
if (cctx->ctx_compile_type == CT_DEBUG)
- generate_instr(cctx, ISN_DEBUG);
+ generate_instr_debug(cctx);
}
p = skipwhite(arg);
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;
{
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
{
&& 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.
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;
int idx;
ga_clear(&dfunc->df_def_args_isn);
+ ga_clear_strings(&dfunc->df_var_names);
if (dfunc->df_instr != NULL)
{
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;
}
#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.
// 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;
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)
+ 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;
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,