mirror of
https://github.com/vim/vim.git
synced 2025-07-26 11:04:33 -04:00
patch 9.0.1254: calling a method on an interface does not work
Problem: Calling a method on an interface does not work. Solution: At runtime figure out what method to call. (closes #11901)
This commit is contained in:
parent
192e24d974
commit
d0200c8631
@ -1,5 +1,5 @@
|
|||||||
/* vim9class.c */
|
/* vim9class.c */
|
||||||
int object_index_from_itf_index(class_T *itf, int idx, class_T *cl);
|
int object_index_from_itf_index(class_T *itf, int is_method, int idx, class_T *cl);
|
||||||
void ex_class(exarg_T *eap);
|
void ex_class(exarg_T *eap);
|
||||||
type_T *class_member_type(class_T *cl, char_u *name, char_u *name_end, int *member_idx);
|
type_T *class_member_type(class_T *cl, char_u *name, char_u *name_end, int *member_idx);
|
||||||
void ex_enum(exarg_T *eap);
|
void ex_enum(exarg_T *eap);
|
||||||
|
@ -57,7 +57,7 @@ int check_internal_func_args(cctx_T *cctx, int func_idx, int argcount, int metho
|
|||||||
int generate_BCALL(cctx_T *cctx, int func_idx, int argcount, int method_call);
|
int generate_BCALL(cctx_T *cctx, int func_idx, int argcount, int method_call);
|
||||||
int generate_LISTAPPEND(cctx_T *cctx);
|
int generate_LISTAPPEND(cctx_T *cctx);
|
||||||
int generate_BLOBAPPEND(cctx_T *cctx);
|
int generate_BLOBAPPEND(cctx_T *cctx);
|
||||||
int generate_CALL(cctx_T *cctx, ufunc_T *ufunc, int pushed_argcount);
|
int generate_CALL(cctx_T *cctx, ufunc_T *ufunc, class_T *cl, int mi, int pushed_argcount);
|
||||||
int generate_UCALL(cctx_T *cctx, char_u *name, int argcount);
|
int generate_UCALL(cctx_T *cctx, char_u *name, int argcount);
|
||||||
int check_func_args_from_type(cctx_T *cctx, type_T *type, int argcount, int at_top, char_u *name);
|
int check_func_args_from_type(cctx_T *cctx, type_T *type, int argcount, int at_top, char_u *name);
|
||||||
int generate_PCALL(cctx_T *cctx, int argcount, char_u *name, type_T *type, int at_top);
|
int generate_PCALL(cctx_T *cctx, int argcount, char_u *name, type_T *type, int at_top);
|
||||||
|
@ -1484,15 +1484,17 @@ typedef struct {
|
|||||||
char_u *ocm_init; // allocated
|
char_u *ocm_init; // allocated
|
||||||
} ocmember_T;
|
} ocmember_T;
|
||||||
|
|
||||||
// used for the lookup table of a class member index
|
// used for the lookup table of a class member index and object method index
|
||||||
typedef struct itf2class_S itf2class_T;
|
typedef struct itf2class_S itf2class_T;
|
||||||
struct itf2class_S {
|
struct itf2class_S {
|
||||||
itf2class_T *i2c_next;
|
itf2class_T *i2c_next;
|
||||||
class_T *i2c_class;
|
class_T *i2c_class;
|
||||||
|
int i2c_is_method; // TRUE for method indexes
|
||||||
// array with ints follows
|
// array with ints follows
|
||||||
};
|
};
|
||||||
|
|
||||||
#define CLASS_INTERFACE 1
|
#define CLASS_INTERFACE 1
|
||||||
|
#define CLASS_EXTENDED 2 // another class extends this one
|
||||||
|
|
||||||
// "class_T": used for v_class of typval of VAR_CLASS
|
// "class_T": used for v_class of typval of VAR_CLASS
|
||||||
// Also used for an interface (class_flags has CLASS_INTERFACE).
|
// Also used for an interface (class_flags has CLASS_INTERFACE).
|
||||||
|
@ -1001,6 +1001,56 @@ def Test_class_implements_interface()
|
|||||||
v9.CheckScriptSuccess(lines)
|
v9.CheckScriptSuccess(lines)
|
||||||
enddef
|
enddef
|
||||||
|
|
||||||
|
def Test_call_interface_method()
|
||||||
|
var lines =<< trim END
|
||||||
|
vim9script
|
||||||
|
interface Base
|
||||||
|
def Enter(): void
|
||||||
|
endinterface
|
||||||
|
|
||||||
|
class Child implements Base
|
||||||
|
def Enter(): void
|
||||||
|
g:result ..= 'child'
|
||||||
|
enddef
|
||||||
|
endclass
|
||||||
|
|
||||||
|
def F(obj: Base)
|
||||||
|
obj.Enter()
|
||||||
|
enddef
|
||||||
|
|
||||||
|
g:result = ''
|
||||||
|
F(Child.new())
|
||||||
|
assert_equal('child', g:result)
|
||||||
|
unlet g:result
|
||||||
|
END
|
||||||
|
v9.CheckScriptSuccess(lines)
|
||||||
|
|
||||||
|
lines =<< trim END
|
||||||
|
vim9script
|
||||||
|
class Base
|
||||||
|
def Enter(): void
|
||||||
|
g:result ..= 'base'
|
||||||
|
enddef
|
||||||
|
endclass
|
||||||
|
|
||||||
|
class Child extends Base
|
||||||
|
def Enter(): void
|
||||||
|
g:result ..= 'child'
|
||||||
|
enddef
|
||||||
|
endclass
|
||||||
|
|
||||||
|
def F(obj: Base)
|
||||||
|
obj.Enter()
|
||||||
|
enddef
|
||||||
|
|
||||||
|
g:result = ''
|
||||||
|
F(Child.new())
|
||||||
|
assert_equal('child', g:result)
|
||||||
|
unlet g:result
|
||||||
|
END
|
||||||
|
v9.CheckScriptSuccess(lines)
|
||||||
|
enddef
|
||||||
|
|
||||||
def Test_class_used_as_type()
|
def Test_class_used_as_type()
|
||||||
var lines =<< trim END
|
var lines =<< trim END
|
||||||
vim9script
|
vim9script
|
||||||
|
@ -695,6 +695,8 @@ static char *(features[]) =
|
|||||||
|
|
||||||
static int included_patches[] =
|
static int included_patches[] =
|
||||||
{ /* Add new patch number below this line */
|
{ /* Add new patch number below this line */
|
||||||
|
/**/
|
||||||
|
1254,
|
||||||
/**/
|
/**/
|
||||||
1253,
|
1253,
|
||||||
/**/
|
/**/
|
||||||
|
@ -112,6 +112,7 @@ typedef enum {
|
|||||||
// function call
|
// function call
|
||||||
ISN_BCALL, // call builtin function isn_arg.bfunc
|
ISN_BCALL, // call builtin function isn_arg.bfunc
|
||||||
ISN_DCALL, // call def function isn_arg.dfunc
|
ISN_DCALL, // call def function isn_arg.dfunc
|
||||||
|
ISN_METHODCALL, // call method on interface, uses isn_arg.mfunc
|
||||||
ISN_UCALL, // call user function or funcref/partial isn_arg.ufunc
|
ISN_UCALL, // call user function or funcref/partial isn_arg.ufunc
|
||||||
ISN_PCALL, // call partial, use isn_arg.pfunc
|
ISN_PCALL, // call partial, use isn_arg.pfunc
|
||||||
ISN_PCALL_END, // cleanup after ISN_PCALL with cpf_top set
|
ISN_PCALL_END, // cleanup after ISN_PCALL with cpf_top set
|
||||||
@ -234,6 +235,13 @@ typedef struct {
|
|||||||
int cdf_argcount; // number of arguments on top of stack
|
int cdf_argcount; // number of arguments on top of stack
|
||||||
} cdfunc_T;
|
} cdfunc_T;
|
||||||
|
|
||||||
|
// arguments to ISN_METHODCALL
|
||||||
|
typedef struct {
|
||||||
|
class_T *cmf_itf; // interface used
|
||||||
|
int cmf_idx; // index in "def_functions" for ISN_DCALL
|
||||||
|
int cmf_argcount; // number of arguments on top of stack
|
||||||
|
} cmfunc_T;
|
||||||
|
|
||||||
// arguments to ISN_PCALL
|
// arguments to ISN_PCALL
|
||||||
typedef struct {
|
typedef struct {
|
||||||
int cpf_top; // when TRUE partial is above the arguments
|
int cpf_top; // when TRUE partial is above the arguments
|
||||||
@ -517,6 +525,7 @@ struct isn_S {
|
|||||||
trycont_T trycont;
|
trycont_T trycont;
|
||||||
cbfunc_T bfunc;
|
cbfunc_T bfunc;
|
||||||
cdfunc_T dfunc;
|
cdfunc_T dfunc;
|
||||||
|
cmfunc_T *mfunc;
|
||||||
cpfunc_T pfunc;
|
cpfunc_T pfunc;
|
||||||
cufunc_T ufunc;
|
cufunc_T ufunc;
|
||||||
echo_T echo;
|
echo_T echo;
|
||||||
|
@ -201,16 +201,17 @@ add_members_to_class(
|
|||||||
* "cl" implementing that interface.
|
* "cl" implementing that interface.
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
object_index_from_itf_index(class_T *itf, int idx, class_T *cl)
|
object_index_from_itf_index(class_T *itf, int is_method, int idx, class_T *cl)
|
||||||
{
|
{
|
||||||
if (idx > itf->class_obj_member_count)
|
if (idx > (is_method ? itf->class_obj_method_count
|
||||||
|
: itf->class_obj_member_count))
|
||||||
{
|
{
|
||||||
siemsg("index %d out of range for interface %s", idx, itf->class_name);
|
siemsg("index %d out of range for interface %s", idx, itf->class_name);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
itf2class_T *i2c;
|
itf2class_T *i2c;
|
||||||
for (i2c = itf->class_itf2class; i2c != NULL; i2c = i2c->i2c_next)
|
for (i2c = itf->class_itf2class; i2c != NULL; i2c = i2c->i2c_next)
|
||||||
if (i2c->i2c_class == cl)
|
if (i2c->i2c_class == cl && i2c->i2c_is_method == is_method)
|
||||||
break;
|
break;
|
||||||
if (i2c == NULL)
|
if (i2c == NULL)
|
||||||
{
|
{
|
||||||
@ -789,7 +790,11 @@ early_ret:
|
|||||||
if (cl->class_name == NULL)
|
if (cl->class_name == NULL)
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
|
|
||||||
cl->class_extends = extends_cl;
|
if (extends_cl != NULL)
|
||||||
|
{
|
||||||
|
cl->class_extends = extends_cl;
|
||||||
|
extends_cl->class_flags |= CLASS_EXTENDED;
|
||||||
|
}
|
||||||
|
|
||||||
// Add class and object members to "cl".
|
// Add class and object members to "cl".
|
||||||
if (add_members_to_class(&classmembers,
|
if (add_members_to_class(&classmembers,
|
||||||
@ -820,11 +825,26 @@ early_ret:
|
|||||||
VIM_CLEAR(ga_impl.ga_data);
|
VIM_CLEAR(ga_impl.ga_data);
|
||||||
ga_impl.ga_len = 0;
|
ga_impl.ga_len = 0;
|
||||||
|
|
||||||
|
cl->class_interfaces_cl = intf_classes;
|
||||||
|
intf_classes = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cl->class_interface_count > 0 || extends_cl != NULL)
|
||||||
|
{
|
||||||
// For each interface add a lookuptable for the member index on the
|
// For each interface add a lookuptable for the member index on the
|
||||||
// interface to the member index in this class.
|
// interface to the member index in this class.
|
||||||
for (int i = 0; i < cl->class_interface_count; ++i)
|
// And a lookuptable for the object method index on the interface
|
||||||
|
// to the object method index in this class.
|
||||||
|
// Also do this for the extended class, if any.
|
||||||
|
for (int i = 0; i <= cl->class_interface_count; ++i)
|
||||||
{
|
{
|
||||||
class_T *ifcl = intf_classes[i];
|
class_T *ifcl = i < cl->class_interface_count
|
||||||
|
? cl->class_interfaces_cl[i]
|
||||||
|
: extends_cl;
|
||||||
|
if (ifcl == NULL)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Table for members.
|
||||||
itf2class_T *if2cl = alloc_clear(sizeof(itf2class_T)
|
itf2class_T *if2cl = alloc_clear(sizeof(itf2class_T)
|
||||||
+ ifcl->class_obj_member_count * sizeof(int));
|
+ ifcl->class_obj_member_count * sizeof(int));
|
||||||
if (if2cl == NULL)
|
if (if2cl == NULL)
|
||||||
@ -832,22 +852,64 @@ early_ret:
|
|||||||
if2cl->i2c_next = ifcl->class_itf2class;
|
if2cl->i2c_next = ifcl->class_itf2class;
|
||||||
ifcl->class_itf2class = if2cl;
|
ifcl->class_itf2class = if2cl;
|
||||||
if2cl->i2c_class = cl;
|
if2cl->i2c_class = cl;
|
||||||
|
if2cl->i2c_is_method = FALSE;
|
||||||
|
|
||||||
for (int if_i = 0; if_i < ifcl->class_obj_member_count; ++if_i)
|
for (int if_i = 0; if_i < ifcl->class_obj_member_count; ++if_i)
|
||||||
for (int cl_i = 0; cl_i < cl->class_obj_member_count; ++cl_i)
|
for (int cl_i = 0; cl_i < cl->class_obj_member_count;
|
||||||
|
++cl_i)
|
||||||
{
|
{
|
||||||
if (STRCMP(ifcl->class_obj_members[if_i].ocm_name,
|
if (STRCMP(ifcl->class_obj_members[if_i].ocm_name,
|
||||||
cl->class_obj_members[cl_i].ocm_name) == 0)
|
cl->class_obj_members[cl_i].ocm_name) == 0)
|
||||||
{
|
{
|
||||||
int *table = (int *)(if2cl + 1);
|
int *table = (int *)(if2cl + 1);
|
||||||
table[if_i] = cl_i;
|
table[if_i] = cl_i;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
cl->class_interfaces_cl = intf_classes;
|
// Table for methods.
|
||||||
intf_classes = NULL;
|
if2cl = alloc_clear(sizeof(itf2class_T)
|
||||||
|
+ ifcl->class_obj_method_count * sizeof(int));
|
||||||
|
if (if2cl == NULL)
|
||||||
|
goto cleanup;
|
||||||
|
if2cl->i2c_next = ifcl->class_itf2class;
|
||||||
|
ifcl->class_itf2class = if2cl;
|
||||||
|
if2cl->i2c_class = cl;
|
||||||
|
if2cl->i2c_is_method = TRUE;
|
||||||
|
|
||||||
|
for (int if_i = 0; if_i < ifcl->class_obj_method_count; ++if_i)
|
||||||
|
{
|
||||||
|
int done = FALSE;
|
||||||
|
for (int cl_i = 0; cl_i < objmethods.ga_len; ++cl_i)
|
||||||
|
{
|
||||||
|
if (STRCMP(ifcl->class_obj_methods[if_i]->uf_name,
|
||||||
|
((ufunc_T **)objmethods.ga_data)[cl_i]->uf_name)
|
||||||
|
== 0)
|
||||||
|
{
|
||||||
|
int *table = (int *)(if2cl + 1);
|
||||||
|
table[if_i] = cl_i;
|
||||||
|
done = TRUE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!done && extends_cl != NULL)
|
||||||
|
{
|
||||||
|
for (int cl_i = 0;
|
||||||
|
cl_i < extends_cl->class_obj_member_count; ++cl_i)
|
||||||
|
{
|
||||||
|
if (STRCMP(ifcl->class_obj_methods[if_i]->uf_name,
|
||||||
|
extends_cl->class_obj_methods[cl_i]->uf_name)
|
||||||
|
== 0)
|
||||||
|
{
|
||||||
|
int *table = (int *)(if2cl + 1);
|
||||||
|
table[if_i] = cl_i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_class && cl->class_class_member_count > 0)
|
if (is_class && cl->class_class_member_count > 0)
|
||||||
|
@ -2262,7 +2262,8 @@ execute_storeindex(isn_T *iptr, ectx_T *ectx)
|
|||||||
class_T *itf = iptr->isn_arg.storeindex.si_class;
|
class_T *itf = iptr->isn_arg.storeindex.si_class;
|
||||||
if (itf != NULL)
|
if (itf != NULL)
|
||||||
// convert interface member index to class member index
|
// convert interface member index to class member index
|
||||||
idx = object_index_from_itf_index(itf, idx, obj->obj_class);
|
idx = object_index_from_itf_index(itf, FALSE,
|
||||||
|
idx, obj->obj_class);
|
||||||
|
|
||||||
clear_tv(&otv[idx]);
|
clear_tv(&otv[idx]);
|
||||||
otv[idx] = *tv;
|
otv[idx] = *tv;
|
||||||
@ -2950,6 +2951,20 @@ load_namespace_var(ectx_T *ectx, isntype_T isn_type, isn_T *iptr)
|
|||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
object_required_error(typval_T *tv)
|
||||||
|
{
|
||||||
|
garray_T type_list;
|
||||||
|
ga_init2(&type_list, sizeof(type_T *), 10);
|
||||||
|
type_T *type = typval2type(tv, get_copyID(), &type_list, TVTT_DO_MEMBER);
|
||||||
|
char *tofree = NULL;
|
||||||
|
char *typename = type_name(type, &tofree);
|
||||||
|
semsg(_(e_object_required_found_str), typename);
|
||||||
|
vim_free(tofree);
|
||||||
|
clear_type_list(&type_list);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Execute instructions in execution context "ectx".
|
* Execute instructions in execution context "ectx".
|
||||||
* Return OK or FAIL;
|
* Return OK or FAIL;
|
||||||
@ -4125,6 +4140,30 @@ exec_instructions(ectx_T *ectx)
|
|||||||
goto on_error;
|
goto on_error;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
// call a method on an interface
|
||||||
|
case ISN_METHODCALL:
|
||||||
|
{
|
||||||
|
SOURCING_LNUM = iptr->isn_lnum;
|
||||||
|
tv = STACK_TV_BOT(-1);
|
||||||
|
if (tv->v_type != VAR_OBJECT)
|
||||||
|
{
|
||||||
|
object_required_error(tv);
|
||||||
|
goto on_error;
|
||||||
|
}
|
||||||
|
object_T *obj = tv->vval.v_object;
|
||||||
|
class_T *cl = obj->obj_class;
|
||||||
|
|
||||||
|
// convert the interface index to the object index
|
||||||
|
cmfunc_T *mfunc = iptr->isn_arg.mfunc;
|
||||||
|
int idx = object_index_from_itf_index(mfunc->cmf_itf,
|
||||||
|
TRUE, mfunc->cmf_idx, cl);
|
||||||
|
|
||||||
|
if (call_ufunc(cl->class_obj_methods[idx], NULL,
|
||||||
|
mfunc->cmf_argcount, ectx, NULL, NULL) == FAIL)
|
||||||
|
goto on_error;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
// call a builtin function
|
// call a builtin function
|
||||||
case ISN_BCALL:
|
case ISN_BCALL:
|
||||||
SOURCING_LNUM = iptr->isn_lnum;
|
SOURCING_LNUM = iptr->isn_lnum;
|
||||||
@ -5213,15 +5252,7 @@ exec_instructions(ectx_T *ectx)
|
|||||||
if (tv->v_type != VAR_OBJECT)
|
if (tv->v_type != VAR_OBJECT)
|
||||||
{
|
{
|
||||||
SOURCING_LNUM = iptr->isn_lnum;
|
SOURCING_LNUM = iptr->isn_lnum;
|
||||||
garray_T type_list;
|
object_required_error(tv);
|
||||||
ga_init2(&type_list, sizeof(type_T *), 10);
|
|
||||||
type_T *type = typval2type(tv, get_copyID(),
|
|
||||||
&type_list, TVTT_DO_MEMBER);
|
|
||||||
char *tofree = NULL;
|
|
||||||
char *typename = type_name(type, &tofree);
|
|
||||||
semsg(_(e_object_required_found_str), typename);
|
|
||||||
vim_free(tofree);
|
|
||||||
clear_type_list(&type_list);
|
|
||||||
goto on_error;
|
goto on_error;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -5234,8 +5265,8 @@ exec_instructions(ectx_T *ectx)
|
|||||||
idx = iptr->isn_arg.classmember.cm_idx;
|
idx = iptr->isn_arg.classmember.cm_idx;
|
||||||
// convert the interface index to the object index
|
// convert the interface index to the object index
|
||||||
idx = object_index_from_itf_index(
|
idx = object_index_from_itf_index(
|
||||||
iptr->isn_arg.classmember.cm_class,
|
iptr->isn_arg.classmember.cm_class,
|
||||||
idx, obj->obj_class);
|
FALSE, idx, obj->obj_class);
|
||||||
}
|
}
|
||||||
|
|
||||||
// the members are located right after the object struct
|
// the members are located right after the object struct
|
||||||
@ -6637,6 +6668,17 @@ list_instructions(char *pfx, isn_T *instr, int instr_count, ufunc_T *ufunc)
|
|||||||
cdfunc->cdf_argcount);
|
cdfunc->cdf_argcount);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case ISN_METHODCALL:
|
||||||
|
{
|
||||||
|
cmfunc_T *mfunc = iptr->isn_arg.mfunc;
|
||||||
|
|
||||||
|
smsg("%s%4d METHODCALL %s.%s(argc %d)", pfx, current,
|
||||||
|
mfunc->cmf_itf->class_name,
|
||||||
|
mfunc->cmf_itf->class_obj_methods[
|
||||||
|
mfunc->cmf_idx]->uf_name,
|
||||||
|
mfunc->cmf_argcount);
|
||||||
|
}
|
||||||
|
break;
|
||||||
case ISN_UCALL:
|
case ISN_UCALL:
|
||||||
{
|
{
|
||||||
cufunc_T *cufunc = &iptr->isn_arg.ufunc;
|
cufunc_T *cufunc = &iptr->isn_arg.ufunc;
|
||||||
|
@ -321,9 +321,10 @@ compile_class_object_index(cctx_T *cctx, char_u **arg, type_T *type)
|
|||||||
}
|
}
|
||||||
|
|
||||||
ufunc_T *ufunc = NULL;
|
ufunc_T *ufunc = NULL;
|
||||||
for (int i = is_super ? child_count : 0; i < function_count; ++i)
|
int fi;
|
||||||
|
for (fi = is_super ? child_count : 0; fi < function_count; ++fi)
|
||||||
{
|
{
|
||||||
ufunc_T *fp = functions[i];
|
ufunc_T *fp = functions[fi];
|
||||||
// Use a separate pointer to avoid that ASAN complains about
|
// Use a separate pointer to avoid that ASAN complains about
|
||||||
// uf_name[] only being 4 characters.
|
// uf_name[] only being 4 characters.
|
||||||
char_u *ufname = (char_u *)fp->uf_name;
|
char_u *ufname = (char_u *)fp->uf_name;
|
||||||
@ -347,7 +348,11 @@ compile_class_object_index(cctx_T *cctx, char_u **arg, type_T *type)
|
|||||||
int argcount = 0;
|
int argcount = 0;
|
||||||
if (compile_arguments(arg, cctx, &argcount, CA_NOT_SPECIAL) == FAIL)
|
if (compile_arguments(arg, cctx, &argcount, CA_NOT_SPECIAL) == FAIL)
|
||||||
return FAIL;
|
return FAIL;
|
||||||
return generate_CALL(cctx, ufunc, argcount);
|
|
||||||
|
if (type->tt_type == VAR_OBJECT
|
||||||
|
&& (cl->class_flags & (CLASS_INTERFACE | CLASS_EXTENDED)))
|
||||||
|
return generate_CALL(cctx, ufunc, cl, fi, argcount);
|
||||||
|
return generate_CALL(cctx, ufunc, NULL, 0, argcount);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type->tt_type == VAR_OBJECT)
|
if (type->tt_type == VAR_OBJECT)
|
||||||
@ -364,7 +369,7 @@ compile_class_object_index(cctx_T *cctx, char_u **arg, type_T *type)
|
|||||||
}
|
}
|
||||||
|
|
||||||
*arg = name_end;
|
*arg = name_end;
|
||||||
if (cl->class_flags & CLASS_INTERFACE)
|
if (cl->class_flags & (CLASS_INTERFACE | CLASS_EXTENDED))
|
||||||
return generate_GET_ITF_MEMBER(cctx, cl, i, m->ocm_type);
|
return generate_GET_ITF_MEMBER(cctx, cl, i, m->ocm_type);
|
||||||
return generate_GET_OBJ_MEMBER(cctx, i, m->ocm_type);
|
return generate_GET_OBJ_MEMBER(cctx, i, m->ocm_type);
|
||||||
}
|
}
|
||||||
@ -1063,7 +1068,7 @@ compile_call(
|
|||||||
{
|
{
|
||||||
if (!func_is_global(ufunc))
|
if (!func_is_global(ufunc))
|
||||||
{
|
{
|
||||||
res = generate_CALL(cctx, ufunc, argcount);
|
res = generate_CALL(cctx, ufunc, NULL, 0, argcount);
|
||||||
goto theend;
|
goto theend;
|
||||||
}
|
}
|
||||||
if (!has_g_namespace
|
if (!has_g_namespace
|
||||||
@ -1092,7 +1097,7 @@ compile_call(
|
|||||||
// If we can find a global function by name generate the right call.
|
// If we can find a global function by name generate the right call.
|
||||||
if (ufunc != NULL)
|
if (ufunc != NULL)
|
||||||
{
|
{
|
||||||
res = generate_CALL(cctx, ufunc, argcount);
|
res = generate_CALL(cctx, ufunc, NULL, 0, argcount);
|
||||||
goto theend;
|
goto theend;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1709,11 +1709,18 @@ generate_BLOBAPPEND(cctx_T *cctx)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Generate an ISN_DCALL or ISN_UCALL instruction.
|
* Generate an ISN_DCALL, ISN_UCALL or ISN_METHODCALL instruction.
|
||||||
|
* When calling a method on an object, of which we know the interface only,
|
||||||
|
* then "cl" is the interface and "mi" the method index on the interface.
|
||||||
* Return FAIL if the number of arguments is wrong.
|
* Return FAIL if the number of arguments is wrong.
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
generate_CALL(cctx_T *cctx, ufunc_T *ufunc, int pushed_argcount)
|
generate_CALL(
|
||||||
|
cctx_T *cctx,
|
||||||
|
ufunc_T *ufunc,
|
||||||
|
class_T *cl,
|
||||||
|
int mi,
|
||||||
|
int pushed_argcount)
|
||||||
{
|
{
|
||||||
isn_T *isn;
|
isn_T *isn;
|
||||||
int regular_args = ufunc->uf_args.ga_len;
|
int regular_args = ufunc->uf_args.ga_len;
|
||||||
@ -1783,11 +1790,21 @@ generate_CALL(cctx_T *cctx, ufunc_T *ufunc, int pushed_argcount)
|
|||||||
return FAIL;
|
return FAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((isn = generate_instr(cctx,
|
if ((isn = generate_instr(cctx, cl != NULL ? ISN_METHODCALL
|
||||||
ufunc->uf_def_status != UF_NOT_COMPILED ? ISN_DCALL
|
: ufunc->uf_def_status != UF_NOT_COMPILED
|
||||||
: ISN_UCALL)) == NULL)
|
? ISN_DCALL : ISN_UCALL)) == NULL)
|
||||||
return FAIL;
|
return FAIL;
|
||||||
if (isn->isn_type == ISN_DCALL)
|
if (isn->isn_type == ISN_METHODCALL)
|
||||||
|
{
|
||||||
|
isn->isn_arg.mfunc = ALLOC_ONE(cmfunc_T);
|
||||||
|
if (isn->isn_arg.mfunc == NULL)
|
||||||
|
return FAIL;
|
||||||
|
isn->isn_arg.mfunc->cmf_itf = cl;
|
||||||
|
++cl->class_refcount;
|
||||||
|
isn->isn_arg.mfunc->cmf_idx = mi;
|
||||||
|
isn->isn_arg.mfunc->cmf_argcount = argcount;
|
||||||
|
}
|
||||||
|
else if (isn->isn_type == ISN_DCALL)
|
||||||
{
|
{
|
||||||
isn->isn_arg.dfunc.cdf_idx = ufunc->uf_dfunc_idx;
|
isn->isn_arg.dfunc.cdf_idx = ufunc->uf_dfunc_idx;
|
||||||
isn->isn_arg.dfunc.cdf_argcount = argcount;
|
isn->isn_arg.dfunc.cdf_argcount = argcount;
|
||||||
@ -2483,6 +2500,14 @@ delete_instr(isn_T *isn)
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case ISN_METHODCALL:
|
||||||
|
{
|
||||||
|
cmfunc_T *mfunc = isn->isn_arg.mfunc;
|
||||||
|
class_unref(mfunc->cmf_itf);
|
||||||
|
vim_free(mfunc);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case ISN_NEWFUNC:
|
case ISN_NEWFUNC:
|
||||||
{
|
{
|
||||||
newfuncarg_T *arg = isn->isn_arg.newfunc.nf_arg;
|
newfuncarg_T *arg = isn->isn_arg.newfunc.nf_arg;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user