Patch 9.0.1074
Problem: Class members are not supported yet.
Solution: Add initial support for class members.
Files: src/errors.h, src/eval.c, src/structs.h, src/vim9.h,
src/vim9class.c, src/vim9compile.c, src/vim9expr.c,
src/vim9instr.c, src/proto/vim9instr.pro,
src/vim9cmds.c, src/vim9execute.c, src/testdir/test_vim9_class.vim
*** ../vim-9.0.1073/src/errors.h 2022-12-16 16:41:19.204714805 +0000
--- src/errors.h 2022-12-18 18:30:04.387224250 +0000
***************
*** 3378,3393 ****
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"));
! 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
EXTERN char e_internal_error_shortmess_too_long[]
INIT(= N_("E1336: Internal error: shortmess too long"));
--- 3378,3399 ----
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"));
! EXTERN char e_public_must_be_followed_by_this_or_static[]
! INIT(= N_("E1331: Public must be followed by \"this\" or \"static\""));
! EXTERN char e_public_member_name_cannot_start_with_underscore_str[]
! INIT(= N_("E1332: Public member name cannot start with underscore:
%s"));
! EXTERN char e_cannot_access_private_member_str[]
! INIT(= N_("E1333: Cannot access private member: %s"));
EXTERN char e_object_member_not_found_str[]
INIT(= N_("E1334: Object member not found: %s"));
! EXTERN char e_member_is_not_writable_str[]
! INIT(= N_("E1335: Member is not writable: %s"));
#endif
EXTERN char e_internal_error_shortmess_too_long[]
INIT(= N_("E1336: Internal error: shortmess too long"));
+ #ifdef FEAT_EVAL
+ EXTERN char e_class_member_not_found_str[]
+ INIT(= N_("E1337: Class member not found: %s"));
+ EXTERN char e_member_not_found_on_class_str_str[]
+ INIT(= N_("E1338: Member not found on class \"%s\": %s"));
+ #endif
*** ../vim-9.0.1073/src/eval.c 2022-12-14 20:54:52.411699476 +0000
--- src/eval.c 2022-12-18 18:23:43.039785841 +0000
***************
*** 1193,1211 ****
var2.v_type = VAR_UNKNOWN;
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)
semsg(_(e_dot_can_only_be_used_on_dictionary_str), name);
return NULL;
}
! 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)
emsg(_(e_can_only_index_list_dictionary_or_blob));
--- 1193,1213 ----
var2.v_type = VAR_UNKNOWN;
while (*p == '[' || (*p == '.' && p[1] != '=' && p[1] != '.'))
{
! vartype_T v_type = lp->ll_tv->v_type;
!
! if (*p == '.' && v_type != VAR_DICT
! && v_type != VAR_OBJECT
! && v_type != VAR_CLASS)
{
if (!quiet)
semsg(_(e_dot_can_only_be_used_on_dictionary_str), name);
return NULL;
}
! if (v_type != VAR_LIST
! && v_type != VAR_DICT
! && v_type != VAR_BLOB
! && v_type != VAR_OBJECT
! && v_type != VAR_CLASS)
{
if (!quiet)
emsg(_(e_can_only_index_list_dictionary_or_blob));
***************
*** 1214,1222 ****
// A NULL list/blob works like an empty list/blob, allocate one now.
int r = OK;
! if (lp->ll_tv->v_type == VAR_LIST && lp->ll_tv->vval.v_list == NULL)
r = rettv_list_alloc(lp->ll_tv);
! else if (lp->ll_tv->v_type == VAR_BLOB
&& lp->ll_tv->vval.v_blob == NULL)
r = rettv_blob_alloc(lp->ll_tv);
if (r == FAIL)
--- 1216,1224 ----
// A NULL list/blob works like an empty list/blob, allocate one now.
int r = OK;
! if (v_type == VAR_LIST && lp->ll_tv->vval.v_list == NULL)
r = rettv_list_alloc(lp->ll_tv);
! else if (v_type == VAR_BLOB
&& lp->ll_tv->vval.v_blob == NULL)
r = rettv_blob_alloc(lp->ll_tv);
if (r == FAIL)
***************
*** 1278,1284 ****
// Optionally get the second index [ :expr].
if (*p == ':')
{
! if (lp->ll_tv->v_type == VAR_DICT)
{
if (!quiet)
emsg(_(e_cannot_slice_dictionary));
--- 1280,1286 ----
// Optionally get the second index [ :expr].
if (*p == ':')
{
! if (v_type == VAR_DICT)
{
if (!quiet)
emsg(_(e_cannot_slice_dictionary));
***************
*** 1334,1340 ****
++p;
}
! if (lp->ll_tv->v_type == VAR_DICT)
{
if (len == -1)
{
--- 1336,1342 ----
++p;
}
! if (v_type == VAR_DICT)
{
if (len == -1)
{
***************
*** 1435,1441 ****
clear_tv(&var1);
lp->ll_tv = &lp->ll_di->di_tv;
}
! else if (lp->ll_tv->v_type == VAR_BLOB)
{
long bloblen = blob_len(lp->ll_tv->vval.v_blob);
--- 1437,1443 ----
clear_tv(&var1);
lp->ll_tv = &lp->ll_di->di_tv;
}
! else if (v_type == VAR_BLOB)
{
long bloblen = blob_len(lp->ll_tv->vval.v_blob);
***************
*** 1466,1472 ****
lp->ll_tv = NULL;
break;
}
! else if (lp->ll_tv->v_type == VAR_LIST)
{
/*
* Get the number and item for the only or first index of the List.
--- 1468,1474 ----
lp->ll_tv = NULL;
break;
}
! else if (v_type == VAR_LIST)
{
/*
* Get the number and item for the only or first index of the List.
***************
*** 1513,1519 ****
}
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;
--- 1515,1521 ----
}
else // v_type == VAR_CLASS || v_type == VAR_OBJECT
{
! class_T *cl = (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;
***************
*** 1521,1543 ****
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;
--- 1523,1550 ----
if (cl != NULL)
{
lp->ll_valtype = NULL;
! int count = v_type == VAR_OBJECT ? cl->class_obj_member_count
! : cl->class_class_member_count;
! ocmember_T *members = v_type == VAR_OBJECT
! ? cl->class_obj_members
! : cl->class_class_members;
! for (int i = 0; i < count; ++i)
! {
! ocmember_T *om = members + i;
! if (STRNCMP(om->ocm_name, key, p - key) == 0
! && om->ocm_name[p - key] == NUL)
{
! switch (om->ocm_access)
{
case ACCESS_PRIVATE:
! semsg(_(e_cannot_access_private_member_str),
! om->ocm_name);
return NULL;
case ACCESS_READ:
if (!(flags & GLV_READ_ONLY))
{
! semsg(_(e_member_is_not_writable_str),
! om->ocm_name);
return NULL;
}
break;
***************
*** 1545,1562 ****
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;
}
}
--- 1552,1573 ----
break;
}
! lp->ll_valtype = om->ocm_type;
! if (v_type == VAR_OBJECT)
lp->ll_tv = ((typval_T *)(
lp->ll_tv->vval.v_object + 1)) + i;
! else
! lp->ll_tv = &cl->class_members_tv[i];
break;
}
}
if (lp->ll_valtype == NULL)
{
! if (v_type == VAR_OBJECT)
! semsg(_(e_object_member_not_found_str), key);
! else
! semsg(_(e_class_member_not_found_str), key);
return NULL;
}
}
***************
*** 5936,5943 ****
{
if (i > 0)
ga_concat(&ga, (char_u *)", ");
! objmember_T *m = &cl->class_obj_members[i];
! ga_concat(&ga, m->om_name);
ga_concat(&ga, (char_u *)": ");
char_u *tf = NULL;
ga_concat(&ga, echo_string_core(
--- 5947,5954 ----
{
if (i > 0)
ga_concat(&ga, (char_u *)", ");
! ocmember_T *m = &cl->class_obj_members[i];
! ga_concat(&ga, m->ocm_name);
ga_concat(&ga, (char_u *)": ");
char_u *tf = NULL;
ga_concat(&ga, echo_string_core(
*** ../vim-9.0.1073/src/structs.h 2022-12-14 20:54:52.411699476 +0000
--- src/structs.h 2022-12-18 21:15:20.235962840 +0000
***************
*** 1387,1392 ****
--- 1387,1393 ----
typedef double float_T;
+ typedef struct typval_S typval_T;
typedef struct listvar_S list_T;
typedef struct dictvar_S dict_T;
typedef struct partial_S partial_T;
***************
*** 1466,1479 ****
} 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;
// "class_T": used for v_class of typval of VAR_CLASS
struct class_S
--- 1467,1480 ----
} omacc_T;
/*
! * Entry for an object or class member variable.
*/
typedef struct {
! char_u *ocm_name; // allocated
! omacc_T ocm_access;
! type_T *ocm_type;
! char_u *ocm_init; // allocated
! } ocmember_T;
// "class_T": used for v_class of typval of VAR_CLASS
struct class_S
***************
*** 1481,1494 ****
char_u *class_name; // allocated
int class_refcount;
int class_obj_member_count;
! objmember_T *class_obj_members; // allocated
int class_obj_method_count;
ufunc_T **class_obj_methods; // allocated
garray_T class_type_list; // used for type pointers
! type_T class_type;
type_T class_object_type; // same as class_type but VAR_OBJECT
};
--- 1482,1506 ----
char_u *class_name; // allocated
int class_refcount;
+ // class members: "static varname"
+ int class_class_member_count;
+ ocmember_T *class_class_members; // allocated
+ typval_T *class_members_tv; // allocated array of class member vals
+
+ // class methods: "static def SomeMethod()"
+ int class_class_method_count;
+ ufunc_T **class_class_methods; // allocated
+
+ // object members: "this.varname"
int class_obj_member_count;
! ocmember_T *class_obj_members; // allocated
+ // object methods: "def SomeMethod()"
int class_obj_method_count;
ufunc_T **class_obj_methods; // allocated
garray_T class_type_list; // used for type pointers
! type_T class_type; // type used for the class
type_T class_object_type; // same as class_type but VAR_OBJECT
};
***************
*** 1513,1519 ****
/*
* Structure to hold an internal variable without a name.
*/
! typedef struct
{
vartype_T v_type;
char v_lock; // see below: VAR_LOCKED, VAR_FIXED
--- 1525,1531 ----
/*
* Structure to hold an internal variable without a name.
*/
! struct typval_S
{
vartype_T v_type;
char v_lock; // see below: VAR_LOCKED, VAR_FIXED
***************
*** 1534,1540 ****
class_T *v_class; // class value (can be NULL)
object_T *v_object; // object value (can be NULL)
} vval;
! } typval_T;
// Values for "dv_scope".
#define VAR_SCOPE 1 // a:, v:, s:, etc. scope dictionaries
--- 1546,1552 ----
class_T *v_class; // class value (can be NULL)
object_T *v_object; // object value (can be NULL)
} vval;
! };
// Values for "dv_scope".
#define VAR_SCOPE 1 // a:, v:, s:, etc. scope dictionaries
*** ../vim-9.0.1073/src/vim9.h 2022-12-13 18:42:19.749879633 +0000
--- src/vim9.h 2022-12-18 21:30:58.774980527 +0000
***************
*** 36,41 ****
--- 36,43 ----
ISN_GET_OBJ_MEMBER, // object member, index is isn_arg.number
ISN_STORE_THIS, // store value in "this" object member, index is
// isn_arg.number
+ ISN_LOAD_CLASSMEMBER, // load class member, using classmember_T
+ ISN_STORE_CLASSMEMBER, // store in class member, using classmember_T
// get and set variables
ISN_LOAD, // push local variable isn_arg.number
***************
*** 476,481 ****
--- 478,489 ----
class_T *construct_class; // class the object is created from
} construct_T;
+ // arguments to ISN_STORE_CLASSMEMBER and ISN_LOAD_CLASSMEMBER
+ typedef struct {
+ class_T *cm_class;
+ int cm_idx;
+ } classmember_T;
+
/*
* Instruction
*/
***************
*** 528,533 ****
--- 536,542 ----
deferins_T defer;
echowin_T echowin;
construct_T construct;
+ classmember_T classmember;
} isn_arg;
};
***************
*** 538,544 ****
ufunc_T *df_ufunc; // struct containing most stuff
int df_refcount; // how many ufunc_T point to this
dfunc_T
int df_idx; // index in def_functions
! int df_deleted; // if TRUE function was deleted
int df_script_seq; // Value of sctx_T sc_seq when the
function
// was compiled.
char_u *df_name; // name used for error messages
--- 547,555 ----
ufunc_T *df_ufunc; // struct containing most stuff
int df_refcount; // how many ufunc_T point to this
dfunc_T
int df_idx; // index in def_functions
! char df_deleted; // if TRUE function was deleted
! char df_delete_busy; // TRUE when in
! // delete_def_function_contents()
int df_script_seq; // Value of sctx_T sc_seq when the
function
// was compiled.
char_u *df_name; // name used for error messages
***************
*** 735,740 ****
--- 746,752 ----
dest_window,
dest_tab,
dest_vimvar,
+ dest_class_member,
dest_script,
dest_reg,
dest_expr,
***************
*** 766,771 ****
--- 778,787 ----
lvar_T lhs_local_lvar; // used for existing local destination
lvar_T lhs_arg_lvar; // used for argument destination
lvar_T *lhs_lvar; // points to destination lvar
+
+ class_T *lhs_class; // for dest_class_member
+ int lhs_classmember_idx; // for dest_class_member
+
int lhs_scriptvar_sid;
int lhs_scriptvar_idx;
*** ../vim-9.0.1073/src/vim9class.c 2022-12-14 20:54:52.407699483 +0000
--- src/vim9class.c 2022-12-18 21:19:06.883718087 +0000
***************
*** 22,27 ****
--- 22,175 ----
#endif
/*
+ * Parse a member declaration, both object and class member.
+ * Returns OK or FAIL. When OK then "varname_end" is set to just after the
+ * variable name and "type_ret" is set to the decleared or detected type.
+ * "init_expr" is set to the initialisation expression (allocated), if there
is
+ * one.
+ */
+ static int
+ parse_member(
+ exarg_T *eap,
+ char_u *line,
+ char_u *varname,
+ int has_public, // TRUE if "public" seen before "varname"
+ char_u **varname_end,
+ garray_T *type_list,
+ type_T **type_ret,
+ char_u **init_expr)
+ {
+ *varname_end = to_name_end(varname, FALSE);
+ if (*varname == '_' && has_public)
+ {
+ semsg(_(e_public_member_name_cannot_start_with_underscore_str), line);
+ return FAIL;
+ }
+
+ char_u *colon = skipwhite(*varname_end);
+ char_u *type_arg = colon;
+ type_T *type = NULL;
+ if (*colon == ':')
+ {
+ if (VIM_ISWHITE(**varname_end))
+ {
+ semsg(_(e_no_white_space_allowed_before_colon_str), varname);
+ return FAIL;
+ }
+ if (!VIM_ISWHITE(colon[1]))
+ {
+ semsg(_(e_white_space_required_after_str_str), ":", varname);
+ return FAIL;
+ }
+ type_arg = skipwhite(colon + 1);
+ type = parse_type(&type_arg, type_list, TRUE);
+ if (type == NULL)
+ return FAIL;
+ }
+
+ char_u *expr_start = skipwhite(type_arg);
+ char_u *expr_end = expr_start;
+ if (type == NULL && *expr_start != '=')
+ {
+ emsg(_(e_type_or_initialization_required));
+ return FAIL;
+ }
+
+ if (*expr_start == '=')
+ {
+ if (!VIM_ISWHITE(expr_start[-1]) || !VIM_ISWHITE(expr_start[1]))
+ {
+ semsg(_(e_white_space_required_before_and_after_str_at_str),
+ "=", type_arg);
+ return FAIL;
+ }
+ expr_start = skipwhite(expr_start + 1);
+
+ expr_end = expr_start;
+ evalarg_T evalarg;
+ fill_evalarg_from_eap(&evalarg, eap, FALSE);
+ skip_expr(&expr_end, NULL);
+
+ if (type == NULL)
+ {
+ // No type specified, use the type of the initializer.
+ typval_T tv;
+ tv.v_type = VAR_UNKNOWN;
+ char_u *expr = expr_start;
+ int res = eval0(expr, &tv, eap, &evalarg);
+
+ if (res == OK)
+ type = typval2type(&tv, get_copyID(), type_list,
+ TVTT_DO_MEMBER);
+ if (type == NULL)
+ {
+ semsg(_(e_cannot_get_object_member_type_from_initializer_str),
+ expr_start);
+ clear_evalarg(&evalarg, NULL);
+ return FAIL;
+ }
+ }
+ clear_evalarg(&evalarg, NULL);
+ }
+ if (!valid_declaration_type(type))
+ return FAIL;
+
+ *type_ret = type;
+ if (expr_end > expr_start)
+ *init_expr = vim_strnsave(expr_start, expr_end - expr_start);
+ return OK;
+ }
+
+ /*
+ * Add a member to an object or a class.
+ * Returns OK when successful, "init_expr" will be consumed then.
+ * Returns FAIL otherwise, caller might need to free "init_expr".
+ */
+ static int
+ add_member(
+ garray_T *gap,
+ char_u *varname,
+ char_u *varname_end,
+ int has_public,
+ type_T *type,
+ char_u *init_expr)
+ {
+ if (ga_grow(gap, 1) == FAIL)
+ return FAIL;
+ ocmember_T *m = ((ocmember_T *)gap->ga_data) + gap->ga_len;
+ m->ocm_name = vim_strnsave(varname, varname_end - varname);
+ m->ocm_access = has_public ? ACCESS_ALL
+ : *varname == '_' ? ACCESS_PRIVATE : ACCESS_READ;
+ m->ocm_type = type;
+ if (init_expr != NULL)
+ m->ocm_init = init_expr;
+ ++gap->ga_len;
+ return OK;
+ }
+
+ /*
+ * Move the class or object members found while parsing a class into the
class.
+ * "gap" contains the found members.
+ * "members" will be set to the newly allocated array of members and
+ * "member_count" set to the number of members.
+ * Returns OK or FAIL.
+ */
+ static int
+ add_members_to_class(
+ garray_T *gap,
+ ocmember_T **members,
+ int *member_count)
+ {
+ *member_count = gap->ga_len;
+ *members = gap->ga_len == 0 ? NULL : ALLOC_MULT(ocmember_T, gap->ga_len);
+ if (gap->ga_len > 0 && *members == NULL)
+ return FAIL;
+ mch_memmove(*members, gap->ga_data, sizeof(ocmember_T) * gap->ga_len);
+ VIM_CLEAR(gap->ga_data);
+ return OK;
+ }
+
+ /*
* Handle ":class" and ":abstract class" up to ":endclass".
*/
void
***************
*** 64,79 ****
// extends SomeClass
// implements SomeInterface
// specifies SomeInterface
! // check nothing follows
!
! // TODO: handle "is_export" if it is set
garray_T type_list; // list of pointers to allocated types
ga_init2(&type_list, sizeof(type_T *), 10);
// Growarray with object members declared in the class.
garray_T objmembers;
! ga_init2(&objmembers, sizeof(objmember_T), 10);
// Growarray with object methods declared in the class.
garray_T objmethods;
--- 212,234 ----
// extends SomeClass
// implements SomeInterface
// specifies SomeInterface
! // check that nothing follows
! // handle "is_export" if it is set
garray_T type_list; // list of pointers to allocated types
ga_init2(&type_list, sizeof(type_T *), 10);
+ // Growarray with class members declared in the class.
+ garray_T classmembers;
+ ga_init2(&classmembers, sizeof(ocmember_T), 10);
+
+ // Growarray with object methods declared in the class.
+ garray_T classmethods;
+ ga_init2(&classmethods, sizeof(ufunc_T *), 10);
+
// Growarray with object members declared in the class.
garray_T objmembers;
! ga_init2(&objmembers, sizeof(ocmember_T), 10);
// Growarray with object methods declared in the class.
garray_T objmethods;
***************
*** 92,103 ****
break;
char_u *line = skipwhite(theline);
- // TODO:
- // class members (public, read access, private):
- // static varname
- // public static varname
- // static _varname
-
char_u *p = line;
if (checkforcmd(&p, "endclass", 4))
{
--- 247,252 ----
***************
*** 110,118 ****
break;
}
- // "this._varname"
- // "this.varname"
- // "public this.varname"
int has_public = FALSE;
if (checkforcmd(&p, "public", 3))
{
--- 259,264 ----
***************
*** 124,135 ****
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]))
--- 270,286 ----
has_public = TRUE;
p = skipwhite(line + 6);
! if (STRNCMP(p, "this", 4) != 0 && STRNCMP(p, "static", 6) != 0)
{
! emsg(_(e_public_must_be_followed_by_this_or_static));
break;
}
}
+
+ // object members (public, read access, private):
+ // "this._varname"
+ // "this.varname"
+ // "public this.varname"
if (STRNCMP(p, "this", 4) == 0)
{
if (p[4] != '.' || !eval_isnamec1(p[5]))
***************
*** 138,232 ****
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;
type_T *type = NULL;
! if (*colon == ':')
{
! if (VIM_ISWHITE(*varname_end))
! {
! semsg(_(e_no_white_space_allowed_before_colon_str),
! varname);
! break;
! }
! if (!VIM_ISWHITE(colon[1]))
! {
! semsg(_(e_white_space_required_after_str_str), ":",
! varname);
! break;
! }
! type_arg = skipwhite(colon + 1);
! type = parse_type(&type_arg, &type_list, TRUE);
! if (type == NULL)
! break;
}
! char_u *expr_start = skipwhite(type_arg);
! char_u *expr_end = expr_start;
! if (type == NULL && *expr_start != '=')
{
! emsg(_(e_type_or_initialization_required));
! break;
}
!
! if (*expr_start == '=')
{
! if (!VIM_ISWHITE(expr_start[-1]) || !VIM_ISWHITE(expr_start[1]))
! {
! semsg(_(e_white_space_required_before_and_after_str_at_str),
! "=", type_arg);
break;
! }
! expr_start = skipwhite(expr_start + 1);
!
! expr_end = expr_start;
! evalarg_T evalarg;
! fill_evalarg_from_eap(&evalarg, eap, FALSE);
! skip_expr(&expr_end, NULL);
!
! if (type == NULL)
{
! // No type specified, use the type of the initializer.
! typval_T tv;
! tv.v_type = VAR_UNKNOWN;
! char_u *expr = expr_start;
! int res = eval0(expr, &tv, eap, &evalarg);
!
! if (res == OK)
! type = typval2type(&tv, get_copyID(), &type_list,
! TVTT_DO_MEMBER);
! if (type == NULL)
! {
!
semsg(_(e_cannot_get_object_member_type_from_initializer_str),
! expr_start);
! clear_evalarg(&evalarg, NULL);
! break;
! }
}
- clear_evalarg(&evalarg, NULL);
}
- if (!valid_declaration_type(type))
- break;
-
- if (ga_grow(&objmembers, 1) == FAIL)
- break;
- 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);
- ++objmembers.ga_len;
}
// constructors:
--- 289,340 ----
break;
}
char_u *varname = p + 5;
! char_u *varname_end = NULL;
type_T *type = NULL;
! char_u *init_expr = NULL;
! if (parse_member(eap, line, varname, has_public,
! &varname_end, &type_list, &type, &init_expr) == FAIL)
! break;
! if (add_member(&objmembers, varname, varname_end,
! has_public, type, init_expr) == FAIL)
{
! vim_free(init_expr);
! break;
}
+ }
! // class members and methods
! else if (checkforcmd(&p, "static", 6))
! {
! p = skipwhite(p);
! if (checkforcmd(&p, "def", 3))
{
! // TODO: class method
! // static def someMethod()
! // enddef
! // static def <Tval> someMethod()
! // enddef
}
! else
{
! // class members (public, read access, private):
! // "static _varname"
! // "static varname"
! // "public static varname"
! char_u *varname = p;
! char_u *varname_end = NULL;
! type_T *type = NULL;
! char_u *init_expr = NULL;
! if (parse_member(eap, line, varname, has_public,
! &varname_end, &type_list, &type, &init_expr) == FAIL)
break;
! if (add_member(&classmembers, varname, varname_end,
! has_public, type, init_expr) == FAIL)
{
! vim_free(init_expr);
! break;
}
}
}
// constructors:
***************
*** 238,249 ****
// def someMethod()
// enddef
// TODO:
- // static def someMethod()
- // enddef
// def <Tval> someMethod()
// enddef
- // static def <Tval> someMethod()
- // enddef
else if (checkforcmd(&p, "def", 3))
{
exarg_T ea;
--- 346,353 ----
***************
*** 282,303 ****
class_T *cl = NULL;
if (success)
{
cl = ALLOC_CLEAR_ONE(class_T);
if (cl == NULL)
goto cleanup;
cl->class_refcount = 1;
cl->class_name = vim_strnsave(arg, name_end - arg);
! // Members are used by the new() function, add them here.
! cl->class_obj_member_count = objmembers.ga_len;
! cl->class_obj_members = objmembers.ga_len == 0 ? NULL
! : ALLOC_MULT(objmember_T, objmembers.ga_len);
! if (cl->class_name == NULL
! || (objmembers.ga_len > 0 && cl->class_obj_members == NULL))
goto cleanup;
! mch_memmove(cl->class_obj_members, objmembers.ga_data,
! sizeof(objmember_T) * objmembers.ga_len);
! vim_free(objmembers.ga_data);
int have_new = FALSE;
for (int i = 0; i < objmethods.ga_len; ++i)
--- 386,437 ----
class_T *cl = NULL;
if (success)
{
+ // "endclass" encountered without failures: Create the class.
+
cl = ALLOC_CLEAR_ONE(class_T);
if (cl == NULL)
goto cleanup;
cl->class_refcount = 1;
cl->class_name = vim_strnsave(arg, name_end - arg);
+ if (cl->class_name == NULL)
+ goto cleanup;
! // Add class and object members to "cl".
! if (add_members_to_class(&classmembers,
! &cl->class_class_members,
! &cl->class_class_member_count) == FAIL
! || add_members_to_class(&objmembers,
! &cl->class_obj_members,
! &cl->class_obj_member_count) == FAIL)
goto cleanup;
!
! if (cl->class_class_member_count > 0)
! {
! // Allocate a typval for each class member and initialize it.
! cl->class_members_tv = ALLOC_CLEAR_MULT(typval_T,
! cl->class_class_member_count);
! if (cl->class_members_tv != NULL)
! for (int i = 0; i < cl->class_class_member_count; ++i)
! {
! ocmember_T *m = &cl->class_class_members[i];
! typval_T *tv = &cl->class_members_tv[i];
! if (m->ocm_init != NULL)
! {
! typval_T *etv = eval_expr(m->ocm_init, eap);
! if (etv != NULL)
! {
! *tv = *etv;
! vim_free(etv);
! }
! }
! else
! {
! // TODO: proper default value
! tv->v_type = m->ocm_type->tt_type;
! tv->vval.v_string = NULL;
! }
! }
! }
int have_new = FALSE;
for (int i = 0; i < objmethods.ga_len; ++i)
***************
*** 318,325 ****
if (i > 0)
ga_concat(&fga, (char_u *)", ");
ga_concat(&fga, (char_u *)"this.");
! objmember_T *m = cl->class_obj_members + i;
! ga_concat(&fga, (char_u *)m->om_name);
ga_concat(&fga, (char_u *)" = v:none");
}
ga_concat(&fga, (char_u *)")\nenddef\n");
--- 452,459 ----
if (i > 0)
ga_concat(&fga, (char_u *)", ");
ga_concat(&fga, (char_u *)"this.");
! ocmember_T *m = cl->class_obj_members + i;
! ga_concat(&fga, (char_u *)m->ocm_name);
ga_concat(&fga, (char_u *)" = v:none");
}
ga_concat(&fga, (char_u *)")\nenddef\n");
***************
*** 355,360 ****
--- 489,495 ----
}
}
+ // TODO: class methods
cl->class_obj_method_count = objmethods.ga_len;
cl->class_obj_methods = ALLOC_MULT(ufunc_T *, objmethods.ga_len);
if (cl->class_obj_methods == NULL)
***************
*** 378,390 ****
cl->class_type_list = type_list;
// TODO:
! // - Add the methods to the class
! // - array with ufunc_T pointers
! // - Fill hashtab with object members and methods
! // - Generate the default new() method, if needed.
! // Later:
! // - class members
! // - class methods
// Add the class to the script-local variables.
typval_T tv;
--- 513,519 ----
cl->class_type_list = type_list;
// TODO:
! // - Fill hashtab with object members and methods ?
// Add the class to the script-local variables.
typval_T tv;
***************
*** 404,416 ****
vim_free(cl);
}
! for (int i = 0; i < objmembers.ga_len; ++i)
{
! objmember_T *m = ((objmember_T *)objmembers.ga_data) + i;
! vim_free(m->om_name);
! vim_free(m->om_init);
}
- ga_clear(&objmembers);
for (int i = 0; i < objmethods.ga_len; ++i)
{
--- 533,552 ----
vim_free(cl);
}
! for (int round = 1; round <= 2; ++round)
{
! garray_T *gap = round == 1 ? &classmembers : &objmembers;
! if (gap->ga_len == 0 || gap->ga_data == NULL)
! continue;
!
! for (int i = 0; i < gap->ga_len; ++i)
! {
! ocmember_T *m = ((ocmember_T *)gap->ga_data) + i;
! vim_free(m->ocm_name);
! vim_free(m->ocm_init);
! }
! ga_clear(gap);
}
for (int i = 0; i < objmethods.ga_len; ++i)
{
***************
*** 437,447 ****
for (int i = 0; i < cl->class_obj_member_count; ++i)
{
! objmember_T *m = cl->class_obj_members + i;
! if (STRNCMP(m->om_name, name, len) == 0 && m->om_name[len] == NUL)
{
*member_idx = i;
! return m->om_type;
}
}
return &t_any;
--- 573,583 ----
for (int i = 0; i < cl->class_obj_member_count; ++i)
{
! ocmember_T *m = cl->class_obj_members + i;
! if (STRNCMP(m->ocm_name, name, len) == 0 && m->ocm_name[len] == NUL)
{
*member_idx = i;
! return m->ocm_type;
}
}
return &t_any;
***************
*** 572,584 ****
{
for (int i = 0; i < cl->class_obj_member_count; ++i)
{
! 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;
}
--- 708,719 ----
{
for (int i = 0; i < cl->class_obj_member_count; ++i)
{
! ocmember_T *m = &cl->class_obj_members[i];
! if (STRNCMP(name, m->ocm_name, len) == 0 && m->ocm_name[len] == NUL)
{
if (*name == '_')
{
! semsg(_(e_cannot_access_private_member_str), m->ocm_name);
return FAIL;
}
***************
*** 597,603 ****
semsg(_(e_member_not_found_on_object_str_str), cl->class_name, name);
}
! // TODO: class member
return FAIL;
}
--- 732,762 ----
semsg(_(e_member_not_found_on_object_str_str), cl->class_name, name);
}
! else if (rettv->v_type == VAR_CLASS)
! {
! // class member
! 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 == '_')
! {
! semsg(_(e_cannot_access_private_member_str), m->ocm_name);
! return FAIL;
! }
!
! typval_T *tv = &cl->class_members_tv[i];
! copy_tv(tv, rettv);
! class_unref(cl);
!
! *arg = name_end;
! return OK;
! }
! }
!
! semsg(_(e_member_not_found_on_class_str_str), cl->class_name, name);
! }
return FAIL;
}
***************
*** 708,722 ****
void
class_unref(class_T *cl)
{
! if (cl != NULL && --cl->class_refcount <= 0)
{
! vim_free(cl->class_name);
for (int i = 0; i < cl->class_obj_member_count; ++i)
{
! objmember_T *m = &cl->class_obj_members[i];
! vim_free(m->om_name);
! vim_free(m->om_init);
}
vim_free(cl->class_obj_members);
--- 867,895 ----
void
class_unref(class_T *cl)
{
! if (cl != NULL && --cl->class_refcount <= 0 && cl->class_name != NULL)
{
! // Freeing what the class contains may recursively come back here.
! // Clear "class_name" first, if it is NULL the class does not need to
! // be freed.
! VIM_CLEAR(cl->class_name);
!
! for (int i = 0; i < cl->class_class_member_count; ++i)
! {
! ocmember_T *m = &cl->class_class_members[i];
! vim_free(m->ocm_name);
! vim_free(m->ocm_init);
! if (cl->class_members_tv != NULL)
! clear_tv(&cl->class_members_tv[i]);
! }
! vim_free(cl->class_class_members);
! vim_free(cl->class_members_tv);
for (int i = 0; i < cl->class_obj_member_count; ++i)
{
! ocmember_T *m = &cl->class_obj_members[i];
! vim_free(m->ocm_name);
! vim_free(m->ocm_init);
}
vim_free(cl->class_obj_members);
*** ../vim-9.0.1073/src/vim9compile.c 2022-12-14 13:49:56.214667568 +0000
--- src/vim9compile.c 2022-12-18 21:30:03.843036521 +0000
***************
*** 302,307 ****
--- 302,329 ----
}
/*
+ * If "name" is a class member in cctx->ctx_ufunc->uf_class return the index
in
+ * class.class_class_members[].
+ * Otherwise return -1;
+ */
+ static int
+ class_member_index(char_u *name, size_t len, cctx_T *cctx)
+ {
+ if (cctx == NULL || cctx->ctx_ufunc == NULL
+ || cctx->ctx_ufunc->uf_class == NULL)
+ return -1;
+ class_T *cl = cctx->ctx_ufunc->uf_class;
+ 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)
+ return i;
+ }
+ return -1;
+ }
+
+ /*
* Return TRUE if "name" is a local variable, argument, script variable or
* imported.
*/
***************
*** 316,321 ****
--- 338,344 ----
&& (cctx->ctx_ufunc->uf_flags & FC_OBJECT)
&& STRNCMP(name, "this", 4) == 0)))
|| script_var_exists(name, len, cctx, NULL) == OK
+ || class_member_index(name, len, cctx) >= 0
|| find_imported(name, len, FALSE) != NULL;
}
***************
*** 353,358 ****
--- 376,384 ----
if (len == 1 && *p == '_')
return OK;
+ if (class_member_index(p, len, cctx) >= 0)
+ return OK;
+
if (script_var_exists(p, len, cctx, cstack) == OK)
{
if (is_arg)
***************
*** 1195,1208 ****
* Generate the load instruction for "name".
*/
static void
! generate_loadvar(
! cctx_T *cctx,
! assign_dest_T dest,
! char_u *name,
! lvar_T *lvar,
! type_T *type)
{
! switch (dest)
{
case dest_option:
case dest_func_option:
--- 1221,1232 ----
* Generate the load instruction for "name".
*/
static void
! generate_loadvar(cctx_T *cctx, lhs_T *lhs)
{
! char_u *name = lhs->lhs_name;
! type_T *type = lhs->lhs_type;
!
! switch (lhs->lhs_dest)
{
case dest_option:
case dest_func_option:
***************
*** 1245,1250 ****
--- 1269,1275 ----
case dest_local:
if (cctx->ctx_skip != SKIP_YES)
{
+ lvar_T *lvar = lhs->lhs_lvar;
if (lvar->lv_from_outer > 0)
generate_LOADOUTER(cctx, lvar->lv_idx, lvar->lv_from_outer,
lvar->lv_loop_depth, lvar->lv_loop_idx, type);
***************
*** 1252,1257 ****
--- 1277,1286 ----
generate_LOAD(cctx, ISN_LOAD, lvar->lv_idx, NULL, type);
}
break;
+ case dest_class_member:
+ generate_CLASSMEMBER(cctx, TRUE, lhs->lhs_class,
+ lhs->lhs_classmember_idx);
+ break;
case dest_expr:
// list or dict value should already be on the stack.
break;
***************
*** 1533,1539 ****
--- 1562,1570 ----
if (lookup_local(var_start, lhs->lhs_varlen,
&lhs->lhs_local_lvar, cctx) == OK)
+ {
lhs->lhs_lvar = &lhs->lhs_local_lvar;
+ }
else
{
CLEAR_FIELD(lhs->lhs_arg_lvar);
***************
*** 1549,1554 ****
--- 1580,1586 ----
lhs->lhs_lvar = &lhs->lhs_arg_lvar;
}
}
+
if (lhs->lhs_lvar != NULL)
{
if (is_decl)
***************
*** 1557,1562 ****
--- 1589,1600 ----
return FAIL;
}
}
+ else if ((lhs->lhs_classmember_idx = class_member_index(
+ var_start, lhs->lhs_varlen, cctx)) >= 0)
+ {
+ lhs->lhs_dest = dest_class_member;
+ lhs->lhs_class = cctx->ctx_ufunc->uf_class;
+ }
else
{
int script_namespace = lhs->lhs_varlen > 1
***************
*** 1965,1972 ****
return FAIL;
}
else
! generate_loadvar(cctx, lhs->lhs_dest, lhs->lhs_name,
! lhs->lhs_lvar, lhs->lhs_type);
return OK;
}
--- 2003,2009 ----
return FAIL;
}
else
! generate_loadvar(cctx, lhs);
return OK;
}
***************
*** 2998,3017 ****
for (int i = 0; i < ufunc->uf_class->class_obj_member_count; ++i)
{
! objmember_T *m = &ufunc->uf_class->class_obj_members[i];
! if (m->om_init != NULL)
{
! char_u *expr = m->om_init;
if (compile_expr0(&expr, &cctx) == FAIL)
goto erret;
! if (!ends_excmd2(m->om_init, expr))
{
semsg(_(e_trailing_characters_str), expr);
goto erret;
}
}
else
! push_default_value(&cctx, m->om_type->tt_type,
FALSE, NULL);
generate_STORE_THIS(&cctx, i);
}
--- 3035,3054 ----
for (int i = 0; i < ufunc->uf_class->class_obj_member_count; ++i)
{
! ocmember_T *m = &ufunc->uf_class->class_obj_members[i];
! if (m->ocm_init != NULL)
{
! char_u *expr = m->ocm_init;
if (compile_expr0(&expr, &cctx) == FAIL)
goto erret;
! if (!ends_excmd2(m->ocm_init, expr))
{
semsg(_(e_trailing_characters_str), expr);
goto erret;
}
}
else
! push_default_value(&cctx, m->ocm_type->tt_type,
FALSE, NULL);
generate_STORE_THIS(&cctx, i);
}
***************
*** 3792,3797 ****
--- 3829,3841 ----
{
int idx;
+ // In same cases the instructions may refer to a class in which the
+ // function is defined and unreferencing the class may call back here
+ // recursively. Set the df_delete_busy to avoid problems.
+ if (dfunc->df_delete_busy)
+ return;
+ dfunc->df_delete_busy = TRUE;
+
ga_clear(&dfunc->df_def_args_isn);
ga_clear_strings(&dfunc->df_var_names);
***************
*** 3800,3813 ****
for (idx = 0; idx < dfunc->df_instr_count; ++idx)
delete_instr(dfunc->df_instr + idx);
VIM_CLEAR(dfunc->df_instr);
- dfunc->df_instr = NULL;
}
if (dfunc->df_instr_debug != NULL)
{
for (idx = 0; idx < dfunc->df_instr_debug_count; ++idx)
delete_instr(dfunc->df_instr_debug + idx);
VIM_CLEAR(dfunc->df_instr_debug);
- dfunc->df_instr_debug = NULL;
}
#ifdef FEAT_PROFILE
if (dfunc->df_instr_prof != NULL)
--- 3844,3855 ----
***************
*** 3815,3821 ****
for (idx = 0; idx < dfunc->df_instr_prof_count; ++idx)
delete_instr(dfunc->df_instr_prof + idx);
VIM_CLEAR(dfunc->df_instr_prof);
- dfunc->df_instr_prof = NULL;
}
#endif
--- 3857,3862 ----
***************
*** 3823,3828 ****
--- 3864,3871 ----
dfunc->df_deleted = TRUE;
if (dfunc->df_ufunc != NULL)
dfunc->df_ufunc->uf_def_status = UF_NOT_COMPILED;
+
+ dfunc->df_delete_busy = FALSE;
}
/*
*** ../vim-9.0.1073/src/vim9expr.c 2022-12-14 20:54:52.411699476 +0000
--- src/vim9expr.c 2022-12-18 18:27:03.819479601 +0000
***************
*** 278,294 ****
{
for (int i = 0; i < cl->class_obj_member_count; ++i)
{
! 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;
return OK;
--- 278,293 ----
{
for (int i = 0; i < cl->class_obj_member_count; ++i)
{
! ocmember_T *m = &cl->class_obj_members[i];
! if (STRNCMP(name, m->ocm_name, len) == 0 && m->ocm_name[len] == NUL)
{
if (*name == '_' && cctx->ctx_ufunc->uf_class != cl)
{
! semsg(_(e_cannot_access_private_member_str), m->ocm_name);
return FAIL;
}
! generate_GET_OBJ_MEMBER(cctx, i, m->ocm_type);
*arg = name_end;
return OK;
*** ../vim-9.0.1073/src/vim9instr.c 2022-12-13 18:42:19.749879633 +0000
--- src/vim9instr.c 2022-12-18 20:53:32.093494839 +0000
***************
*** 957,962 ****
--- 957,994 ----
}
/*
+ * Generate an ISN_LOAD_CLASSMEMBER ("load" == TRUE) or ISN_STORE_CLASSMEMBER
+ * ("load" == FALSE) instruction.
+ */
+ int
+ generate_CLASSMEMBER(
+ cctx_T *cctx,
+ int load,
+ class_T *cl,
+ int idx)
+ {
+ isn_T *isn;
+
+ RETURN_OK_IF_SKIP(cctx);
+ if (load)
+ {
+ ocmember_T *m = &cl->class_class_members[idx];
+ isn = generate_instr_type(cctx, ISN_LOAD_CLASSMEMBER, m->ocm_type);
+ }
+ else
+ {
+ isn = generate_instr_drop(cctx, ISN_STORE_CLASSMEMBER, 1);
+ }
+ if (isn == NULL)
+ return FAIL;
+ isn->isn_arg.classmember.cm_class = cl;
+ ++cl->class_refcount;
+ isn->isn_arg.classmember.cm_idx = idx;
+
+ return OK;
+ }
+
+ /*
* Generate an ISN_STOREOUTER instruction.
*/
static int
***************
*** 2114,2119 ****
--- 2146,2152 ----
/*
* Generate a STORE instruction for "dest", not being "dest_local".
+ * "lhs" might be NULL.
* Return FAIL when out of memory.
*/
int
***************
*** 2122,2131 ****
assign_dest_T dest,
int opt_flags,
int vimvaridx,
- int scriptvar_idx,
- int scriptvar_sid,
type_T *type,
! char_u *name)
{
switch (dest)
{
--- 2155,2163 ----
assign_dest_T dest,
int opt_flags,
int vimvaridx,
type_T *type,
! char_u *name,
! lhs_T *lhs)
{
switch (dest)
{
***************
*** 2156,2164 ****
case dest_vimvar:
return generate_STORE(cctx, ISN_STOREV, vimvaridx, NULL);
case dest_script:
if (scriptvar_idx < 0)
{
! isntype_T isn_type = ISN_STORES;
if (SCRIPT_ID_VALID(scriptvar_sid)
&& SCRIPT_ITEM(scriptvar_sid)->sn_import_autoload
--- 2188,2198 ----
case dest_vimvar:
return generate_STORE(cctx, ISN_STOREV, vimvaridx, NULL);
case dest_script:
+ int scriptvar_idx = lhs->lhs_scriptvar_idx;
+ int scriptvar_sid = lhs->lhs_scriptvar_sid;
if (scriptvar_idx < 0)
{
! isntype_T isn_type = ISN_STORES;
if (SCRIPT_ID_VALID(scriptvar_sid)
&& SCRIPT_ITEM(scriptvar_sid)->sn_import_autoload
***************
*** 2177,2182 ****
--- 2211,2220 ----
}
return generate_VIM9SCRIPT(cctx, ISN_STORESCRIPT,
scriptvar_sid, scriptvar_idx, type);
+ case dest_class_member:
+ return generate_CLASSMEMBER(cctx, FALSE,
+ lhs->lhs_class, lhs->lhs_classmember_idx);
+
case dest_local:
case dest_expr:
// cannot happen
***************
*** 2210,2217 ****
if (lhs->lhs_dest != dest_local)
return generate_store_var(cctx, lhs->lhs_dest,
lhs->lhs_opt_flags, lhs->lhs_vimvaridx,
! lhs->lhs_scriptvar_idx, lhs->lhs_scriptvar_sid,
! lhs->lhs_type, lhs->lhs_name);
if (lhs->lhs_lvar != NULL)
{
--- 2248,2254 ----
if (lhs->lhs_dest != dest_local)
return generate_store_var(cctx, lhs->lhs_dest,
lhs->lhs_opt_flags, lhs->lhs_vimvaridx,
! lhs->lhs_type, lhs->lhs_name, lhs);
if (lhs->lhs_lvar != NULL)
{
***************
*** 2422,2427 ****
--- 2459,2469 ----
vim_free(isn->isn_arg.script.scriptref);
break;
+ case ISN_LOAD_CLASSMEMBER:
+ case ISN_STORE_CLASSMEMBER:
+ class_unref(isn->isn_arg.classmember.cm_class);
+ break;
+
case ISN_TRY:
vim_free(isn->isn_arg.tryref.try_ref);
break;
*** ../vim-9.0.1073/src/proto/vim9instr.pro 2022-12-13 18:42:19.749879633
+0000
--- src/proto/vim9instr.pro 2022-12-18 21:15:25.863956663 +0000
***************
*** 32,37 ****
--- 32,38 ----
int generate_SLICE(cctx_T *cctx, int count);
int generate_CHECKLEN(cctx_T *cctx, int min_len, int more_OK);
int generate_STORE(cctx_T *cctx, isntype_T isn_type, int idx, char_u *name);
+ int generate_CLASSMEMBER(cctx_T *cctx, int load, class_T *cl, int idx);
int generate_STORENR(cctx_T *cctx, int idx, varnumber_T value);
int generate_LOAD(cctx_T *cctx, isntype_T isn_type, int idx, char_u *name,
type_T *type);
int generate_LOADOUTER(cctx_T *cctx, int idx, int nesting, int loop_depth,
int loop_idx, type_T *type);
***************
*** 74,80 ****
int generate_UNPACK(cctx_T *cctx, int var_count, int semicolon);
int generate_cmdmods(cctx_T *cctx, cmdmod_T *cmod);
int generate_undo_cmdmods(cctx_T *cctx);
! int generate_store_var(cctx_T *cctx, assign_dest_T dest, int opt_flags, int
vimvaridx, int scriptvar_idx, int scriptvar_sid, type_T *type, char_u *name);
int inside_loop_scope(cctx_T *cctx);
int generate_store_lhs(cctx_T *cctx, lhs_T *lhs, int instr_count, int
is_decl);
void may_generate_prof_end(cctx_T *cctx, int prof_lnum);
--- 75,81 ----
int generate_UNPACK(cctx_T *cctx, int var_count, int semicolon);
int generate_cmdmods(cctx_T *cctx, cmdmod_T *cmod);
int generate_undo_cmdmods(cctx_T *cctx);
! int generate_store_var(cctx_T *cctx, assign_dest_T dest, int opt_flags, int
vimvaridx, type_T *type, char_u *name, lhs_T *lhs);
int inside_loop_scope(cctx_T *cctx);
int generate_store_lhs(cctx_T *cctx, lhs_T *lhs, int instr_count, int
is_decl);
void may_generate_prof_end(cctx_T *cctx, int prof_lnum);
*** ../vim-9.0.1073/src/vim9cmds.c 2022-12-02 18:12:01.018476816 +0000
--- src/vim9cmds.c 2022-12-18 20:05:02.684604705 +0000
***************
*** 1013,1019 ****
if (dest != dest_local)
{
if (generate_store_var(cctx, dest, opt_flags, vimvaridx,
! 0, 0, type, name) == FAIL)
goto failed;
}
else if (varlen == 1 && *arg == '_')
--- 1013,1019 ----
if (dest != dest_local)
{
if (generate_store_var(cctx, dest, opt_flags, vimvaridx,
! type, name, NULL) == FAIL)
goto failed;
}
else if (varlen == 1 && *arg == '_')
*** ../vim-9.0.1073/src/vim9execute.c 2022-12-13 18:42:19.749879633 +0000
--- src/vim9execute.c 2022-12-18 20:43:29.722073643 +0000
***************
*** 3817,3822 ****
--- 3817,3843 ----
goto on_error;
break;
+ case ISN_LOAD_CLASSMEMBER:
+ {
+ if (GA_GROW_FAILS(&ectx->ec_stack, 1))
+ goto theend;
+ classmember_T *cm = &iptr->isn_arg.classmember;
+ *STACK_TV_BOT(0) =
+ cm->cm_class->class_members_tv[cm->cm_idx];
+ ++ectx->ec_stack.ga_len;
+ }
+ break;
+
+ case ISN_STORE_CLASSMEMBER:
+ {
+ classmember_T *cm = &iptr->isn_arg.classmember;
+ tv = &cm->cm_class->class_members_tv[cm->cm_idx];
+ clear_tv(tv);
+ *tv = *STACK_TV_BOT(-1);
+ --ectx->ec_stack.ga_len;
+ }
+ break;
+
// Load or store variable or argument from outer scope.
case ISN_LOADOUTER:
case ISN_STOREOUTER:
***************
*** 6403,6408 ****
--- 6424,6442 ----
smsg("%s%4d STORERANGE", pfx, current);
break;
+ case ISN_LOAD_CLASSMEMBER:
+ case ISN_STORE_CLASSMEMBER:
+ {
+ class_T *cl = iptr->isn_arg.classmember.cm_class;
+ int idx = iptr->isn_arg.classmember.cm_idx;
+ ocmember_T *ocm = &cl->class_class_members[idx];
+ smsg("%s%4d %s CLASSMEMBER %s.%s", pfx, current,
+ iptr->isn_type == ISN_LOAD_CLASSMEMBER
+ ? "LOAD" : "STORE",
+ cl->class_name, ocm->ocm_name);
+ }
+ break;
+
// constants
case ISN_PUSHNR:
smsg("%s%4d PUSHNR %lld", pfx, current,
*** ../vim-9.0.1073/src/testdir/test_vim9_class.vim 2022-12-14
20:54:52.411699476 +0000
--- src/testdir/test_vim9_class.vim 2022-12-18 21:17:55.343794543 +0000
***************
*** 306,311 ****
--- 306,335 ----
assert_fails('trip.two = 22', 'E1335')
trip.three = 33
assert_equal(33, trip.three)
+
+ assert_fails('trip.four = 4', 'E1334')
+ END
+ v9.CheckScriptSuccess(lines)
+ enddef
+
+ def Test_class_member_access()
+ var lines =<< trim END
+ vim9script
+ class TextPos
+ this.lnum = 1
+ this.col = 1
+ static counter = 0
+
+ def AddToCounter(nr: number)
+ counter += nr
+ enddef
+ endclass
+
+ assert_equal(0, TextPos.counter)
+ TextPos.AddToCounter(3)
+ assert_equal(3, TextPos.counter)
+
+ assert_fails('TextPos.counter += 5', 'E1335')
END
v9.CheckScriptSuccess(lines)
enddef
*** ../vim-9.0.1073/src/version.c 2022-12-18 17:47:11.430957013 +0000
--- src/version.c 2022-12-18 18:30:47.947164802 +0000
***************
*** 697,698 ****
--- 697,700 ----
{ /* Add new patch number below this line */
+ /**/
+ 1074,
/**/
--
Shit makes the flowers grow and that's beautiful
/// Bram Moolenaar -- [email protected] -- http://www.Moolenaar.net \\\
/// \\\
\\\ sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ ///
\\\ help me help AIDS victims -- http://ICCF-Holland.org ///
--
--
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/20221218214324.657731C0950%40moolenaar.net.