# HG changeset patch # User Christian Brabandt # Date 1696785306 -7200 # Node ID f61713271934254733558231e198d9e127553fe2 # Parent 398e729a5cd96284f057fc340cc6ef4ee4e04b86 patch 9.0.2002: Vim9: need cleanup of class related interface code Commit: https://github.com/vim/vim/commit/b852305dbf42f1206ecc6ae414fc200235fe2963 Author: Yegappan Lakshmanan Date: Sun Oct 8 19:07:39 2023 +0200 patch 9.0.2002: Vim9: need cleanup of class related interface code Problem: Vim9: need cleanup of class related interface code Solution: Remove the unused class variable and class method related code for interfaces. Remove unused class variable and class method related code for interfaces. Refactor the code. Optimize the object/class member double lookup in compile_lhs(). Change unused global functions to static functions. closes: #13302 Signed-off-by: Christian Brabandt Co-authored-by: Yegappan Lakshmanan diff --git a/src/proto/vim9class.pro b/src/proto/vim9class.pro --- a/src/proto/vim9class.pro +++ b/src/proto/vim9class.pro @@ -10,11 +10,8 @@ ufunc_T *find_class_func(char_u **arg); int class_member_idx(class_T *cl, char_u *name, size_t namelen); ocmember_T *class_member_lookup(class_T *cl, char_u *name, size_t namelen, int *idx); int class_method_idx(class_T *cl, char_u *name, size_t namelen); -ufunc_T *class_method_lookup(class_T *cl, char_u *name, size_t namelen, int *idx); -int object_member_idx(class_T *cl, char_u *name, size_t namelen); ocmember_T *object_member_lookup(class_T *cl, char_u *name, size_t namelen, int *idx); int object_method_idx(class_T *cl, char_u *name, size_t namelen); -ufunc_T *object_method_lookup(class_T *cl, char_u *name, size_t namelen, int *idx); ocmember_T *member_lookup(class_T *cl, vartype_T v_type, char_u *name, size_t namelen, int *idx); void emsg_var_cl_define(char *msg, char_u *name, size_t len, class_T *cl); ufunc_T *method_lookup(class_T *cl, vartype_T v_type, char_u *name, size_t namelen, int *idx); @@ -26,7 +23,6 @@ void class_unref(class_T *cl); int class_free_nonref(int copyID); int set_ref_in_classes(int copyID); void object_created(object_T *obj); -void object_cleared(object_T *obj); int object_free_nonref(int copyID); void method_not_found_msg(class_T *cl, vartype_T v_type, char_u *name, size_t len); void member_not_found_msg(class_T *cl, vartype_T v_type, char_u *name, size_t len); diff --git a/src/testdir/test_vim9_class.vim b/src/testdir/test_vim9_class.vim --- a/src/testdir/test_vim9_class.vim +++ b/src/testdir/test_vim9_class.vim @@ -975,6 +975,28 @@ def Test_class_new_with_object_member() Check() END v9.CheckSourceSuccess(lines) + + # Try using "this." argument in a class method + lines =<< trim END + vim9script + class A + this.val = 10 + static def Foo(this.val: number) + enddef + endclass + END + v9.CheckSourceFailure(lines, 'E1390: Cannot use an object variable "this.val" except with the "new" method', 4) + + # Try using "this." argument in an object method + lines =<< trim END + vim9script + class A + this.val = 10 + def Foo(this.val: number) + enddef + endclass + END + v9.CheckSourceFailure(lines, 'E1390: Cannot use an object variable "this.val" except with the "new" method', 4) enddef def Test_class_object_member_inits() @@ -1722,7 +1744,7 @@ def Test_class_member() var a = A.new() var v = a.bar END - v9.CheckSourceFailure(lines, 'E1326: Variable not found on object "A": bar', 5) + v9.CheckSourceFailure(lines, 'E1337: Class variable "bar" not found in class "A"', 5) enddef " These messages should show the defining class of the variable (base class), @@ -4255,7 +4277,7 @@ def Test_private_object_method() var a = A.new() a._Foo() END - v9.CheckSourceFailure(lines, 'E1366: Cannot access private method: _Foo()', 9) + v9.CheckSourceFailure(lines, 'E1366: Cannot access private method: _Foo', 9) # Try calling a private method using an object (from a def function) lines =<< trim END @@ -4468,7 +4490,7 @@ def Test_private_object_method() var c = C.new() assert_equal(1234, c._Foo()) END - v9.CheckSourceFailure(lines, 'E1366: Cannot access private method: _Foo()', 16) + v9.CheckSourceFailure(lines, 'E1366: Cannot access private method: _Foo', 16) # Using "_" prefix in a method name should fail outside of a class lines =<< trim END @@ -4494,7 +4516,7 @@ def Test_private_class_method() endclass A._Foo() END - v9.CheckSourceFailure(lines, 'E1366: Cannot access private method: _Foo()', 8) + v9.CheckSourceFailure(lines, 'E1366: Cannot access private method: _Foo', 8) # Try calling a class private method (from a def function) lines =<< trim END @@ -5122,7 +5144,7 @@ def Test_class_variable_access_using_obj var a = A.new() echo a.svar2 END - v9.CheckSourceFailure(lines, 'E1375: Class variable "svar2" accessible only using class "A"', 8) + v9.CheckSourceFailure(lines, 'E1337: Class variable "svar2" not found in class "A"', 8) # Cannot write to a class variable using an object in script context lines =<< trim END @@ -5597,7 +5619,7 @@ def Test_class_variable() var a = A.new() var i = a.val END - v9.CheckSourceFailure(lines, 'E1375: Class variable "val" accessible only using class "A"', 7) + v9.CheckSourceFailure(lines, 'E1337: Class variable "val" not found in class "A"', 7) # Modifying a class variable using an object at function level lines =<< trim END @@ -5969,6 +5991,18 @@ def Test_extend_interface() END v9.CheckSourceSuccess(lines) + # extending empty interface + lines =<< trim END + vim9script + interface A + endinterface + interface B extends A + endinterface + class C implements B + endclass + END + v9.CheckSourceSuccess(lines) + lines =<< trim END vim9script interface A @@ -6567,6 +6601,17 @@ def Test_reserved_varname() o.F() END v9.CheckSourceFailure(lines, $'E1034: Cannot use reserved name {kword}', 3) + + # class variable name + if kword != 'this' + lines =<< trim eval END + vim9script + class C + public static {kword}: list = [1, 2, 3] + endclass + END + v9.CheckSourceFailure(lines, $'E1034: Cannot use reserved name {kword}', 3) + endif endfor enddef diff --git a/src/version.c b/src/version.c --- 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 */ /**/ + 2002, +/**/ 2001, /**/ 2000, diff --git a/src/vim9class.c b/src/vim9class.c --- a/src/vim9class.c +++ b/src/vim9class.c @@ -555,7 +555,6 @@ validate_abstract_class_methods( intf_variable_present( char_u *intf_class_name, ocmember_T *if_var, - int is_class_var, ocmember_T *cl_mt, int cl_member_count, class_T *extends_cl) @@ -600,15 +599,10 @@ intf_variable_present( if (!variable_present && extends_cl != NULL) { - int ext_cl_count = is_class_var - ? extends_cl->class_class_member_count - : extends_cl->class_obj_member_count; - ocmember_T *ext_cl_mt = is_class_var - ? extends_cl->class_class_members - : extends_cl->class_obj_members; + int ext_cl_count = extends_cl->class_obj_member_count; + ocmember_T *ext_cl_mt = extends_cl->class_obj_members; return intf_variable_present(intf_class_name, if_var, - is_class_var, ext_cl_mt, - ext_cl_count, + ext_cl_mt, ext_cl_count, extends_cl->class_extends); } @@ -616,43 +610,32 @@ intf_variable_present( } /* - * Check the variables of the interface class "ifcl" match the class variables - * ("classmembers_gap") and object variables ("objmembers_gap") of a class. - * Returns TRUE if the class and object variables names are valid. + * Check the variables of the interface class "ifcl" match object variables + * ("objmembers_gap") of a class. + * Returns TRUE if the object variables names are valid. */ static int validate_interface_variables( char_u *intf_class_name, class_T *ifcl, - garray_T *classmembers_gap, garray_T *objmembers_gap, class_T *extends_cl) { - for (int loop = 1; loop <= 2; ++loop) + int if_count = ifcl->class_obj_member_count; + if (if_count == 0) + return TRUE; + + ocmember_T *if_ms = ifcl->class_obj_members; + ocmember_T *cl_ms = (ocmember_T *)(objmembers_gap->ga_data); + int cl_count = objmembers_gap->ga_len; + for (int if_i = 0; if_i < if_count; ++if_i) { - // loop == 1: check class variables - // loop == 2: check object variables - int is_class_var = (loop == 1); - int if_count = is_class_var ? ifcl->class_class_member_count - : ifcl->class_obj_member_count; - if (if_count == 0) - continue; - ocmember_T *if_ms = is_class_var ? ifcl->class_class_members - : ifcl->class_obj_members; - ocmember_T *cl_ms = (ocmember_T *)(is_class_var - ? classmembers_gap->ga_data - : objmembers_gap->ga_data); - int cl_count = is_class_var ? classmembers_gap->ga_len - : objmembers_gap->ga_len; - for (int if_i = 0; if_i < if_count; ++if_i) + if (!intf_variable_present(intf_class_name, &if_ms[if_i], cl_ms, + cl_count, extends_cl)) { - if (!intf_variable_present(intf_class_name, &if_ms[if_i], - is_class_var, cl_ms, cl_count, extends_cl)) - { - semsg(_(e_variable_str_of_interface_str_not_implemented), - if_ms[if_i].ocm_name, intf_class_name); - return FALSE; - } + semsg(_(e_variable_str_of_interface_str_not_implemented), + if_ms[if_i].ocm_name, intf_class_name); + return FALSE; } } @@ -685,7 +668,6 @@ intf_method_type_matches(ufunc_T *if_met static int intf_method_present( ufunc_T *if_ufunc, - int is_class_method, ufunc_T **cl_fp, int cl_count, class_T *extends_cl) @@ -707,15 +689,10 @@ intf_method_present( if (!method_present && extends_cl != NULL) { - ufunc_T **ext_cl_fp = (ufunc_T **)(is_class_method - ? extends_cl->class_class_functions - : extends_cl->class_obj_methods); - int ext_cl_count = is_class_method - ? extends_cl->class_class_function_count - : extends_cl->class_obj_method_count; - return intf_method_present(if_ufunc, is_class_method, ext_cl_fp, - ext_cl_count, - extends_cl->class_extends); + ufunc_T **ext_cl_fp = (ufunc_T **)(extends_cl->class_obj_methods); + int ext_cl_count = extends_cl->class_obj_method_count; + return intf_method_present(if_ufunc, ext_cl_fp, ext_cl_count, + extends_cl->class_extends); } return method_present; @@ -733,37 +710,25 @@ intf_method_present( validate_interface_methods( char_u *intf_class_name, class_T *ifcl, - garray_T *classfunctions_gap, garray_T *objmethods_gap, class_T *extends_cl) { - for (int loop = 1; loop <= 2; ++loop) + int if_count = ifcl->class_obj_method_count; + if (if_count == 0) + return TRUE; + + ufunc_T **if_fp = ifcl->class_obj_methods; + ufunc_T **cl_fp = (ufunc_T **)(objmethods_gap->ga_data); + int cl_count = objmethods_gap->ga_len; + for (int if_i = 0; if_i < if_count; ++if_i) { - // loop == 1: check class methods - // loop == 2: check object methods - int is_class_method = (loop == 1); - int if_count = is_class_method ? ifcl->class_class_function_count - : ifcl->class_obj_method_count; - if (if_count == 0) - continue; - ufunc_T **if_fp = is_class_method ? ifcl->class_class_functions - : ifcl->class_obj_methods; - ufunc_T **cl_fp = (ufunc_T **)(is_class_method - ? classfunctions_gap->ga_data - : objmethods_gap->ga_data); - int cl_count = is_class_method ? classfunctions_gap->ga_len - : objmethods_gap->ga_len; - for (int if_i = 0; if_i < if_count; ++if_i) + char_u *if_name = if_fp[if_i]->uf_name; + + if (!intf_method_present(if_fp[if_i], cl_fp, cl_count, extends_cl)) { - char_u *if_name = if_fp[if_i]->uf_name; - - if (!intf_method_present(if_fp[if_i], is_class_method, cl_fp, - cl_count, extends_cl)) - { - semsg(_(e_method_str_of_interface_str_not_implemented), - if_name, intf_class_name); - return FALSE; - } + semsg(_(e_method_str_of_interface_str_not_implemented), + if_name, intf_class_name); + return FALSE; } } @@ -781,8 +746,6 @@ validate_interface_methods( validate_implements_classes( garray_T *impl_gap, class_T **intf_classes, - garray_T *classfunctions_gap, - garray_T *classmembers_gap, garray_T *objmethods_gap, garray_T *objmembers_gap, class_T *extends_cl) @@ -816,15 +779,14 @@ validate_implements_classes( ++ifcl->class_refcount; // check the variables of the interface match the members of the class - success = validate_interface_variables(impl, ifcl, classmembers_gap, - objmembers_gap, extends_cl); + success = validate_interface_variables(impl, ifcl, objmembers_gap, + extends_cl); // check the functions/methods of the interface match the // functions/methods of the class if (success) - success = validate_interface_methods(impl, ifcl, - classfunctions_gap, objmethods_gap, - extends_cl); + success = validate_interface_methods(impl, ifcl, objmethods_gap, + extends_cl); clear_tv(&tv); } @@ -1873,9 +1835,7 @@ early_ret: intf_classes = ALLOC_CLEAR_MULT(class_T *, ga_impl.ga_len); success = validate_implements_classes(&ga_impl, intf_classes, - &classfunctions, &classmembers, - &objmethods, &objmembers, - extends_cl); + &objmethods, &objmembers, extends_cl); } // Check no function argument name is used as a class member. @@ -2172,18 +2132,90 @@ get_member_tv( return FAIL; } - // The object only contains a pointer to the class, the member - // values array follows right after that. - object_T *obj = rettv->vval.v_object; if (is_object) { + // The object only contains a pointer to the class, the member values + // array follows right after that. + object_T *obj = rettv->vval.v_object; typval_T *tv = (typval_T *)(obj + 1) + m_idx; copy_tv(tv, rettv); + object_unref(obj); } else + { copy_tv(&cl->class_members_tv[m_idx], rettv); + class_unref(cl); + } + + return OK; +} + +/* + * Call an object or class method "name" in class "cl". The method return + * value is returned in "rettv". + */ + static int +call_oc_method( + class_T *cl, + char_u *name, + size_t len, + char_u *name_end, + evalarg_T *evalarg, + char_u **arg, + typval_T *rettv) +{ + ufunc_T *fp; + typval_T argvars[MAX_FUNC_ARGS + 1]; + int argcount = 0; + + fp = method_lookup(cl, rettv->v_type, name, len, NULL); + if (fp == NULL) + { + method_not_found_msg(cl, rettv->v_type, name, len); + return FAIL; + } - object_unref(obj); + if (*fp->uf_name == '_') + { + // Cannot access a private method outside of a class + semsg(_(e_cannot_access_private_method_str), fp->uf_name); + return FAIL; + } + + char_u *argp = name_end; + int ret = get_func_arguments(&argp, evalarg, 0, argvars, &argcount); + if (ret == FAIL) + return FAIL; + + funcexe_T funcexe; + CLEAR_FIELD(funcexe); + funcexe.fe_evaluate = TRUE; + if (rettv->v_type == VAR_OBJECT) + { + funcexe.fe_object = rettv->vval.v_object; + ++funcexe.fe_object->obj_refcount; + } + + // Clear the class or object after calling the function, in + // case the refcount is one. + typval_T tv_tofree = *rettv; + rettv->v_type = VAR_UNKNOWN; + + // Call the user function. Result goes into rettv; + int error = call_user_func_check(fp, argcount, argvars, rettv, &funcexe, + NULL); + + // Clear the previous rettv and the arguments. + clear_tv(&tv_tofree); + for (int idx = 0; idx < argcount; ++idx) + clear_tv(&argvars[idx]); + + if (error != FCERR_NONE) + { + user_func_error(error, printable_func_name(fp), funcexe.fe_found_var); + return FAIL; + } + *arg = argp; return OK; } @@ -2242,104 +2274,22 @@ class_object_index( } if (*name_end == '(') - { - ufunc_T *fp; - - fp = method_lookup(cl, rettv->v_type, name, len, NULL); - if (fp == NULL) - { - method_not_found_msg(cl, rettv->v_type, name, len); - return FAIL; - } - - typval_T argvars[MAX_FUNC_ARGS + 1]; - int argcount = 0; - - if (*fp->uf_name == '_') - { - // Cannot access a private method outside of a class - semsg(_(e_cannot_access_private_method_str), name); - return FAIL; - } - - char_u *argp = name_end; - int ret = get_func_arguments(&argp, evalarg, 0, - argvars, &argcount); - if (ret == FAIL) - return FAIL; + // Invoke the class or object method + return call_oc_method(cl, name, len, name_end, evalarg, arg, rettv); - funcexe_T funcexe; - CLEAR_FIELD(funcexe); - funcexe.fe_evaluate = TRUE; - if (rettv->v_type == VAR_OBJECT) - { - funcexe.fe_object = rettv->vval.v_object; - ++funcexe.fe_object->obj_refcount; - } - - // Clear the class or object after calling the function, in - // case the refcount is one. - typval_T tv_tofree = *rettv; - rettv->v_type = VAR_UNKNOWN; - - // Call the user function. Result goes into rettv; - int error = call_user_func_check(fp, argcount, argvars, - rettv, &funcexe, NULL); - - // Clear the previous rettv and the arguments. - clear_tv(&tv_tofree); - for (int idx = 0; idx < argcount; ++idx) - clear_tv(&argvars[idx]); - - if (error != FCERR_NONE) - { - user_func_error(error, printable_func_name(fp), - funcexe.fe_found_var); - return FAIL; - } - *arg = argp; - return OK; - } - - else if (rettv->v_type == VAR_OBJECT) + else if (rettv->v_type == VAR_OBJECT || rettv->v_type == VAR_CLASS) { // Search in the object member variable table and the class member // variable table. - if (get_member_tv(cl, TRUE, name, len, rettv) == OK) + int is_object = rettv->v_type == VAR_OBJECT; + if (get_member_tv(cl, is_object, name, len, rettv) == OK) { *arg = name_end; return OK; } if (did_emsg == did_emsg_save) - member_not_found_msg(cl, VAR_OBJECT, name, len); - } - - else if (rettv->v_type == VAR_CLASS) - { - int m_idx; - - // class member - ocmember_T *m = class_member_lookup(cl, name, len, &m_idx); - if (m == NULL) - { - member_not_found_msg(cl, VAR_CLASS, name, len); - return FAIL; - } - - if (*name == '_') - { - emsg_var_cl_define(e_cannot_access_private_variable_str, - m->ocm_name, 0, cl); - return FAIL; - } - - typval_T *tv = &cl->class_members_tv[m_idx]; - copy_tv(tv, rettv); - class_unref(cl); - - *arg = name_end; - return OK; + member_not_found_msg(cl, is_object, name, len); } return FAIL; @@ -2433,23 +2383,11 @@ class_member_lookup(class_T *cl, char_u } /* - * Returns the index of class method "name" in the class "cl". - * Returns -1, if the method is not found. - */ - int -class_method_idx(class_T *cl, char_u *name, size_t namelen) -{ - int idx; - class_method_lookup(cl, name, namelen, &idx); - return idx; -} - -/* * Returns a pointer to the class method "name" in class "cl". * Returns NULL if the method is not found. * The method index is set in "idx". */ - ufunc_T * + static ufunc_T * class_method_lookup(class_T *cl, char_u *name, size_t namelen, int *idx) { ufunc_T *ret_fp = NULL; @@ -2471,11 +2409,23 @@ class_method_lookup(class_T *cl, char_u } /* + * Returns the index of class method "name" in the class "cl". + * Returns -1, if the method is not found. + */ + int +class_method_idx(class_T *cl, char_u *name, size_t namelen) +{ + int idx; + class_method_lookup(cl, name, namelen, &idx); + return idx; +} + +/* * Returns the index of object member variable "name" in the class "cl". * Returns -1, if the variable is not found. * If "namelen" is zero, then it is assumed that "name" is NUL terminated. */ - int + static int object_member_idx(class_T *cl, char_u *name, size_t namelen) { int idx; @@ -2519,23 +2469,11 @@ object_member_lookup(class_T *cl, char_u } /* - * Returns the index of object method "name" in the class "cl". - * Returns -1, if the method is not found. - */ - int -object_method_idx(class_T *cl, char_u *name, size_t namelen) -{ - int idx; - object_method_lookup(cl, name, namelen, &idx); - return idx; -} - -/* * Returns a pointer to the object method "name" in class "cl". * Returns NULL if the method is not found. * The object method index is set in "idx". */ - ufunc_T * + static ufunc_T * object_method_lookup(class_T *cl, char_u *name, size_t namelen, int *idx) { ufunc_T *ret_fp = NULL; @@ -2559,6 +2497,18 @@ object_method_lookup(class_T *cl, char_u } /* + * Returns the index of object method "name" in the class "cl". + * Returns -1, if the method is not found. + */ + int +object_method_idx(class_T *cl, char_u *name, size_t namelen) +{ + int idx; + object_method_lookup(cl, name, namelen, &idx); + return idx; +} + +/* * Lookup a class or object member variable by name. If v_type is VAR_CLASS, * then lookup a class member variable and if it is VAR_OBJECT, then lookup a * object member variable. @@ -2682,42 +2632,6 @@ copy_object(typval_T *from, typval_T *to } /* - * Free an object. - */ - static void -object_clear(object_T *obj) -{ - // Avoid a recursive call, it can happen if "obj" has a circular reference. - obj->obj_refcount = INT_MAX; - - class_T *cl = obj->obj_class; - - if (!cl) - return; - - // the member values are just after the object structure - typval_T *tv = (typval_T *)(obj + 1); - for (int i = 0; i < cl->class_obj_member_count; ++i) - clear_tv(tv + i); - - // Remove from the list headed by "first_object". - object_cleared(obj); - - vim_free(obj); - class_unref(cl); -} - -/* - * Unreference an object. - */ - void -object_unref(object_T *obj) -{ - if (obj != NULL && --obj->obj_refcount <= 0) - object_clear(obj); -} - -/* * Make a copy of a class. */ void @@ -2866,7 +2780,7 @@ static object_T *next_nonref_obj = NULL; * Call this function when an object has been cleared and is about to be freed. * It is removed from the list headed by "first_object". */ - void + static void object_cleared(object_T *obj) { if (obj->obj_next_used != NULL) @@ -2882,6 +2796,42 @@ object_cleared(object_T *obj) } /* + * Free an object. + */ + static void +object_clear(object_T *obj) +{ + // Avoid a recursive call, it can happen if "obj" has a circular reference. + obj->obj_refcount = INT_MAX; + + class_T *cl = obj->obj_class; + + if (!cl) + return; + + // the member values are just after the object structure + typval_T *tv = (typval_T *)(obj + 1); + for (int i = 0; i < cl->class_obj_member_count; ++i) + clear_tv(tv + i); + + // Remove from the list headed by "first_object". + object_cleared(obj); + + vim_free(obj); + class_unref(cl); +} + +/* + * Unreference an object. + */ + void +object_unref(object_T *obj) +{ + if (obj != NULL && --obj->obj_refcount <= 0) + object_clear(obj); +} + +/* * Go through the list of all objects and free items without "copyID". */ int diff --git a/src/vim9cmds.c b/src/vim9cmds.c --- a/src/vim9cmds.c +++ b/src/vim9cmds.c @@ -254,7 +254,7 @@ compile_lock_unlock( { // Push the class of the bare class variable name name = cl->class_name; - len = STRLEN(name); + len = (int)STRLEN(name); #ifdef LOG_LOCKVAR ch_log(NULL, "LKVAR: ... cctx_class_member: name %s", name); diff --git a/src/vim9compile.c b/src/vim9compile.c --- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -2011,16 +2011,33 @@ compile_lhs( // for an object or class member get the type of the member class_T *cl = lhs->lhs_type->tt_class; int is_object = lhs->lhs_type->tt_type == VAR_OBJECT; - - if (!lhs_class_member_modifiable(lhs, var_start, cctx)) + char_u *name = var_start + lhs->lhs_varlen + 1; + size_t namelen = lhs->lhs_end - var_start - lhs->lhs_varlen - 1; + + ocmember_T *m = member_lookup(cl, lhs->lhs_type->tt_type, + name, namelen, &lhs->lhs_member_idx); + if (m == NULL) + { + member_not_found_msg(cl, lhs->lhs_type->tt_type, name, namelen); return FAIL; - - lhs->lhs_member_type = class_member_type(cl, - is_object, - after + 1, lhs->lhs_end, - &lhs->lhs_member_idx); - if (lhs->lhs_member_idx < 0) + } + + // If it is private member variable, then accessing it outside the + // class is not allowed. + // If it is a read only class variable, then it can be modified + // only inside the class where it is defined. + if ((m->ocm_access != VIM_ACCESS_ALL) && + ((is_object && !inside_class(cctx, cl)) + || (!is_object && cctx->ctx_ufunc->uf_class != cl))) + { + char *msg = (m->ocm_access == VIM_ACCESS_PRIVATE) + ? e_cannot_access_private_variable_str + : e_variable_is_not_writable_str; + emsg_var_cl_define(msg, m->ocm_name, 0, cl); return FAIL; + } + + lhs->lhs_member_type = m->ocm_type; } else {