diff options
author | Ernie Rael <errael@raelity.com> | 2024-08-13 23:27:22 +0200 |
---|---|---|
committer | Christian Brabandt <cb@256bit.org> | 2024-08-13 23:27:22 +0200 |
commit | 58c957943030b4dc551bf210b76cb2882182260c (patch) | |
tree | 32714fc474d0af2ad85f15885ab1cb114b65d89d | |
parent | d33afe12c6639d70fca82230df6b9fdee7365423 (diff) |
patch 9.1.0673: Vim9: too recursive func calls when calling super-class methodv9.1.0673
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>
-rw-r--r-- | src/proto/vim9instr.pro | 2 | ||||
-rw-r--r-- | src/testdir/test_vim9_class.vim | 135 | ||||
-rw-r--r-- | src/version.c | 2 | ||||
-rw-r--r-- | src/vim9.h | 3 | ||||
-rw-r--r-- | src/vim9execute.c | 21 | ||||
-rw-r--r-- | src/vim9expr.c | 17 | ||||
-rw-r--r-- | src/vim9instr.c | 6 |
7 files changed, 171 insertions, 15 deletions
diff --git a/src/proto/vim9instr.pro b/src/proto/vim9instr.pro index 1b2f79c959..8ee33b8cc1 100644 --- a/src/proto/vim9instr.pro +++ b/src/proto/vim9instr.pro @@ -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_LISTAPPEND(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 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); diff --git a/src/testdir/test_vim9_class.vim b/src/testdir/test_vim9_class.vim index 56c7b99d94..8791a5218d 100644 --- a/src/testdir/test_vim9_class.vim +++ b/src/testdir/test_vim9_class.vim @@ -3252,6 +3252,141 @@ def Test_using_base_class() v9.CheckSourceSuccess(lines) 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() var lines =<< trim END vim9script diff --git a/src/version.c b/src/version.c index 159357e21b..edde65f729 100644 --- a/src/version.c +++ b/src/version.c @@ -705,6 +705,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 673, +/**/ 672, /**/ 671, diff --git a/src/vim9.h b/src/vim9.h index 54938fe20c..c502ac462f 100644 --- a/src/vim9.h +++ b/src/vim9.h @@ -238,8 +238,9 @@ typedef struct { // arguments to ISN_METHODCALL typedef struct { 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_is_super; // doing "super.Func", use cmf_itf, not cmf_idx } cmfunc_T; // arguments to ISN_PCALL diff --git a/src/vim9execute.c b/src/vim9execute.c index 40b549934b..f523e27b89 100644 --- a/src/vim9execute.c +++ b/src/vim9execute.c @@ -4400,14 +4400,23 @@ exec_instructions(ectx_T *ectx) 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 - int idx = object_index_from_itf_index(mfunc->cmf_itf, - TRUE, mfunc->cmf_idx, cl); + // convert the interface index to the object index + int idx = object_index_from_itf_index(mfunc->cmf_itf, + TRUE, mfunc->cmf_idx, cl); + ufunc = cl->class_obj_methods[idx]; + } - if (call_ufunc(cl->class_obj_methods[idx], NULL, - mfunc->cmf_argcount, ectx, NULL, NULL) == FAIL) + if (call_ufunc(ufunc, NULL, mfunc->cmf_argcount, ectx, + NULL, NULL) == FAIL) goto on_error; } break; diff --git a/src/vim9expr.c b/src/vim9expr.c index c2428f4bbe..cfdea7bc41 100644 --- a/src/vim9expr.c +++ b/src/vim9expr.c @@ -349,6 +349,11 @@ compile_class_object_index(cctx_T *cctx, char_u **arg, type_T *type) else { // 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; child_count = cl->class_obj_method_count_child; 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); 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); + return generate_CALL(cctx, ufunc, cl, fi, argcount, is_super); + return generate_CALL(cctx, ufunc, NULL, 0, argcount, FALSE); } 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, &method_idx); 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; @@ -1238,7 +1243,7 @@ compile_call( { if (!func_is_global(ufunc)) { - res = generate_CALL(cctx, ufunc, NULL, 0, argcount); + res = generate_CALL(cctx, ufunc, NULL, 0, argcount, FALSE); goto theend; } if (!has_g_namespace @@ -1257,7 +1262,7 @@ compile_call( if (cctx->ctx_ufunc->uf_defclass == cl) { res = generate_CALL(cctx, cl->class_class_functions[mi], NULL, - 0, argcount); + 0, argcount, FALSE); } else { @@ -1285,7 +1290,7 @@ compile_call( // If we can find a global function by name generate the right call. if (ufunc != NULL) { - res = generate_CALL(cctx, ufunc, NULL, 0, argcount); + res = generate_CALL(cctx, ufunc, NULL, 0, argcount, FALSE); goto theend; } diff --git a/src/vim9instr.c b/src/vim9instr.c index ad8beb1a30..4368e3c2d8 100644 --- a/src/vim9instr.c +++ b/src/vim9instr.c @@ -1805,6 +1805,8 @@ generate_BLOBAPPEND(cctx_T *cctx) * 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. + * 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. */ int @@ -1813,7 +1815,8 @@ generate_CALL( ufunc_T *ufunc, class_T *cl, int mi, - int pushed_argcount) + int pushed_argcount, + int is_super) { isn_T *isn; int regular_args = ufunc->uf_args.ga_len; @@ -1898,6 +1901,7 @@ generate_CALL( ++cl->class_refcount; isn->isn_arg.mfunc->cmf_idx = mi; isn->isn_arg.mfunc->cmf_argcount = argcount; + isn->isn_arg.mfunc->cmf_is_super = is_super; } else if (isn->isn_type == ISN_DCALL) { |