changeset 31980:13329cb9faee v9.0.1322

patch 9.0.1322: crash when indexing "any" which is an object Commit: https://github.com/vim/vim/commit/2c1c803c7e0cc356dd55a2cd49fbffbbf7db766e Author: Bram Moolenaar <Bram@vim.org> Date: Sat Feb 18 18:38:37 2023 +0000 patch 9.0.1322: crash when indexing "any" which is an object Problem: Crash when indexing "any" which is an object. Solution: Check the index is a number. Do not check the member type of an object. (closes #12019)
author Bram Moolenaar <Bram@vim.org>
date Sat, 18 Feb 2023 19:45:07 +0100
parents e13e9924e503
children 834ba9805a25
files src/testdir/test_vim9_class.vim src/version.c src/vim9compile.c src/vim9execute.c
diffstat 4 files changed, 105 insertions(+), 13 deletions(-) [+]
line wrap: on
line diff
--- a/src/testdir/test_vim9_class.vim
+++ b/src/testdir/test_vim9_class.vim
@@ -253,6 +253,56 @@ def Test_class_member_initializer()
   v9.CheckScriptSuccess(lines)
 enddef
 
+def Test_member_any_used_as_object()
+  var lines =<< trim END
+      vim9script
+
+      class Inner
+        this.value: number = 0
+      endclass
+
+      class Outer
+        this.inner: any
+      endclass
+
+      def F(outer: Outer)
+        outer.inner.value = 1
+      enddef
+
+      var inner_obj = Inner.new(0)
+      var outer_obj = Outer.new(inner_obj)
+      F(outer_obj)
+      assert_equal(1, inner_obj.value)
+  END
+  v9.CheckScriptSuccess(lines)
+
+  lines =<< trim END
+      vim9script
+
+      class Inner
+        this.value: number = 0
+      endclass
+
+      class Outer
+        this.inner: Inner
+      endclass
+
+      def F(outer: Outer)
+        outer.inner.value = 1
+      enddef
+
+      def Test_assign_to_nested_typed_member()
+        var inner = Inner.new(0)
+        var outer = Outer.new(inner)
+        F(outer)
+        assert_equal(1, inner.value)
+      enddef
+
+      Test_assign_to_nested_typed_member()
+  END
+  v9.CheckScriptSuccess(lines)
+enddef
+
 def Test_assignment_with_operator()
   var lines =<< trim END
       vim9script
--- a/src/version.c
+++ b/src/version.c
@@ -696,6 +696,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    1322,
+/**/
     1321,
 /**/
     1320,
