patch 9.1.0673: Vim9: too recursive func calls when calling super-class method

Commit: 
https://github.com/vim/vim/commit/58c957943030b4dc551bf210b76cb2882182260c
Author: Ernie Rael <err...@raelity.com>
Date:   Tue Aug 13 23:27:22 2024 +0200

    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 <err...@raelity.com>
    Signed-off-by: Christian Brabandt <c...@256bit.org>

diff --git a/src/proto/vim9instr.pro b/src/proto/vim9instr.pro
index 1b2f79c95..8ee33b8cc 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 56c7b99d9..8791a5218 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 159357e21..edde65f72 100644
--- a/src/version.c
+++ b/src/version.c
@@ -704,6 +704,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    673,
 /**/
     672,
 /**/
diff --git a/src/vim9.h b/src/vim9.h
index 54938fe20..c502ac462 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 40b549934..f523e27b8 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 c2428f4bb..cfdea7bc4 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 ad8beb1a3..4368e3c2d 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)
     {

-- 
-- 
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

--- 
You received this message because you are subscribed to the Google Groups 
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to vim_dev+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/vim_dev/E1sdzKF-00HCQc-Bm%40256bit.org.

Raspunde prin e-mail lui