From 778ada48fb66918088ead75b5f59d301e4da541f Mon Sep 17 00:00:00 2001 From: Yegappan Lakshmanan Date: Mon, 17 Feb 2025 20:21:23 +0100 Subject: [PATCH] patch 9.1.1119: Vim9: Not able to use an autoloaded class from another autoloaded script Problem: Vim9: Not able to use an autoloaded class from another autoloaded script (Elliot) Solution: make it work (Yegappan Lakshmanan) fixes: #15031 closes: #16652 Signed-off-by: Yegappan Lakshmanan Signed-off-by: Christian Brabandt --- src/errors.h | 3 +- src/testdir/test_vim9_class.vim | 2 +- src/testdir/test_vim9_import.vim | 71 ++++++++++++++++++++++++++++ src/version.c | 2 + src/vim9class.c | 5 +- src/vim9execute.c | 81 +++++++++++++++++++++----------- 6 files changed, 131 insertions(+), 33 deletions(-) diff --git a/src/errors.h b/src/errors.h index 34a0d5832f..a1165056ba 100644 --- a/src/errors.h +++ b/src/errors.h @@ -3552,7 +3552,8 @@ EXTERN char e_type_can_only_be_defined_in_vim9_script[] INIT(= N_("E1393: Type can only be defined in Vim9 script")); EXTERN char e_type_name_must_start_with_uppercase_letter_str[] INIT(= N_("E1394: Type name must start with an uppercase letter: %s")); -// E1395 unused +EXTERN char e_using_null_class[] + INIT(= N_("E1395: Using a null class")); EXTERN char e_typealias_already_exists_for_str[] INIT(= N_("E1396: Type alias \"%s\" already exists")); EXTERN char e_missing_typealias_name[] diff --git a/src/testdir/test_vim9_class.vim b/src/testdir/test_vim9_class.vim index 0f692ed9cf..1e63f22df9 100644 --- a/src/testdir/test_vim9_class.vim +++ b/src/testdir/test_vim9_class.vim @@ -544,7 +544,7 @@ def Test_using_null_class() var lines =<< trim END @_ = null_class.member END - v9.CheckDefExecAndScriptFailure(lines, ['E715: Dictionary required', 'E1363: Incomplete type']) + v9.CheckDefExecAndScriptFailure(lines, ['E1395: Using a null class', 'E1363: Incomplete type']) # Test for using a null class as a value lines =<< trim END diff --git a/src/testdir/test_vim9_import.vim b/src/testdir/test_vim9_import.vim index c92aed065b..8d81697b2c 100644 --- a/src/testdir/test_vim9_import.vim +++ b/src/testdir/test_vim9_import.vim @@ -3494,4 +3494,75 @@ def Test_vim9_import_and_class_extends_2() &rtp = save_rtp enddef +" Test for using an autoloaded class from another autoloaded script +def Test_class_from_auloaded_script() + mkdir('Xdir', 'R') + var save_rtp = &rtp + &rtp = getcwd() + exe 'set rtp^=' .. getcwd() .. '/Xdir' + + mkdir('Xdir/autoload/SomeClass/bar', 'p') + + var lines =<< trim END + vim9script + + export class Baz + static var v1: string = "v1" + var v2: string = "v2" + def GetName(): string + return "baz" + enddef + endclass + END + writefile(lines, 'Xdir/autoload/SomeClass/bar/baz.vim', 'D') + + lines =<< trim END + vim9script + + import autoload './bar/baz.vim' + + export def MyTestFoo(): string + assert_fails('var x = baz.Baz.NonExisting()', 'E1325: Method "NonExisting" not found in class "Baz"') + assert_fails('var x = baz.Baz.foobar', 'E1337: Class variable "foobar" not found in class "Baz"') + + const instance = baz.Baz.new() + return $'{instance.GetName()} {baz.Baz.v1} {instance.v2}' + enddef + END + writefile(lines, 'Xdir/autoload/SomeClass/foo.vim', 'D') + + lines =<< trim END + vim9script + + import autoload 'SomeClass/foo.vim' + import autoload 'SomeClass/bar/baz.vim' + + def NotInAutoload() + # Use non-existing class method and variable + assert_fails('var x = baz.Baz.NonExisting()', 'E1325: Method "NonExisting" not found in class "Baz"') + + var caught_exception = false + try + var x = baz.Baz.foobar + catch /E1337: Class variable "foobar" not found in class "Baz"/ + caught_exception = true + endtry + assert_true(caught_exception) + + const instance = baz.Baz.new() + assert_equal("baz v1 v2", $'{instance.GetName()} {baz.Baz.v1} {instance.v2}') + enddef + + def InAutoload() + assert_equal("baz v1 v2", foo.MyTestFoo()) + enddef + + NotInAutoload() + InAutoload() + END + v9.CheckScriptSuccess(lines) + + &rtp = save_rtp +enddef + " vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker diff --git a/src/version.c b/src/version.c index 29fa07854a..7ba180e30e 100644 --- a/src/version.c +++ b/src/version.c @@ -704,6 +704,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1119, /**/ 1118, /**/ diff --git a/src/vim9class.c b/src/vim9class.c index b5e3a13be7..f0413425f6 100644 --- a/src/vim9class.c +++ b/src/vim9class.c @@ -2053,8 +2053,7 @@ early_ret: tv.v_type = VAR_CLASS; tv.vval.v_class = cl; SOURCING_LNUM = start_lnum; - int rc = set_var_const(cl->class_name, current_sctx.sc_sid, - NULL, &tv, FALSE, 0, 0); + int rc = set_var_const(cl->class_name, 0, NULL, &tv, FALSE, 0, 0); if (rc == FAIL) goto cleanup; @@ -2874,7 +2873,7 @@ ex_type(exarg_T *eap) tv.vval.v_class = type->tt_class; ++tv.vval.v_class->class_refcount; } - set_var_const(name_start, current_sctx.sc_sid, NULL, &tv, FALSE, + set_var_const(name_start, 0, NULL, &tv, FALSE, ASSIGN_CONST | ASSIGN_FINAL, 0); done: diff --git a/src/vim9execute.c b/src/vim9execute.c index c7f0e673b2..65f6536fdb 100644 --- a/src/vim9execute.c +++ b/src/vim9execute.c @@ -3162,34 +3162,53 @@ object_required_error(typval_T *tv) } /* - * Accessing the member of an object stored in a variable of type "any". + * Accessing the variable or method of an object or a class stored in a + * variable of type "any". * Returns OK if the member variable is present. * Returns FAIL if the variable is not found. */ static int -any_var_get_obj_member(class_T *current_class, isn_T *iptr, typval_T *tv) +var_any_get_oc_member(class_T *current_class, isn_T *iptr, typval_T *tv) { - object_T *obj = tv->vval.v_object; + int is_object = tv->v_type == VAR_OBJECT; + class_T *tv_cl; + object_T *obj = NULL; typval_T mtv; - if (obj == NULL) + if (is_object) { - SOURCING_LNUM = iptr->isn_lnum; - emsg(_(e_using_null_object)); - return FAIL; + obj = tv->vval.v_object; + if (obj == NULL) + { + SOURCING_LNUM = iptr->isn_lnum; + emsg(_(e_using_null_object)); + return FAIL; + } + tv_cl = obj->obj_class; + } + else + { + tv_cl = tv->vval.v_class; + if (tv_cl == NULL) + { + SOURCING_LNUM = iptr->isn_lnum; + emsg(_(e_using_null_class)); + return FAIL; + } } - // get_member_tv() needs the object information in the typval argument. - // So set the object information. + // get_member_tv() needs the class/object information in the typval + // argument. So set the object information. copy_tv(tv, &mtv); - // 'name' can either be a object variable or a object method + // 'name' can either be an instance or class variable or method int namelen = (int)STRLEN(iptr->isn_arg.string); int save_did_emsg = did_emsg; - if (get_member_tv(obj->obj_class, TRUE, iptr->isn_arg.string, namelen, + if (get_member_tv(tv_cl, is_object, iptr->isn_arg.string, namelen, current_class, &mtv) == OK) { + // instance or class variable copy_tv(&mtv, tv); clear_tv(&mtv); return OK; @@ -3198,31 +3217,36 @@ any_var_get_obj_member(class_T *current_class, isn_T *iptr, typval_T *tv) if (did_emsg != save_did_emsg) return FAIL; - // could be a member function - ufunc_T *obj_method; - int obj_method_idx; + // could be a class or instance method + ufunc_T *oc_method; + int oc_method_idx; - obj_method = method_lookup(obj->obj_class, VAR_OBJECT, - iptr->isn_arg.string, namelen, - &obj_method_idx); - if (obj_method == NULL) + oc_method = method_lookup(tv_cl, tv->v_type, iptr->isn_arg.string, + namelen, &oc_method_idx); + if (oc_method == NULL) { + char *msg; + SOURCING_LNUM = iptr->isn_lnum; - semsg(_(e_variable_not_found_on_object_str_str), iptr->isn_arg.string, - obj->obj_class->class_name); + if (is_object) + msg = e_variable_not_found_on_object_str_str; + else + msg = e_class_variable_str_not_found_in_class_str; + semsg(_(msg), iptr->isn_arg.string, tv_cl->class_name); return FAIL; } // Protected methods are not accessible outside the class - if (*obj_method->uf_name == '_' - && !class_instance_of(current_class, obj->obj_class)) + if (*oc_method->uf_name == '_' + && !class_instance_of(current_class, tv_cl)) { - semsg(_(e_cannot_access_protected_method_str), obj_method->uf_name); + semsg(_(e_cannot_access_protected_method_str), oc_method->uf_name); return FAIL; } - // Create a partial for the member function - if (obj_method_to_partial_tv(obj, obj_method, tv) == FAIL) + // Create a partial for the instance or class method + if (obj_method_to_partial_tv(is_object ? obj : NULL, oc_method, tv) + == FAIL) return FAIL; return OK; @@ -5671,15 +5695,16 @@ exec_instructions(ectx_T *ectx) tv = STACK_TV_BOT(-1); - if (tv->v_type == VAR_OBJECT) + if (tv->v_type == VAR_OBJECT + || tv->v_type == VAR_CLASS) { if (dict_stack_save(tv) == FAIL) goto on_fatal_error; ufunc_T *ufunc = (((dfunc_T *)def_functions.ga_data) + ectx->ec_dfunc_idx)->df_ufunc; - // Class object (not a Dict) - if (any_var_get_obj_member(ufunc->uf_class, iptr, tv) == FAIL) + // Class or an object (not a Dict) + if (var_any_get_oc_member(ufunc->uf_class, iptr, tv) == FAIL) goto on_error; } else