--- a/src/vim9compile.c
+++ b/src/vim9compile.c
@@ -2011,13 +2011,13 @@ compile_load_lhs(
 	size_t	    varlen = lhs->lhs_varlen;
 	int	    c = var_start[varlen];
 	int	    lines_len = cctx->ctx_ufunc->uf_lines.ga_len;
-	char_u	    *p = var_start;
 	int	    res;
 
 	// Evaluate "ll[expr]" of "ll[expr][idx]".  End the line with a NUL and
 	// limit the lines array length to avoid skipping to a following line.
 	var_start[varlen] = NUL;
 	cctx->ctx_ufunc->uf_lines.ga_len = cctx->ctx_lnum + 1;
+	char_u *p = var_start;
 	res = compile_expr0(&p, cctx);
 	var_start[varlen] = c;
 	cctx->ctx_ufunc->uf_lines.ga_len = lines_len;
@@ -2031,10 +2031,15 @@ compile_load_lhs(
 
 	lhs->lhs_type = cctx->ctx_type_stack.ga_len == 0 ? &t_void
 						  : get_type_on_stack(cctx, 0);
-	// now we can properly check the type
-	if (rhs_type != NULL && lhs->lhs_type->tt_member != NULL
+	// Now we can properly check the type.  The variable is indexed, thus
+	// we need the member type.  For a class or object we don't know the
+	// type yet, it depends on what member is used.
+	vartype_T vartype = lhs->lhs_type->tt_type;
+	type_T *member_type = lhs->lhs_type->tt_member;
+	if (rhs_type != NULL && member_type != NULL
+		&& vartype != VAR_OBJECT && vartype != VAR_CLASS
 		&& rhs_type != &t_void
-		&& need_type(rhs_type, lhs->lhs_type->tt_member, FALSE,
+		&& need_type(rhs_type, member_type, FALSE,
 					    -2, 0, cctx, FALSE, FALSE) == FAIL)
 	    return FAIL;
     }
--- a/src/vim9execute.c
+++ b/src/vim9execute.c
@@ -2126,9 +2126,13 @@ execute_storeindex(isn_T *iptr, ectx_T *
     vartype_T	dest_type = iptr->isn_arg.storeindex.si_vartype;
     typval_T	*tv;
     typval_T	*tv_idx = STACK_TV_BOT(-2);
+    long	lidx = 0;
     typval_T	*tv_dest = STACK_TV_BOT(-1);
     int		status = OK;
 
+    if (tv_idx->v_type == VAR_NUMBER)
+	lidx = (long)tv_idx->vval.v_number;
+
     // Stack contains:
     // -3 value to be stored
     // -2 index
@@ -2140,7 +2144,41 @@ execute_storeindex(isn_T *iptr, ectx_T *
 	dest_type = tv_dest->v_type;
 	if (dest_type == VAR_DICT)
 	    status = do_2string(tv_idx, TRUE, FALSE);
-	else if (dest_type == VAR_LIST && tv_idx->v_type != VAR_NUMBER)
+	else if (dest_type == VAR_OBJECT && tv_idx->v_type == VAR_STRING)
+	{
+	    // Need to get the member index now that the class is known.
+	    object_T *obj = tv_dest->vval.v_object;
+	    class_T *cl = obj->obj_class;
+	    char_u  *member = tv_idx->vval.v_string;
+
+	    ocmember_T *m = NULL;
+	    for (int i = 0; i < cl->class_obj_member_count; ++i)
+	    {
+		m = &cl->class_obj_members[i];
+		if (STRCMP(member, m->ocm_name) == 0)
+		{
+		    if (*member == '_')
+		    {
+			semsg(_(e_cannot_access_private_member_str),
+								  m->ocm_name);
+			status = FAIL;
+		    }
+
+		    lidx = i;
+		    break;
+		}
+		m = NULL;
+	    }
+
+	    if (m == NULL)
+	    {
+		semsg(_(e_member_not_found_on_object_str_str),
+						       cl->class_name, member);
+		status = FAIL;
+	    }
+	}
+	else if ((dest_type == VAR_LIST || dest_type == VAR_OBJECT)
+		&& tv_idx->v_type != VAR_NUMBER)
 	{
 	    emsg(_(e_number_expected));
 	    status = FAIL;
@@ -2151,7 +2189,6 @@ execute_storeindex(isn_T *iptr, ectx_T *
     {
 	if (dest_type == VAR_LIST)
 	{
-	    long	    lidx = (long)tv_idx->vval.v_number;
 	    list_T	    *list = tv_dest->vval.v_list;
 
 	    if (list == NULL)
@@ -2224,7 +2261,6 @@ execute_storeindex(isn_T *iptr, ectx_T *
 	}
 	else if (dest_type == VAR_BLOB)
 	{
-	    long	    lidx = (long)tv_idx->vval.v_number;
 	    blob_T	    *blob = tv_dest->vval.v_blob;
 	    varnumber_T	    nr;
 	    int		    error = FALSE;
@@ -2255,18 +2291,17 @@ execute_storeindex(isn_T *iptr, ectx_T *
 	}
 	else if (dest_type == VAR_CLASS || dest_type == VAR_OBJECT)
 	{
-	    long	    idx = (long)tv_idx->vval.v_number;
 	    object_T	    *obj = tv_dest->vval.v_object;
 	    typval_T	    *otv = (typval_T *)(obj + 1);
 
 	    class_T	    *itf = iptr->isn_arg.storeindex.si_class;
 	    if (itf != NULL)
 		// convert interface member index to class member index
-		idx = object_index_from_itf_index(itf, FALSE,
-							  idx, obj->obj_class);
-
-	    clear_tv(&otv[idx]);
-	    otv[idx] = *tv;
+		lidx = object_index_from_itf_index(itf, FALSE,
+							 lidx, obj->obj_class);
+
+	    clear_tv(&otv[lidx]);
+	    otv[lidx] = *tv;
 	}
 	else
 	{