summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorErnie Rael <errael@raelity.com>2023-09-04 22:30:41 +0200
committerChristian Brabandt <cb@256bit.org>2023-09-04 22:30:41 +0200
commit18143d3111b2122c7a94ca51085a60b3073cb139 (patch)
treec882f6d5b2f9e8f5643ab6c23a8555d0c1cc3373
parentdccc29c228f8336ef7dd069a447886639af4458e (diff)
patch 9.0.1867: Vim9: access to interface statics possiblev9.0.1867
Problem: Vim9: access to interface statics possible Solution: Prevent direct access to interface statics closes: #13007 Signed-off-by: Christian Brabandt <cb@256bit.org> Co-authored-by: Ernie Rael <errael@raelity.com>
-rw-r--r--src/errors.h3
-rw-r--r--src/eval.c8
-rw-r--r--src/proto/vim9class.pro2
-rw-r--r--src/proto/vim9instr.pro4
-rw-r--r--src/testdir/test_vim9_class.vim223
-rw-r--r--src/version.c2
-rw-r--r--src/vim9.h1
-rw-r--r--src/vim9class.c36
-rw-r--r--src/vim9compile.c13
-rw-r--r--src/vim9execute.c25
-rw-r--r--src/vim9expr.c30
-rw-r--r--src/vim9instr.c10
12 files changed, 332 insertions, 25 deletions
diff --git a/src/errors.h b/src/errors.h
index c0e98df78b..e719ffde1a 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 7cfe68cc66..4e1dbdeeaf 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 e685c03691..0f8aa7f1fe 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 1ba5639a56..3f287d0640 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 8ae136f283..a293f247db 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 5eb9c59ab1..0e3239e24e 100644
--- a/src/version.c
+++ b/src/version.c
@@ -700,6 +700,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
+ 1867,
+/**/
1866,
/**/
1865,
diff --git a/src/vim9.h b/src/vim9.h
index cf24c8d0ea..e30ebda5c8 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 77fd5461b3..30a3b66471 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 2786026ce6..5778be6e27 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 c29873c89c..c98370eefb 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 e894969384..f9756c0bba 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 ba1aa0c1dd..68fad25d51 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);
}