# HG changeset patch # User Bram Moolenaar # Date 1671051604 -3600 # Node ID 5ef28f5ff35744ecf738eb44fbc7b6ac8f2f3409 # Parent d03a62e7fe0e7a074b238fac9785967007b3294f patch 9.0.1060: private and public object members are not implemented yet Commit: https://github.com/vim/vim/commit/3d473ee1a6aed7cb9eae458bbd8d42dffdc754f9 Author: Bram Moolenaar Date: Wed Dec 14 20:59:32 2022 +0000 patch 9.0.1060: private and public object members are not implemented yet problem: Private and public object members are not implemented yet. Solution: Implement private and public object members. diff --git a/src/errors.h b/src/errors.h --- a/src/errors.h +++ b/src/errors.h @@ -3378,4 +3378,14 @@ EXTERN char e_cannot_get_object_member_t INIT(= N_("E1329: Cannot get object member type from initializer: %s")); EXTERN char e_invalid_type_for_object_member_str[] INIT(= N_("E1330: Invalid type for object member: %s")); -#endif +EXTERN char e_public_must_be_followed_by_this[] + INIT(= N_("E1331: Public must be followed by \"this\"")); +EXTERN char e_public_object_member_name_cannot_start_with_underscore_str[] + INIT(= N_("E1332: Public object member name cannot start with underscore: %s")); +EXTERN char e_cannot_access_private_object_member_str[] + INIT(= N_("E1333: Cannot access private object member: %s")); +EXTERN char e_object_member_not_found_str[] + INIT(= N_("E1334: Object member not found: %s")); +EXTERN char e_object_member_is_not_writable_str[] + INIT(= N_("E1335: Object member is not writable: %s")); +#endif diff --git a/src/eval.c b/src/eval.c --- a/src/eval.c +++ b/src/eval.c @@ -1194,6 +1194,7 @@ get_lval( while (*p == '[' || (*p == '.' && p[1] != '=' && p[1] != '.')) { if (*p == '.' && lp->ll_tv->v_type != VAR_DICT + && lp->ll_tv->v_type != VAR_OBJECT && lp->ll_tv->v_type != VAR_CLASS) { if (!quiet) @@ -1203,6 +1204,7 @@ get_lval( if (lp->ll_tv->v_type != VAR_LIST && lp->ll_tv->v_type != VAR_DICT && lp->ll_tv->v_type != VAR_BLOB + && lp->ll_tv->v_type != VAR_OBJECT && lp->ll_tv->v_type != VAR_CLASS) { if (!quiet) @@ -1509,10 +1511,55 @@ get_lval( lp->ll_tv = &lp->ll_li->li_tv; } - else // v_type == VAR_CLASS - { - // TODO: check object members and methods if - // "key" points name start, "p" to the end + else // v_type == VAR_CLASS || v_type == VAR_OBJECT + { + class_T *cl = (lp->ll_tv->v_type == VAR_OBJECT + && lp->ll_tv->vval.v_object != NULL) + ? lp->ll_tv->vval.v_object->obj_class + : lp->ll_tv->vval.v_class; + // TODO: what if class is NULL? + if (cl != NULL) + { + lp->ll_valtype = NULL; + for (int i = 0; i < cl->class_obj_member_count; ++i) + { + objmember_T *om = cl->class_obj_members + i; + if (STRNCMP(om->om_name, key, p - key) == 0 + && om->om_name[p - key] == NUL) + { + switch (om->om_access) + { + case ACCESS_PRIVATE: + semsg(_(e_cannot_access_private_object_member_str), + om->om_name); + return NULL; + case ACCESS_READ: + if (!(flags & GLV_READ_ONLY)) + { + semsg(_(e_object_member_is_not_writable_str), + om->om_name); + return NULL; + } + break; + case ACCESS_ALL: + break; + } + + lp->ll_valtype = om->om_type; + + if (lp->ll_tv->v_type == VAR_OBJECT) + lp->ll_tv = ((typval_T *)( + lp->ll_tv->vval.v_object + 1)) + i; + // TODO: what about a class? + break; + } + } + if (lp->ll_valtype == NULL) + { + semsg(_(e_object_member_not_found_str), key); + return NULL; + } + } } } @@ -1640,7 +1687,7 @@ set_var_lval( else { /* - * Assign to a List or Dictionary item. + * Assign to a List, Dictionary or Object item. */ if ((flags & (ASSIGN_CONST | ASSIGN_FINAL)) && (flags & ASSIGN_FOR_LOOP) == 0) diff --git a/src/structs.h b/src/structs.h --- a/src/structs.h +++ b/src/structs.h @@ -1459,11 +1459,18 @@ typedef struct { type_T *type_decl; // declared type or equal to type_current } type2_T; +typedef enum { + ACCESS_PRIVATE, // read/write only inside th class + ACCESS_READ, // read everywhere, write only inside th class + ACCESS_ALL // read/write everywhere +} omacc_T; + /* * Entry for an object member variable. */ typedef struct { char_u *om_name; // allocated + omacc_T om_access; type_T *om_type; char_u *om_init; // allocated } objmember_T; @@ -1720,7 +1727,8 @@ struct ufunc_S def_status_T uf_def_status; // UF_NOT_COMPILED, UF_TO_BE_COMPILED, etc. int uf_dfunc_idx; // only valid if uf_def_status is UF_COMPILED - class_T *uf_class; // for object method and constructor + class_T *uf_class; // for object method and constructor; does not + // count for class_refcount garray_T uf_args; // arguments, including optional arguments garray_T uf_def_args; // default argument expressions 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 @@ -283,6 +283,33 @@ def Test_class_object_member_inits() v9.CheckScriptFailure(lines, 'E1330:') enddef +def Test_class_object_member_access() + var lines =<< trim END + vim9script + class Triple + this._one = 1 + this.two = 2 + public this.three = 3 + + def GetOne(): number + return this._one + enddef + endclass + + var trip = Triple.new() + assert_equal(1, trip.GetOne()) + assert_equal(2, trip.two) + assert_equal(3, trip.three) + assert_fails('echo trip._one', 'E1333') + + assert_fails('trip._one = 11', 'E1333') + assert_fails('trip.two = 22', 'E1335') + trip.three = 33 + assert_equal(33, trip.three) + END + v9.CheckScriptSuccess(lines) +enddef + def Test_class_object_to_string() var lines =<< trim END vim9script diff --git a/src/version.c b/src/version.c --- 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 */ /**/ + 1060, +/**/ 1059, /**/ 1058, diff --git a/src/vim9class.c b/src/vim9class.c --- a/src/vim9class.c +++ b/src/vim9class.c @@ -110,19 +110,40 @@ ex_class(exarg_T *eap) break; } + // "this._varname" // "this.varname" - // "this._varname" - // TODO: - // "public this.varname" - if (STRNCMP(line, "this", 4) == 0) + // "public this.varname" + int has_public = FALSE; + if (checkforcmd(&p, "public", 3)) { - if (line[4] != '.' || !eval_isnamec1(line[5])) + if (STRNCMP(line, "public", 6) != 0) { - semsg(_(e_invalid_object_member_declaration_str), line); + semsg(_(e_command_cannot_be_shortened_str), line); break; } - char_u *varname = line + 5; + has_public = TRUE; + p = skipwhite(line + 6); + + if (STRNCMP(p, "this", 4) != 0) + { + emsg(_(e_public_must_be_followed_by_this)); + break; + } + } + if (STRNCMP(p, "this", 4) == 0) + { + if (p[4] != '.' || !eval_isnamec1(p[5])) + { + semsg(_(e_invalid_object_member_declaration_str), p); + break; + } + char_u *varname = p + 5; char_u *varname_end = to_name_end(varname, FALSE); + if (*varname == '_' && has_public) + { + semsg(_(e_public_object_member_name_cannot_start_with_underscore_str), line); + break; + } char_u *colon = skipwhite(varname_end); char_u *type_arg = colon; @@ -199,6 +220,9 @@ ex_class(exarg_T *eap) objmember_T *m = ((objmember_T *)objmembers.ga_data) + objmembers.ga_len; m->om_name = vim_strnsave(varname, varname_end - varname); + m->om_access = has_public ? ACCESS_ALL + : *varname == '_' ? ACCESS_PRIVATE + : ACCESS_READ; m->om_type = type; if (expr_end > expr_start) m->om_init = vim_strnsave(expr_start, expr_end - expr_start); @@ -551,6 +575,13 @@ class_object_index( objmember_T *m = &cl->class_obj_members[i]; if (STRNCMP(name, m->om_name, len) == 0 && m->om_name[len] == NUL) { + if (*name == '_') + { + semsg(_(e_cannot_access_private_object_member_str), + m->om_name); + 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; diff --git a/src/vim9expr.c b/src/vim9expr.c --- a/src/vim9expr.c +++ b/src/vim9expr.c @@ -281,6 +281,13 @@ compile_class_object_index(cctx_T *cctx, objmember_T *m = &cl->class_obj_members[i]; if (STRNCMP(name, m->om_name, len) == 0 && m->om_name[len] == NUL) { + if (*name == '_' && cctx->ctx_ufunc->uf_class != cl) + { + semsg(_(e_cannot_access_private_object_member_str), + m->om_name); + return FAIL; + } + generate_GET_OBJ_MEMBER(cctx, i, m->om_type); *arg = name_end;