patch 9.0.1867: Vim9: access to interface statics possible

Commit: 
https://github.com/vim/vim/commit/18143d3111b2122c7a94ca51085a60b3073cb139
Author: Ernie Rael <[email protected]>
Date:   Mon Sep 4 22:30:41 2023 +0200

    patch 9.0.1867: Vim9: access to interface statics possible
    
    Problem:  Vim9: access to interface statics possible
    Solution: Prevent direct access to interface statics
    
    closes: #13007
    
    Signed-off-by: Christian Brabandt <[email protected]>
    Co-authored-by: Ernie Rael <[email protected]>

diff --git a/src/errors.h b/src/errors.h
index c0e98df78..e719ffde1 100644
--- a/src/errors.h
+++ b/src/errors.h
@@ -3513,5 +3513,6 @@ EXTERN char 
e_method_str_type_mismatch_expected_str_but_got_str[]
        INIT(= N_("E1407: Member \"%s\": type mismatch, expected %s but got 
%s"));
 EXTERN char e_aptypes_is_null_str_nr[]
        INIT(= "E1408: Internal error: ap_types or ap_types[idx] is NULL: %s: 
%d");
-
+EXTERN char e_interface_static_direct_access_str[]
+       INIT(= N_("E1409: Cannot directly access interface \"%s\" static member 
\"%s\""));
 // E1371 - E1399 unused
diff --git a/src/eval.c b/src/eval.c
index 7cfe68cc6..4e1dbdeea 100644
--- a/src/eval.c
+++ b/src/eval.c
@@ -1180,6 +1180,14 @@ get_lval(
            return NULL;
        lp->ll_tv = &v->di_tv;
     }
+    if (vim9script && writing && lp->ll_tv->v_type == VAR_CLASS
+           && (lp->ll_tv->vval.v_class->class_flags & CLASS_INTERFACE) != 0)
+    {
+       if (!quiet)
+           semsg(_(e_interface_static_direct_access_str),
+                           lp->ll_tv->vval.v_class->class_name, lp->ll_name);
+       return NULL;
+    }
 
     if (vim9script && (flags & GLV_NO_DECL) == 0)
     {
diff --git a/src/proto/vim9class.pro b/src/proto/vim9class.pro
index e685c0369..0f8aa7f1f 100644
--- a/src/proto/vim9class.pro
+++ b/src/proto/vim9class.pro
@@ -1,5 +1,5 @@
 /* vim9class.c */
-int object_index_from_itf_index(class_T *itf, int is_method, int idx, class_T 
*cl);
+int object_index_from_itf_index(class_T *itf, int is_method, int idx, class_T 
*cl, int is_static);
 void ex_class(exarg_T *eap);
 type_T *class_member_type(class_T *cl, int is_object, char_u *name, char_u 
*name_end, int *member_idx, ocmember_T **m);
 void ex_enum(exarg_T *eap);
diff --git a/src/proto/vim9instr.pro b/src/proto/vim9instr.pro
index 1ba5639a5..3f287d064 100644
--- a/src/proto/vim9instr.pro
+++ b/src/proto/vim9instr.pro
@@ -4,8 +4,8 @@ isn_T *generate_instr_drop(cctx_T *cctx, isntype_T isn_type, 
int drop);
 isn_T *generate_instr_type(cctx_T *cctx, isntype_T isn_type, type_T *type);
 isn_T *generate_instr_debug(cctx_T *cctx);
 int generate_CONSTRUCT(cctx_T *cctx, class_T *cl);
-int generate_GET_OBJ_MEMBER(cctx_T *cctx, int idx, type_T *type);
-int generate_GET_ITF_MEMBER(cctx_T *cctx, class_T *itf, int idx, type_T *type);
+int generate_GET_OBJ_MEMBER(cctx_T *cctx, int idx, type_T *type, int 
is_static);
+int generate_GET_ITF_MEMBER(cctx_T *cctx, class_T *itf, int idx, type_T *type, 
int is_static);
 int generate_STORE_THIS(cctx_T *cctx, int idx);
 int may_generate_2STRING(int offset, int tolerant, cctx_T *cctx);
 int generate_add_instr(cctx_T *cctx, vartype_T vartype, type_T *type1, type_T 
*type2, exprtype_T expr_type);
diff --git a/src/testdir/test_vim9_class.vim b/src/testdir/test_vim9_class.vim
index 8ae136f28..a293f247d 100644
--- a/src/testdir/test_vim9_class.vim
+++ b/src/testdir/test_vim9_class.vim
@@ -509,6 +509,36 @@ def Test_assignment_with_operator()
       assert_equal(23, f.x)
   END
   v9.CheckScriptSuccess(lines)
+
+  # do the same thing, but through an interface
+  lines =<< trim END
+      vim9script
+
+      interface I
+        public this.x: number
+      endinterface
+
+      class Foo implements I
+        public this.x: number
+
+        def Add(n: number)
+          var i: I = this
+          i.x += n
+        enddef
+      endclass
+
+      var f =  Foo.new(3)
+      f.Add(17)
+      assert_equal(20, f.x)
+
+      def AddToFoo(i: I)
+        i.x += 3
+      enddef
+
+      AddToFoo(f)
+      assert_equal(23, f.x)
+  END
+  v9.CheckScriptSuccess(lines)
 enddef
 
 def Test_list_of_objects()
@@ -3762,6 +3792,142 @@ def Test_dup_member_variable()
   v9.CheckScriptFailure(lines, 'E1369: Duplicate member: val')
 enddef
 
+def Test_interface_static_member_access()
+  # In a class cannot read from interface static
+  var lines =<< trim END
+    vim9script
+    interface I
+        public static num: number
+    endinterface
+    class C implements I
+        public static num = 3
+        def F()
+            var x = I.num
+        enddef
+    endclass
+    C.new().F()
+  END
+  v9.CheckScriptFailure(lines, 'E1409: Cannot directly access interface "I" 
static member "num"')
+
+  # In a class cannot write to interface static
+  lines =<< trim END
+    vim9script
+    interface I
+        public static num: number
+    endinterface
+    class C implements I
+        public static num = 3
+        def F()
+            I.num = 7
+        enddef
+    endclass
+    C.new().F()
+  END
+  v9.CheckScriptFailure(lines, 'E1409: Cannot directly access interface "I" 
static member "num"')
+
+  # In a def cannot read from interface static
+  lines =<< trim END
+    vim9script
+    interface I
+        public static num: number
+    endinterface
+    def F()
+        var x = I.num
+    enddef
+    F()
+  END
+  v9.CheckScriptFailure(lines, 'E1409: Cannot directly access interface "I" 
static member "num"')
+
+  # In a def cannot write to interface static
+  lines =<< trim END
+    vim9script
+    interface I
+        public static num: number
+    endinterface
+    def F()
+        I.num = 7
+    enddef
+    F()
+  END
+  v9.CheckScriptFailure(lines, 'E1409: Cannot directly access interface "I" 
static member "num"')
+
+  # script level cannot read interface static
+  lines =<< trim END
+    vim9script
+    interface I
+        public static s_var1: number
+    endinterface
+
+    var x = I.s_var1
+  END
+  v9.CheckScriptFailure(lines, 'E1409: Cannot directly access interface "I" 
static member "s_var1"')
+
+  # script level cannot write interface static
+  lines =<< trim END
+    vim9script
+    interface I
+        public static s_var1: number
+    endinterface
+
+    I.s_var1 = 3
+  END
+  v9.CheckScriptFailure(lines, 'E1409: Cannot directly access interface "I" 
static member "I.s_var1 = 3"')
+
+enddef
+
+def Test_static_member_access_outside_class()
+  # Verify access of statics implemented from interface
+  # in a :def (outside of a class)
+  # Note the order of the static is different
+  # between the interface and the class,
+  # since they are allocated in order in each interface/class;
+  # so the static index is mapped from interfaced to  class as needed.
+
+  # Check reading statics
+  var lines =<< trim END
+    vim9script
+
+    interface I
+        public static s_var1: number
+        public static s_var2: number
+    endinterface
+
+    class C implements I
+        public static s_var2 = 2
+        public static x_static = 7
+        public static s_var1 = 1
+    endclass
+
+    def F1(): number
+        assert_equal(1, C.s_var1)
+        assert_equal(2, C.s_var2)
+        assert_equal(7, C.x_static)
+        return 11
+    enddef
+
+    # access the class static through an interface argument
+    def F2(i: I): number
+        assert_equal(1, i.s_var1)
+        assert_equal(2, i.s_var2)
+        return 22
+    enddef
+
+    # access the class static through an object interface
+    def F3(o: C): number
+        assert_equal(1, o.s_var1)
+        assert_equal(2, o.s_var2)
+        assert_equal(7, o.x_static)
+        return 33
+    enddef
+
+    assert_equal(11, F1())
+    var c = C.new()
+    assert_equal(22, F2(c))
+    assert_equal(33, F3(c))
+  END
+  v9.CheckScriptSuccess(lines)
+enddef
+
 " Test for accessing a private member outside a class in a def function
 def Test_private_member_access_outside_class()
   # private object member variable
@@ -3794,6 +3960,63 @@ def Test_private_member_access_outside_class()
     T()
   END
   v9.CheckScriptFailure(lines, 'E1089: Unknown variable: _a = 1')
+
+  # private static member variable
+  lines =<< trim END
+    vim9script
+    class A
+      static _val = 10
+    endclass
+    def T()
+      var a = A.new()
+      var x = a._val
+    enddef
+    T()
+  END
+  v9.CheckScriptFailure(lines, 'E1333: Cannot access private member: _val')
+
+  # private static member variable
+  lines =<< trim END
+    vim9script
+    class A
+      static _val = 10
+    endclass
+    def T()
+      var a = A.new()
+      a._val = 3
+    enddef
+    T()
+  END
+  # TODO: wrong error, should be about private member
+  v9.CheckScriptFailure(lines, 'E1089: Unknown variable')
+
+  # private static class variable
+  lines =<< trim END
+    vim9script
+    class A
+      static _val = 10
+    endclass
+    def T()
+      var x = A._val
+    enddef
+    T()
+  END
+  v9.CheckScriptFailure(lines, 'E1333: Cannot access private member: _val')
+
+  # private static class variable
+  lines =<< trim END
+    vim9script
+    class A
+      static _val = 10
+    endclass
+    def T()
+      A._val = 3
+    enddef
+    T()
+  END
+  v9.CheckScriptFailure(lines, 'E1333: Cannot access private member: _val')
+
+
 enddef
 
 " Test for changing the member access of an interface in a implementation class
diff --git a/src/version.c b/src/version.c
index 5eb9c59ab..0e3239e24 100644
--- a/src/version.c
+++ b/src/version.c
@@ -699,6 +699,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    1867,
 /**/
     1866,
 /**/
diff --git a/src/vim9.h b/src/vim9.h
index cf24c8d0e..e30ebda5c 100644
--- a/src/vim9.h
+++ b/src/vim9.h
@@ -498,6 +498,7 @@ typedef struct {
 typedef struct {
     class_T    *cm_class;
     int                cm_idx;
+    int                cm_static;
 } classmember_T;
 // arguments to ISN_STOREINDEX
 typedef struct {
diff --git a/src/vim9class.c b/src/vim9class.c
index 77fd5461b..30a3b6647 100644
--- a/src/vim9class.c
+++ b/src/vim9class.c
@@ -220,9 +220,11 @@ add_members_to_class(
  * "cl" implementing that interface.
  */
     int
-object_index_from_itf_index(class_T *itf, int is_method, int idx, class_T *cl)
+object_index_from_itf_index(class_T *itf, int is_method, int idx, class_T *cl,
+                                                               int is_static)
 {
-    if (idx > (is_method ? itf->class_obj_method_count
+    if (idx >= (is_method ? itf->class_obj_method_count
+                                  : is_static ? itf->class_class_member_count
                                                : itf->class_obj_member_count))
     {
        siemsg("index %d out of range for interface %s", idx, itf->class_name);
@@ -245,8 +247,28 @@ object_index_from_itf_index(class_T *itf, int is_method, 
int idx, class_T *cl)
                                              cl->class_name, itf->class_name);
        return 0;
     }
-    int *table = (int *)(i2c + 1);
-    return table[idx];
+    if (is_static)
+    {
+       // TODO: Need a table for fast lookup?
+       char_u *name = itf->class_class_members[idx].ocm_name;
+       for (int i = 0; i < i2c->i2c_class->class_class_member_count; ++i)
+       {
+           ocmember_T *m = &i2c->i2c_class->class_class_members[i];
+           if (STRCMP(name, m->ocm_name) == 0)
+           {
+               return i;
+           }
+       }
+       siemsg("class %s, interface %s, static %s not found",
+                                     cl->class_name, itf->class_name, name);
+       return 0;
+    }
+    else
+    {
+       // A table follows the i2c for the class
+       int *table = (int *)(i2c + 1);
+       return table[idx];
+    }
 }
 
 /*
@@ -1808,6 +1830,12 @@ class_object_index(
                    semsg(_(e_cannot_access_private_member_str), m->ocm_name);
                    return FAIL;
                }
+               if ((cl->class_flags & CLASS_INTERFACE) != 0)
+               {
+                   semsg(_(e_interface_static_direct_access_str),
+                                               cl->class_name, m->ocm_name);
+                   return FAIL;
+               }
 
                typval_T *tv = &cl->class_members_tv[i];
                copy_tv(tv, rettv);
diff --git a/src/vim9compile.c b/src/vim9compile.c
index 2786026ce..5778be6e2 100644
--- a/src/vim9compile.c
+++ b/src/vim9compile.c
@@ -1874,7 +1874,13 @@ compile_lhs(
                                        &lhs->lhs_member_idx, &m);
            if (lhs->lhs_member_idx < 0)
                return FAIL;
-
+           if ((cl->class_flags & CLASS_INTERFACE) != 0
+                                       && lhs->lhs_type->tt_type == VAR_CLASS)
+           {
+               semsg(_(e_interface_static_direct_access_str),
+                                               cl->class_name, m->ocm_name);
+               return FAIL;
+           }
            // If it is private member variable, then accessing it outside the
            // class is not allowed.
            if ((m->ocm_access != VIM_ACCESS_ALL) && !inside_class(cctx, cl))
@@ -2112,8 +2118,9 @@ compile_load_lhs_with_index(lhs_T *lhs, char_u 
*var_start, cctx_T *cctx)
                return FAIL;
        }
        if (cl->class_flags & CLASS_INTERFACE)
-           return generate_GET_ITF_MEMBER(cctx, cl, lhs->lhs_member_idx, type);
-       return generate_GET_OBJ_MEMBER(cctx, lhs->lhs_member_idx, type);
+           return generate_GET_ITF_MEMBER(cctx, cl, lhs->lhs_member_idx, type,
+                                                                       FALSE);
+       return generate_GET_OBJ_MEMBER(cctx, lhs->lhs_member_idx, type, FALSE);
     }
 
     compile_load_lhs(lhs, var_start, NULL, cctx);
diff --git a/src/vim9execute.c b/src/vim9execute.c
index c29873c89..c98370eef 100644
--- a/src/vim9execute.c
+++ b/src/vim9execute.c
@@ -2316,8 +2316,8 @@ execute_storeindex(isn_T *iptr, ectx_T *ectx)
                class_T     *itf = iptr->isn_arg.storeindex.si_class;
                if (itf != NULL)
                    // convert interface member index to class member index
-                   lidx = object_index_from_itf_index(itf, FALSE,
-                                                       lidx, obj->obj_class);
+                   lidx = object_index_from_itf_index(itf, FALSE, lidx,
+                                                      obj->obj_class, FALSE);
            }
            else
            {
@@ -4261,7 +4261,8 @@ exec_instructions(ectx_T *ectx)
 
                    // convert the interface index to the object index
                    int idx = object_index_from_itf_index(mfunc->cmf_itf,
-                                                   TRUE, mfunc->cmf_idx, cl);
+                                                   TRUE, mfunc->cmf_idx, cl,
+                                                   FALSE);
 
                    if (call_ufunc(cl->class_obj_methods[idx], NULL,
                                mfunc->cmf_argcount, ectx, NULL, NULL) == FAIL)
@@ -4410,7 +4411,8 @@ exec_instructions(ectx_T *ectx)
 
                        // convert the interface index to the object index
                        int idx = object_index_from_itf_index(extra->fre_class,
-                                             TRUE, extra->fre_method_idx, cl);
+                                             TRUE, extra->fre_method_idx, cl,
+                                             FALSE);
                        ufunc = cl->class_obj_methods[idx];
                    }
                    else if (extra == NULL || extra->fre_func_name == NULL)
@@ -5389,20 +5391,25 @@ exec_instructions(ectx_T *ectx)
                        goto on_error;
                    }
 
+                   int is_static = iptr->isn_arg.classmember.cm_static;
                    int idx;
                    if (iptr->isn_type == ISN_GET_OBJ_MEMBER)
-                       idx = iptr->isn_arg.number;
+                       idx = iptr->isn_arg.classmember.cm_idx;
                    else
                    {
                        idx = iptr->isn_arg.classmember.cm_idx;
                        // convert the interface index to the object index
                        idx = object_index_from_itf_index(
-                                           iptr->isn_arg.classmember.cm_class,
-                                           FALSE, idx, obj->obj_class);
+                                       iptr->isn_arg.classmember.cm_class,
+                                       FALSE, idx, obj->obj_class, is_static);
                    }
 
-                   // the members are located right after the object struct
-                   typval_T *mtv = ((typval_T *)(obj + 1)) + idx;
+                   // The members are located right after the object struct.
+                   typval_T *mtv;
+                   if (is_static)
+                       mtv = &obj->obj_class->class_members_tv[idx];
+                   else
+                       mtv = ((typval_T *)(obj + 1)) + idx;
                    copy_tv(mtv, tv);
 
                    // Unreference the object after getting the member, it may
diff --git a/src/vim9expr.c b/src/vim9expr.c
index e89496938..f9756c0bb 100644
--- a/src/vim9expr.c
+++ b/src/vim9expr.c
@@ -407,8 +407,27 @@ compile_class_object_index(cctx_T *cctx, char_u **arg, 
type_T *type)
 
                *arg = name_end;
                if (cl->class_flags & (CLASS_INTERFACE | CLASS_EXTENDED))
-                   return generate_GET_ITF_MEMBER(cctx, cl, i, m->ocm_type);
-               return generate_GET_OBJ_MEMBER(cctx, i, m->ocm_type);
+                   return generate_GET_ITF_MEMBER(cctx, cl, i, m->ocm_type,
+                                                                       FALSE);
+               return generate_GET_OBJ_MEMBER(cctx, i, m->ocm_type, FALSE);
+           }
+       }
+
+       for (int i = 0; i < cl->class_class_member_count; ++i)
+       {
+           ocmember_T *m = &cl->class_class_members[i];
+           if (STRNCMP(name, m->ocm_name, len) == 0 && m->ocm_name[len] == NUL)
+           {
+               if (*name == '_' && !inside_class(cctx, cl))
+               {
+                   semsg(_(e_cannot_access_private_member_str), m->ocm_name);
+                   return FAIL;
+               }
+               *arg = name_end;
+               if (cl->class_flags & (CLASS_INTERFACE | CLASS_EXTENDED))
+                   return generate_GET_ITF_MEMBER(cctx, cl, i, m->ocm_type,
+                                                                       TRUE);
+               return generate_GET_OBJ_MEMBER(cctx, i, m->ocm_type, TRUE);
            }
        }
 
@@ -439,6 +458,13 @@ compile_class_object_index(cctx_T *cctx, char_u **arg, 
type_T *type)
            ocmember_T *m = &cl->class_class_members[idx];
            if (STRNCMP(name, m->ocm_name, len) == 0 && m->ocm_name[len] == NUL)
            {
+               // Note: type->tt_type = VAR_CLASS
+               if ((cl->class_flags & CLASS_INTERFACE) != 0)
+               {
+                   semsg(_(e_interface_static_direct_access_str),
+                                               cl->class_name, m->ocm_name);
+                   return FAIL;
+               }
                if (*name == '_' && !inside_class(cctx, cl))
                {
                    semsg(_(e_cannot_access_private_member_str), m->ocm_name);
diff --git a/src/vim9instr.c b/src/vim9instr.c
index ba1aa0c1d..68fad25d5 100644
--- a/src/vim9instr.c
+++ b/src/vim9instr.c
@@ -136,7 +136,7 @@ generate_CONSTRUCT(cctx_T *cctx, class_T *cl)
  * index.
  */
     int
-generate_GET_OBJ_MEMBER(cctx_T *cctx, int idx, type_T *type)
+generate_GET_OBJ_MEMBER(cctx_T *cctx, int idx, type_T *type, int is_static)
 {
     RETURN_OK_IF_SKIP(cctx);
 
@@ -145,7 +145,9 @@ generate_GET_OBJ_MEMBER(cctx_T *cctx, int idx, type_T *type)
     if (isn == NULL)
        return FAIL;
 
-    isn->isn_arg.number = idx;
+    isn->isn_arg.classmember.cm_class = NULL;
+    isn->isn_arg.classmember.cm_idx = idx;
+    isn->isn_arg.classmember.cm_static = is_static;
     return push_type_stack2(cctx, type, &t_any);
 }
 
@@ -154,7 +156,8 @@ generate_GET_OBJ_MEMBER(cctx_T *cctx, int idx, type_T *type)
  * by index.
  */
     int
-generate_GET_ITF_MEMBER(cctx_T *cctx, class_T *itf, int idx, type_T *type)
+generate_GET_ITF_MEMBER(cctx_T *cctx, class_T *itf, int idx, type_T *type,
+                                                               int is_static)
 {
     RETURN_OK_IF_SKIP(cctx);
 
@@ -166,6 +169,7 @@ generate_GET_ITF_MEMBER(cctx_T *cctx, class_T *itf, int 
idx, type_T *type)
     isn->isn_arg.classmember.cm_class = itf;
     ++itf->class_refcount;
     isn->isn_arg.classmember.cm_idx = idx;
+    isn->isn_arg.classmember.cm_static = is_static;
     return push_type_stack2(cctx, type, &t_any);
 }
 

-- 
-- 
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 [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/vim_dev/E1qdGRU-00FTMG-PP%40256bit.org.

Raspunde prin e-mail lui