mirror of
https://github.com/vim/vim.git
synced 2025-09-24 03:44:06 -04:00
patch 9.1.0673: Vim9: too recursive func calls when calling super-class method
Problem: Vim9: too recursive func calls when calling super-class method with non-overriden super-call methods. (Aliaksei Budavei) Solution: use interface method, when super is to be used (Ernie Rael) When compiling "super.Func()" force class context to class that defines function that is doing "super.Func()". ISN_METHODCALL arg "cmf_is_super" for specific ufunc. fixes: #15448 fixes: #15463 (2) super.method may not execute in context of defining class closes: #15477 Signed-off-by: Ernie Rael <errael@raelity.com> Signed-off-by: Christian Brabandt <cb@256bit.org>
This commit is contained in:
committed by
Christian Brabandt
parent
d33afe12c6
commit
58c9579430
@@ -58,7 +58,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, class_T *cl, int mi, int pushed_argcount);
|
int generate_CALL(cctx_T *cctx, ufunc_T *ufunc, class_T *cl, int mi, int pushed_argcount, int is_super);
|
||||||
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);
|
||||||
|
@@ -3252,6 +3252,141 @@ def Test_using_base_class()
|
|||||||
v9.CheckSourceSuccess(lines)
|
v9.CheckSourceSuccess(lines)
|
||||||
enddef
|
enddef
|
||||||
|
|
||||||
|
def Test_super_dispatch()
|
||||||
|
# See #15448 and #15463
|
||||||
|
var lines =<< trim END
|
||||||
|
vim9script
|
||||||
|
|
||||||
|
class A
|
||||||
|
def String(): string
|
||||||
|
return 'A'
|
||||||
|
enddef
|
||||||
|
endclass
|
||||||
|
|
||||||
|
class B extends A
|
||||||
|
def String(): string
|
||||||
|
return super.String()
|
||||||
|
enddef
|
||||||
|
endclass
|
||||||
|
|
||||||
|
class C extends B
|
||||||
|
endclass
|
||||||
|
|
||||||
|
assert_equal('A', C.new().String())
|
||||||
|
END
|
||||||
|
v9.CheckSourceSuccess(lines)
|
||||||
|
|
||||||
|
lines =<< trim END
|
||||||
|
vim9script
|
||||||
|
|
||||||
|
class A
|
||||||
|
def F(): string
|
||||||
|
return 'AA'
|
||||||
|
enddef
|
||||||
|
endclass
|
||||||
|
|
||||||
|
class B extends A
|
||||||
|
def F(): string
|
||||||
|
return 'BB'
|
||||||
|
enddef
|
||||||
|
def S(): string
|
||||||
|
return super.F()
|
||||||
|
enddef
|
||||||
|
def S0(): string
|
||||||
|
return this.S()
|
||||||
|
enddef
|
||||||
|
endclass
|
||||||
|
|
||||||
|
class C extends B
|
||||||
|
def F(): string
|
||||||
|
return 'CC'
|
||||||
|
enddef
|
||||||
|
def ToB(): string
|
||||||
|
return super.F()
|
||||||
|
enddef
|
||||||
|
endclass
|
||||||
|
|
||||||
|
assert_equal('AA', B.new().S())
|
||||||
|
assert_equal('AA', C.new().S())
|
||||||
|
assert_equal('AA', B.new().S0())
|
||||||
|
assert_equal('AA', C.new().S0())
|
||||||
|
|
||||||
|
assert_equal('BB', C.new().ToB())
|
||||||
|
|
||||||
|
assert_equal('CC', C.new().F())
|
||||||
|
assert_equal('BB', B.new().F())
|
||||||
|
assert_equal('AA', A.new().F())
|
||||||
|
END
|
||||||
|
v9.CheckSourceSuccess(lines)
|
||||||
|
|
||||||
|
lines =<< trim END
|
||||||
|
vim9script
|
||||||
|
|
||||||
|
var call_chain: list<string>
|
||||||
|
|
||||||
|
abstract class A
|
||||||
|
abstract def _G(): string
|
||||||
|
|
||||||
|
def F(): string
|
||||||
|
call_chain->add('A.F()')
|
||||||
|
return this._G()
|
||||||
|
enddef
|
||||||
|
def _H(): string
|
||||||
|
call_chain->add('A._H()')
|
||||||
|
return this.F()
|
||||||
|
enddef
|
||||||
|
endclass
|
||||||
|
|
||||||
|
class B extends A
|
||||||
|
def _G(): string
|
||||||
|
call_chain->add('B.G()')
|
||||||
|
return 'BBB'
|
||||||
|
enddef
|
||||||
|
def SF(): string
|
||||||
|
call_chain->add('B.SF()')
|
||||||
|
return super._H()
|
||||||
|
enddef
|
||||||
|
endclass
|
||||||
|
|
||||||
|
class C extends B
|
||||||
|
endclass
|
||||||
|
|
||||||
|
class D extends C
|
||||||
|
def SF(): string
|
||||||
|
call_chain->add('D.SF()')
|
||||||
|
return super.SF()
|
||||||
|
enddef
|
||||||
|
endclass
|
||||||
|
|
||||||
|
class E extends D
|
||||||
|
def SF(): string
|
||||||
|
call_chain->add('E.SF()')
|
||||||
|
return super.SF()
|
||||||
|
enddef
|
||||||
|
endclass
|
||||||
|
|
||||||
|
class F extends E
|
||||||
|
def _G(): string
|
||||||
|
call_chain->add('F._G()')
|
||||||
|
return 'FFF'
|
||||||
|
enddef
|
||||||
|
endclass
|
||||||
|
|
||||||
|
# E.new() -> A.F() -> B._G()
|
||||||
|
call_chain = []
|
||||||
|
var o1 = E.new()
|
||||||
|
assert_equal('BBB', o1.F())
|
||||||
|
assert_equal(['A.F()', 'B.G()'], call_chain)
|
||||||
|
|
||||||
|
# F.new() -> E.SF() -> D.SF() -> B.SF() -> A._H() -> A.F() -> F._G()
|
||||||
|
call_chain = []
|
||||||
|
var o2 = F.new()
|
||||||
|
assert_equal('FFF', o2.SF())
|
||||||
|
assert_equal(['E.SF()', 'D.SF()', 'B.SF()', 'A._H()', 'A.F()', 'F._G()'], call_chain)
|
||||||
|
END
|
||||||
|
v9.CheckSourceSuccess(lines)
|
||||||
|
enddef
|
||||||
|
|
||||||
def Test_class_import()
|
def Test_class_import()
|
||||||
var lines =<< trim END
|
var lines =<< trim END
|
||||||
vim9script
|
vim9script
|
||||||
|
@@ -704,6 +704,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 */
|
||||||
|
/**/
|
||||||
|
673,
|
||||||
/**/
|
/**/
|
||||||
672,
|
672,
|
||||||
/**/
|
/**/
|
||||||
|
@@ -238,8 +238,9 @@ typedef struct {
|
|||||||
// arguments to ISN_METHODCALL
|
// arguments to ISN_METHODCALL
|
||||||
typedef struct {
|
typedef struct {
|
||||||
class_T *cmf_itf; // interface used
|
class_T *cmf_itf; // interface used
|
||||||
int cmf_idx; // index in "def_functions" for ISN_DCALL
|
int cmf_idx; // index in "def_functions" for ISN_METHODCALL
|
||||||
int cmf_argcount; // number of arguments on top of stack
|
int cmf_argcount; // number of arguments on top of stack
|
||||||
|
int cmf_is_super; // doing "super.Func", use cmf_itf, not cmf_idx
|
||||||
} cmfunc_T;
|
} cmfunc_T;
|
||||||
|
|
||||||
// arguments to ISN_PCALL
|
// arguments to ISN_PCALL
|
||||||
|
@@ -4400,14 +4400,23 @@ exec_instructions(ectx_T *ectx)
|
|||||||
goto on_error;
|
goto on_error;
|
||||||
}
|
}
|
||||||
|
|
||||||
class_T *cl = obj->obj_class;
|
ufunc_T *ufunc;
|
||||||
|
if (mfunc->cmf_is_super)
|
||||||
|
// Doing "super.Func", use the specific ufunc.
|
||||||
|
ufunc = mfunc->cmf_itf->class_obj_methods[
|
||||||
|
mfunc->cmf_idx];
|
||||||
|
else
|
||||||
|
{
|
||||||
|
class_T *cl = obj->obj_class;
|
||||||
|
|
||||||
// convert the interface index to the object index
|
// convert the interface index to the object index
|
||||||
int idx = object_index_from_itf_index(mfunc->cmf_itf,
|
int idx = object_index_from_itf_index(mfunc->cmf_itf,
|
||||||
TRUE, mfunc->cmf_idx, cl);
|
TRUE, mfunc->cmf_idx, cl);
|
||||||
|
ufunc = cl->class_obj_methods[idx];
|
||||||
|
}
|
||||||
|
|
||||||
if (call_ufunc(cl->class_obj_methods[idx], NULL,
|
if (call_ufunc(ufunc, NULL, mfunc->cmf_argcount, ectx,
|
||||||
mfunc->cmf_argcount, ectx, NULL, NULL) == FAIL)
|
NULL, NULL) == FAIL)
|
||||||
goto on_error;
|
goto on_error;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@@ -349,6 +349,11 @@ compile_class_object_index(cctx_T *cctx, char_u **arg, type_T *type)
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
// type->tt_type == VAR_OBJECT: method call
|
// type->tt_type == VAR_OBJECT: method call
|
||||||
|
// When compiling Func and doing "super.SomeFunc()", must be in the
|
||||||
|
// class context that defines Func.
|
||||||
|
if (is_super)
|
||||||
|
cl = cctx->ctx_ufunc->uf_defclass;
|
||||||
|
|
||||||
function_count = cl->class_obj_method_count;
|
function_count = cl->class_obj_method_count;
|
||||||
child_count = cl->class_obj_method_count_child;
|
child_count = cl->class_obj_method_count_child;
|
||||||
functions = cl->class_obj_methods;
|
functions = cl->class_obj_methods;
|
||||||
@@ -419,8 +424,8 @@ compile_class_object_index(cctx_T *cctx, char_u **arg, type_T *type)
|
|||||||
return generate_PCALL(cctx, argcount, name, ocm->ocm_type, TRUE);
|
return generate_PCALL(cctx, argcount, name, ocm->ocm_type, TRUE);
|
||||||
if (type->tt_type == VAR_OBJECT
|
if (type->tt_type == VAR_OBJECT
|
||||||
&& (cl->class_flags & (CLASS_INTERFACE | CLASS_EXTENDED)))
|
&& (cl->class_flags & (CLASS_INTERFACE | CLASS_EXTENDED)))
|
||||||
return generate_CALL(cctx, ufunc, cl, fi, argcount);
|
return generate_CALL(cctx, ufunc, cl, fi, argcount, is_super);
|
||||||
return generate_CALL(cctx, ufunc, NULL, 0, argcount);
|
return generate_CALL(cctx, ufunc, NULL, 0, argcount, FALSE);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type->tt_type == VAR_OBJECT)
|
if (type->tt_type == VAR_OBJECT)
|
||||||
@@ -1035,7 +1040,7 @@ compile_builtin_method_call(cctx_T *cctx, class_builtin_T builtin_method)
|
|||||||
ufunc_T *uf = class_get_builtin_method(type->tt_class, builtin_method,
|
ufunc_T *uf = class_get_builtin_method(type->tt_class, builtin_method,
|
||||||
&method_idx);
|
&method_idx);
|
||||||
if (uf != NULL)
|
if (uf != NULL)
|
||||||
res = generate_CALL(cctx, uf, type->tt_class, method_idx, 0);
|
res = generate_CALL(cctx, uf, type->tt_class, method_idx, 0, FALSE);
|
||||||
}
|
}
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
@@ -1238,7 +1243,7 @@ compile_call(
|
|||||||
{
|
{
|
||||||
if (!func_is_global(ufunc))
|
if (!func_is_global(ufunc))
|
||||||
{
|
{
|
||||||
res = generate_CALL(cctx, ufunc, NULL, 0, argcount);
|
res = generate_CALL(cctx, ufunc, NULL, 0, argcount, FALSE);
|
||||||
goto theend;
|
goto theend;
|
||||||
}
|
}
|
||||||
if (!has_g_namespace
|
if (!has_g_namespace
|
||||||
@@ -1257,7 +1262,7 @@ compile_call(
|
|||||||
if (cctx->ctx_ufunc->uf_defclass == cl)
|
if (cctx->ctx_ufunc->uf_defclass == cl)
|
||||||
{
|
{
|
||||||
res = generate_CALL(cctx, cl->class_class_functions[mi], NULL,
|
res = generate_CALL(cctx, cl->class_class_functions[mi], NULL,
|
||||||
0, argcount);
|
0, argcount, FALSE);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -1285,7 +1290,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, NULL, 0, argcount);
|
res = generate_CALL(cctx, ufunc, NULL, 0, argcount, FALSE);
|
||||||
goto theend;
|
goto theend;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -1805,6 +1805,8 @@ generate_BLOBAPPEND(cctx_T *cctx)
|
|||||||
* Generate an ISN_DCALL, ISN_UCALL or ISN_METHODCALL 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,
|
* 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.
|
* then "cl" is the interface and "mi" the method index on the interface.
|
||||||
|
* save is_super in the "isn->isn_arg"; it flags execution to use mfunc
|
||||||
|
* directly to determine ufunc.
|
||||||
* Return FAIL if the number of arguments is wrong.
|
* Return FAIL if the number of arguments is wrong.
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
@@ -1813,7 +1815,8 @@ generate_CALL(
|
|||||||
ufunc_T *ufunc,
|
ufunc_T *ufunc,
|
||||||
class_T *cl,
|
class_T *cl,
|
||||||
int mi,
|
int mi,
|
||||||
int pushed_argcount)
|
int pushed_argcount,
|
||||||
|
int is_super)
|
||||||
{
|
{
|
||||||
isn_T *isn;
|
isn_T *isn;
|
||||||
int regular_args = ufunc->uf_args.ga_len;
|
int regular_args = ufunc->uf_args.ga_len;
|
||||||
@@ -1898,6 +1901,7 @@ generate_CALL(
|
|||||||
++cl->class_refcount;
|
++cl->class_refcount;
|
||||||
isn->isn_arg.mfunc->cmf_idx = mi;
|
isn->isn_arg.mfunc->cmf_idx = mi;
|
||||||
isn->isn_arg.mfunc->cmf_argcount = argcount;
|
isn->isn_arg.mfunc->cmf_argcount = argcount;
|
||||||
|
isn->isn_arg.mfunc->cmf_is_super = is_super;
|
||||||
}
|
}
|
||||||
else if (isn->isn_type == ISN_DCALL)
|
else if (isn->isn_type == ISN_DCALL)
|
||||||
{
|
{
|
||||||
|
Reference in New Issue
Block a user