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 <yegappan@yahoo.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
diff --git a/src/errors.h b/src/errors.h
index 34a0d58..a116505 100644
--- a/src/errors.h
+++ b/src/errors.h
@@ -3552,7 +3552,8 @@
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 0f692ed..1e63f22 100644
--- a/src/testdir/test_vim9_class.vim
+++ b/src/testdir/test_vim9_class.vim
@@ -544,7 +544,7 @@
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 c92aed0..8d81697 100644
--- a/src/testdir/test_vim9_import.vim
+++ b/src/testdir/test_vim9_import.vim
@@ -3494,4 +3494,75 @@
&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 29fa078..7ba180e 100644
--- a/src/version.c
+++ b/src/version.c
@@ -705,6 +705,8 @@
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
+ 1119,
+/**/
1118,
/**/
1117,
diff --git a/src/vim9class.c b/src/vim9class.c
index b5e3a13..f041342 100644
--- a/src/vim9class.c
+++ b/src/vim9class.c
@@ -2053,8 +2053,7 @@
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 @@
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 c7f0e67..65f6536 100644
--- a/src/vim9execute.c
+++ b/src/vim9execute.c
@@ -3162,34 +3162,53 @@
}
/*
- * 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 @@
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 @@
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