forked from aniani/vim
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>
This commit is contained in:
parent
b34a688ba0
commit
778ada48fb
@ -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[]
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -704,6 +704,8 @@ static char *(features[]) =
|
||||
|
||||
static int included_patches[] =
|
||||
{ /* Add new patch number below this line */
|
||||
/**/
|
||||
1119,
|
||||
/**/
|
||||
1118,
|
||||
/**/
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user