# HG changeset patch # User Bram Moolenaar # Date 1590439503 -7200 # Node ID 9f921ba86d05609d0cd45d2304c86dd2ef414eaf # Parent cceaa5ec43aaea8ca377b44e71fb21b35bad2000 patch 8.2.0823: Vim9: script reload test is disabled Commit: https://github.com/vim/vim/commit/25e0f5863e9010a75a1ff0d04e8f886403968755 Author: Bram Moolenaar Date: Mon May 25 22:36:50 2020 +0200 patch 8.2.0823: Vim9: script reload test is disabled Problem: Vim9: script reload test is disabled. Solution: Compile a function in the context of the script where it was defined. Set execution stack for compiled function. Add a test that an error is reported for the right file/function. diff --git a/src/ex_docmd.c b/src/ex_docmd.c --- a/src/ex_docmd.c +++ b/src/ex_docmd.c @@ -634,8 +634,8 @@ do_cmdline( int *dbg_tick = NULL; // ptr to dbg_tick field in cookie struct dbg_stuff debug_saved; // saved things for debug mode int initial_trylevel; - struct msglist **saved_msg_list = NULL; - struct msglist *private_msg_list; + msglist_T **saved_msg_list = NULL; + msglist_T *private_msg_list; // "fgetline" and "cookie" passed to do_one_cmd() char_u *(*cmd_getline)(int, void *, int, int); @@ -1238,7 +1238,7 @@ do_cmdline( if (did_throw) { void *p = NULL; - struct msglist *messages = NULL, *next; + msglist_T *messages = NULL, *next; /* * If the uncaught exception is a user exception, report it as an diff --git a/src/ex_eval.c b/src/ex_eval.c --- a/src/ex_eval.c +++ b/src/ex_eval.c @@ -146,8 +146,8 @@ cause_errthrow( int severe, int *ignore) { - struct msglist *elem; - struct msglist **plist; + msglist_T *elem; + msglist_T **plist; /* * Do nothing when displaying the interrupt message or reporting an @@ -251,7 +251,7 @@ cause_errthrow( while (*plist != NULL) plist = &(*plist)->next; - elem = ALLOC_ONE(struct msglist); + elem = ALLOC_CLEAR_ONE(msglist_T); if (elem == NULL) { suppress_errthrow = TRUE; @@ -287,6 +287,11 @@ cause_errthrow( else (*msg_list)->throw_msg = tmsg; } + + // Get the source name and lnum now, it may change before + // reaching do_errthrow(). + elem->sfile = estack_sfile(); + elem->slnum = SOURCING_LNUM; } } } @@ -298,15 +303,16 @@ cause_errthrow( * Free a "msg_list" and the messages it contains. */ static void -free_msglist(struct msglist *l) +free_msglist(msglist_T *l) { - struct msglist *messages, *next; + msglist_T *messages, *next; messages = l; while (messages != NULL) { next = messages->next; vim_free(messages->msg); + vim_free(messages->sfile); vim_free(messages); messages = next; } @@ -428,7 +434,7 @@ get_exception_string( if (type == ET_ERROR) { *should_free = TRUE; - mesg = ((struct msglist *)value)->throw_msg; + mesg = ((msglist_T *)value)->throw_msg; if (cmdname != NULL && *cmdname != NUL) { cmdlen = (int)STRLEN(cmdname); @@ -526,23 +532,34 @@ throw_exception(void *value, except_type if (type == ET_ERROR) // Store the original message and prefix the exception value with // "Vim:" or, if a command name is given, "Vim(cmdname):". - excp->messages = (struct msglist *)value; + excp->messages = (msglist_T *)value; excp->value = get_exception_string(value, type, cmdname, &should_free); if (excp->value == NULL && should_free) goto nomem; excp->type = type; - excp->throw_name = estack_sfile(); - if (excp->throw_name == NULL) - excp->throw_name = vim_strsave((char_u *)""); - if (excp->throw_name == NULL) + if (type == ET_ERROR && ((msglist_T *)value)->sfile != NULL) + { + msglist_T *entry = (msglist_T *)value; + + excp->throw_name = entry->sfile; + entry->sfile = NULL; + excp->throw_lnum = entry->slnum; + } + else { - if (should_free) - vim_free(excp->value); - goto nomem; + excp->throw_name = estack_sfile(); + if (excp->throw_name == NULL) + excp->throw_name = vim_strsave((char_u *)""); + if (excp->throw_name == NULL) + { + if (should_free) + vim_free(excp->value); + goto nomem; + } + excp->throw_lnum = SOURCING_LNUM; } - excp->throw_lnum = SOURCING_LNUM; if (p_verbose >= 13 || debug_break_level > 0) { diff --git a/src/globals.h b/src/globals.h --- a/src/globals.h +++ b/src/globals.h @@ -344,7 +344,7 @@ EXTERN int force_abort INIT(= FALSE); * field of a later list element, when the "emsg_severe" flag was set when the * emsg() call was made. */ -EXTERN struct msglist **msg_list INIT(= NULL); +EXTERN msglist_T **msg_list INIT(= NULL); /* * suppress_errthrow: When TRUE, don't convert an error to an exception. Used diff --git a/src/proto/scriptfile.pro b/src/proto/scriptfile.pro --- a/src/proto/scriptfile.pro +++ b/src/proto/scriptfile.pro @@ -1,7 +1,8 @@ /* scriptfile.c */ void estack_init(void); estack_T *estack_push(etype_T type, char_u *name, long lnum); -void estack_push_ufunc(etype_T type, ufunc_T *ufunc, long lnum); +void estack_push_ufunc(ufunc_T *ufunc, long lnum); +int estack_top_is_ufunc(ufunc_T *ufunc, long lnum); void estack_pop(void); char_u *estack_sfile(void); void ex_runtime(exarg_T *eap); diff --git a/src/scriptfile.c b/src/scriptfile.c --- a/src/scriptfile.c +++ b/src/scriptfile.c @@ -69,14 +69,31 @@ estack_push(etype_T type, char_u *name, * Add a user function to the execution stack. */ void -estack_push_ufunc(etype_T type, ufunc_T *ufunc, long lnum) +estack_push_ufunc(ufunc_T *ufunc, long lnum) { - estack_T *entry = estack_push(type, + estack_T *entry = estack_push(ETYPE_UFUNC, ufunc->uf_name_exp != NULL ? ufunc->uf_name_exp : ufunc->uf_name, lnum); if (entry != NULL) entry->es_info.ufunc = ufunc; } + +/* + * Return TRUE if "ufunc" with "lnum" is already at the top of the exe stack. + */ + int +estack_top_is_ufunc(ufunc_T *ufunc, long lnum) +{ + estack_T *entry; + + if (exestack.ga_len == 0) + return FALSE; + entry = ((estack_T *)exestack.ga_data) + exestack.ga_len - 1; + return entry->es_type == ETYPE_UFUNC + && STRCMP( entry->es_name, ufunc->uf_name_exp != NULL + ? ufunc->uf_name_exp : ufunc->uf_name) == 0 + && entry->es_lnum == lnum; +} #endif /* diff --git a/src/structs.h b/src/structs.h --- a/src/structs.h +++ b/src/structs.h @@ -927,13 +927,16 @@ typedef struct { * A list of error messages that can be converted to an exception. "throw_msg" * is only set in the first element of the list. Usually, it points to the * original message stored in that element, but sometimes it points to a later - * message in the list. See cause_errthrow() below. + * message in the list. See cause_errthrow(). */ +typedef struct msglist msglist_T; struct msglist { - char *msg; // original message - char *throw_msg; // msg to throw: usually original one - struct msglist *next; // next of several messages in a row + char *msg; // original message, allocated + char *throw_msg; // msg to throw: usually original one + char_u *sfile; // value from estack_sfile(), allocated + long slnum; // line number for "sfile" + msglist_T *next; // next of several messages in a row }; /* @@ -1516,6 +1519,7 @@ struct blobvar_S #if defined(FEAT_EVAL) || defined(PROTO) typedef struct funccall_S funccall_T; +// values used for "uf_dfunc_idx" # define UF_NOT_COMPILED -2 # define UF_TO_BE_COMPILED -1 diff --git a/src/testdir/test_vim9_script.vim b/src/testdir/test_vim9_script.vim --- a/src/testdir/test_vim9_script.vim +++ b/src/testdir/test_vim9_script.vim @@ -745,9 +745,6 @@ def Test_vim9script_fails() enddef def Test_vim9script_reload_import() - " TODO: make it work to compile when not in the script context anymore - return - let lines =<< trim END vim9script const var = '' @@ -797,9 +794,6 @@ def Test_vim9script_reload_import() enddef def Test_vim9script_reload_delfunc() - " TODO: make it work to compile when not in the script context anymore - return - let first_lines =<< trim END vim9script def FuncYes(): string @@ -920,6 +914,37 @@ def Test_import_rtp() delete('import', 'rf') enddef +def Test_import_compile_error() + let export_lines = [ + 'vim9script', + 'export def ExpFunc(): string', + ' return notDefined', + 'enddef', + ] + writefile(export_lines, 'Xexported.vim') + + let import_lines = [ + 'vim9script', + 'import ExpFunc from "./Xexported.vim"', + 'def ImpFunc()', + ' echo ExpFunc()', + 'enddef', + 'defcompile', + ] + writefile(import_lines, 'Ximport.vim') + + try + source Ximport.vim + catch /E1001/ + " Error should be fore the Xexported.vim file. + assert_match('E1001: variable not found: notDefined', v:exception) + assert_match('function \d\+_ImpFunc\[1\]..\d\+_ExpFunc, line 1', v:throwpoint) + endtry + + delete('Xexported.vim') + delete('Ximport.vim') +enddef + def Test_fixed_size_list() " will be allocated as one piece of memory, check that changes work let l = [1, 2, 3, 4] diff --git a/src/userfunc.c b/src/userfunc.c --- a/src/userfunc.c +++ b/src/userfunc.c @@ -1114,11 +1114,11 @@ call_user_func( if (fp->uf_dfunc_idx != UF_NOT_COMPILED) { - estack_push_ufunc(ETYPE_UFUNC, fp, 1); + estack_push_ufunc(fp, 1); save_current_sctx = current_sctx; current_sctx = fp->uf_script_ctx; - // Execute the compiled function. + // Execute the function, possibly compiling it first. call_def_function(fp, argcount, argvars, funcexe->partial, rettv); --depth; current_funccal = fc->caller; @@ -1288,7 +1288,7 @@ call_user_func( ++sandbox; } - estack_push_ufunc(ETYPE_UFUNC, fp, 1); + estack_push_ufunc(fp, 1); ESTACK_CHECK_SETUP if (p_verbose >= 12) { diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -747,6 +747,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 823, +/**/ 822, /**/ 821, diff --git a/src/vim9compile.c b/src/vim9compile.c --- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -2323,8 +2323,7 @@ next_line_from_context(cctx_T *cctx) } line = ((char_u **)cctx->ctx_ufunc->uf_lines.ga_data)[cctx->ctx_lnum]; cctx->ctx_line_start = line; - SOURCING_LNUM = cctx->ctx_ufunc->uf_script_ctx.sc_lnum - + cctx->ctx_lnum + 1; + SOURCING_LNUM = cctx->ctx_lnum + 1; } while (line == NULL || *skipwhite(line) == NUL); return line; } @@ -6349,14 +6348,15 @@ compile_def_function(ufunc_T *ufunc, int int called_emsg_before = called_emsg; int ret = FAIL; sctx_T save_current_sctx = current_sctx; + int do_estack_push; int emsg_before = called_emsg; + // When using a function that was compiled before: Free old instructions. + // Otherwise add a new entry in "def_functions". if (ufunc->uf_dfunc_idx >= 0) { - // Redefining a function that was compiled before. dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data) + ufunc->uf_dfunc_idx; - // Free old instructions. delete_def_function_contents(dfunc); } else if (add_def_function(ufunc) == FAIL) @@ -6373,9 +6373,17 @@ compile_def_function(ufunc_T *ufunc, int ga_init2(&cctx.ctx_instr, sizeof(isn_T), 50); instr = &cctx.ctx_instr; - // Most modern script version. + // Set the context to the function, it may be compiled when called from + // another script. Set the script version to the most modern one. + // The line number will be set in next_line_from_context(). + current_sctx = ufunc->uf_script_ctx; current_sctx.sc_version = SCRIPT_VERSION_VIM9; + // Make sure error messages are OK. + do_estack_push = !estack_top_is_ufunc(ufunc, 1); + if (do_estack_push) + estack_push_ufunc(ufunc, 1); + if (ufunc->uf_def_args.ga_len > 0) { int count = ufunc->uf_def_args.ga_len; @@ -6795,6 +6803,9 @@ erret: } current_sctx = save_current_sctx; + if (do_estack_push) + estack_pop(); + free_imported(&cctx); free_locals(&cctx); ga_clear(&cctx.ctx_type_stack); diff --git a/src/vim9execute.c b/src/vim9execute.c --- a/src/vim9execute.c +++ b/src/vim9execute.c @@ -230,7 +230,7 @@ call_dfunc(int cdf_idx, int argcount_arg // Set execution state to the start of the called function. ectx->ec_dfunc_idx = cdf_idx; ectx->ec_instr = dfunc->df_instr; - estack_push_ufunc(ETYPE_UFUNC, dfunc->df_ufunc, 1); + estack_push_ufunc(dfunc->df_ufunc, 1); // Decide where to start execution, handles optional arguments. init_instr_idx(ufunc, argcount, ectx); @@ -656,6 +656,7 @@ call_def_function( int defcount = ufunc->uf_args.ga_len - argc; int save_sc_version = current_sctx.sc_version; int breakcheck_count = 0; + int called_emsg_before = called_emsg; // Get pointer to item in the stack. #define STACK_TV(idx) (((typval_T *)ectx.ec_stack.ga_data) + idx) @@ -673,7 +674,13 @@ call_def_function( if (ufunc->uf_dfunc_idx == UF_NOT_COMPILED || (ufunc->uf_dfunc_idx == UF_TO_BE_COMPILED && compile_def_function(ufunc, FALSE, NULL) == FAIL)) + { + if (called_emsg == called_emsg_before) + semsg(_("E1091: Function is not compiled: %s"), + ufunc->uf_name_exp == NULL + ? ufunc->uf_name : ufunc->uf_name_exp); return FAIL; + } { // Check the function was really compiled.