mirror of
https://github.com/vim/vim.git
synced 2025-07-26 11:04:33 -04:00
patch 9.0.1045: in a class object members cannot be initialized
Problem: In a class object members cannot be initialized. Solution: Support initializing object members. Make "dissassemble" work on an object method.
This commit is contained in:
parent
6c87bbb4e4
commit
7ce7daf6cd
16
src/eval.c
16
src/eval.c
@ -1193,9 +1193,8 @@ get_lval(
|
|||||||
var2.v_type = VAR_UNKNOWN;
|
var2.v_type = VAR_UNKNOWN;
|
||||||
while (*p == '[' || (*p == '.' && p[1] != '=' && p[1] != '.'))
|
while (*p == '[' || (*p == '.' && p[1] != '=' && p[1] != '.'))
|
||||||
{
|
{
|
||||||
int r = OK;
|
if (*p == '.' && lp->ll_tv->v_type != VAR_DICT
|
||||||
|
&& lp->ll_tv->v_type != VAR_CLASS)
|
||||||
if (*p == '.' && lp->ll_tv->v_type != VAR_DICT)
|
|
||||||
{
|
{
|
||||||
if (!quiet)
|
if (!quiet)
|
||||||
semsg(_(e_dot_can_only_be_used_on_dictionary_str), name);
|
semsg(_(e_dot_can_only_be_used_on_dictionary_str), name);
|
||||||
@ -1203,7 +1202,8 @@ get_lval(
|
|||||||
}
|
}
|
||||||
if (lp->ll_tv->v_type != VAR_LIST
|
if (lp->ll_tv->v_type != VAR_LIST
|
||||||
&& lp->ll_tv->v_type != VAR_DICT
|
&& lp->ll_tv->v_type != VAR_DICT
|
||||||
&& lp->ll_tv->v_type != VAR_BLOB)
|
&& lp->ll_tv->v_type != VAR_BLOB
|
||||||
|
&& lp->ll_tv->v_type != VAR_CLASS)
|
||||||
{
|
{
|
||||||
if (!quiet)
|
if (!quiet)
|
||||||
emsg(_(e_can_only_index_list_dictionary_or_blob));
|
emsg(_(e_can_only_index_list_dictionary_or_blob));
|
||||||
@ -1211,6 +1211,7 @@ get_lval(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// A NULL list/blob works like an empty list/blob, allocate one now.
|
// 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)
|
if (lp->ll_tv->v_type == VAR_LIST && lp->ll_tv->vval.v_list == NULL)
|
||||||
r = rettv_list_alloc(lp->ll_tv);
|
r = rettv_list_alloc(lp->ll_tv);
|
||||||
else if (lp->ll_tv->v_type == VAR_BLOB
|
else if (lp->ll_tv->v_type == VAR_BLOB
|
||||||
@ -1463,7 +1464,7 @@ get_lval(
|
|||||||
lp->ll_tv = NULL;
|
lp->ll_tv = NULL;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
else
|
else if (lp->ll_tv->v_type == VAR_LIST)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* Get the number and item for the only or first index of the List.
|
* Get the number and item for the only or first index of the List.
|
||||||
@ -1508,6 +1509,11 @@ get_lval(
|
|||||||
|
|
||||||
lp->ll_tv = &lp->ll_li->li_tv;
|
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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
clear_tv(&var1);
|
clear_tv(&var1);
|
||||||
|
@ -5,6 +5,7 @@ void ex_interface(exarg_T *eap);
|
|||||||
void ex_enum(exarg_T *eap);
|
void ex_enum(exarg_T *eap);
|
||||||
void ex_type(exarg_T *eap);
|
void ex_type(exarg_T *eap);
|
||||||
int class_object_index(char_u **arg, typval_T *rettv, evalarg_T *evalarg, int verbose);
|
int class_object_index(char_u **arg, typval_T *rettv, evalarg_T *evalarg, int verbose);
|
||||||
|
ufunc_T *find_class_func(char_u **arg);
|
||||||
void copy_object(typval_T *from, typval_T *to);
|
void copy_object(typval_T *from, typval_T *to);
|
||||||
void object_unref(object_T *obj);
|
void object_unref(object_T *obj);
|
||||||
void copy_class(typval_T *from, typval_T *to);
|
void copy_class(typval_T *from, typval_T *to);
|
||||||
|
@ -4,7 +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_type(cctx_T *cctx, isntype_T isn_type, type_T *type);
|
||||||
isn_T *generate_instr_debug(cctx_T *cctx);
|
isn_T *generate_instr_debug(cctx_T *cctx);
|
||||||
int generate_CONSTRUCT(cctx_T *cctx, class_T *cl);
|
int generate_CONSTRUCT(cctx_T *cctx, class_T *cl);
|
||||||
int generate_OBJ_MEMBER(cctx_T *cctx, int idx, type_T *type);
|
int generate_GET_OBJ_MEMBER(cctx_T *cctx, int idx, type_T *type);
|
||||||
|
int generate_STORE_THIS(cctx_T *cctx, int idx);
|
||||||
int may_generate_2STRING(int offset, int tolerant, cctx_T *cctx);
|
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);
|
int generate_add_instr(cctx_T *cctx, vartype_T vartype, type_T *type1, type_T *type2, exprtype_T expr_type);
|
||||||
vartype_T operator_type(type_T *type1, type_T *type2);
|
vartype_T operator_type(type_T *type1, type_T *type2);
|
||||||
|
@ -1463,8 +1463,9 @@ typedef struct {
|
|||||||
* Entry for an object member variable.
|
* Entry for an object member variable.
|
||||||
*/
|
*/
|
||||||
typedef struct {
|
typedef struct {
|
||||||
char_u *om_name; // allocated
|
char_u *om_name; // allocated
|
||||||
type_T *om_type;
|
type_T *om_type;
|
||||||
|
char_u *om_init; // allocated
|
||||||
} objmember_T;
|
} objmember_T;
|
||||||
|
|
||||||
// "class_T": used for v_class of typval of VAR_CLASS
|
// "class_T": used for v_class of typval of VAR_CLASS
|
||||||
|
@ -129,7 +129,7 @@ def Test_class_basic()
|
|||||||
|
|
||||||
class TextPosition
|
class TextPosition
|
||||||
this.lnum: number
|
this.lnum: number
|
||||||
this.col: number
|
this.col: number
|
||||||
|
|
||||||
def ToString(): string
|
def ToString(): string
|
||||||
return $'({this.lnum}, {this.col})'
|
return $'({this.lnum}, {this.col})'
|
||||||
@ -147,5 +147,40 @@ def Test_class_basic()
|
|||||||
v9.CheckScriptSuccess(lines)
|
v9.CheckScriptSuccess(lines)
|
||||||
enddef
|
enddef
|
||||||
|
|
||||||
|
def Test_class_member_initializer()
|
||||||
|
var lines =<< trim END
|
||||||
|
vim9script
|
||||||
|
|
||||||
|
class TextPosition
|
||||||
|
this.lnum: number = 1
|
||||||
|
this.col: number = 1
|
||||||
|
|
||||||
|
def new(lnum: number)
|
||||||
|
this.lnum = lnum
|
||||||
|
enddef
|
||||||
|
endclass
|
||||||
|
|
||||||
|
var pos = TextPosition.new(3)
|
||||||
|
assert_equal(3, pos.lnum)
|
||||||
|
assert_equal(1, pos.col)
|
||||||
|
|
||||||
|
var instr = execute('disassemble TextPosition.new')
|
||||||
|
assert_match('new\_s*' ..
|
||||||
|
'0 NEW TextPosition size 72\_s*' ..
|
||||||
|
'\d PUSHNR 1\_s*' ..
|
||||||
|
'\d STORE_THIS 0\_s*' ..
|
||||||
|
'\d PUSHNR 1\_s*' ..
|
||||||
|
'\d STORE_THIS 1\_s*' ..
|
||||||
|
'this.lnum = lnum\_s*' ..
|
||||||
|
'\d LOAD arg\[-1]\_s*' ..
|
||||||
|
'\d PUSHNR 0\_s*' ..
|
||||||
|
'\d LOAD $0\_s*' ..
|
||||||
|
'\d\+ STOREINDEX object\_s*' ..
|
||||||
|
'\d\+ RETURN object.*',
|
||||||
|
instr)
|
||||||
|
END
|
||||||
|
v9.CheckScriptSuccess(lines)
|
||||||
|
enddef
|
||||||
|
|
||||||
|
|
||||||
" vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker
|
" vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker
|
||||||
|
@ -4047,6 +4047,12 @@ trans_function_name(
|
|||||||
name = vim_strsave(lv.ll_tv->vval.v_string);
|
name = vim_strsave(lv.ll_tv->vval.v_string);
|
||||||
*pp = end;
|
*pp = end;
|
||||||
}
|
}
|
||||||
|
else if (lv.ll_tv->v_type == VAR_CLASS
|
||||||
|
&& lv.ll_tv->vval.v_class != NULL)
|
||||||
|
{
|
||||||
|
name = vim_strsave(lv.ll_tv->vval.v_class->class_name);
|
||||||
|
*pp = end;
|
||||||
|
}
|
||||||
else if (lv.ll_tv->v_type == VAR_PARTIAL
|
else if (lv.ll_tv->v_type == VAR_PARTIAL
|
||||||
&& lv.ll_tv->vval.v_partial != NULL)
|
&& lv.ll_tv->vval.v_partial != NULL)
|
||||||
{
|
{
|
||||||
@ -5240,8 +5246,17 @@ find_func_by_name(char_u *name, compiletype_T *compile_type)
|
|||||||
fname = vim_strnsave(name, arg - name);
|
fname = vim_strnsave(name, arg - name);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
|
// First try finding a method in a class, find_func_by_name() will give
|
||||||
|
// an error if the function is not found.
|
||||||
|
ufunc = find_class_func(&arg);
|
||||||
|
if (ufunc != NULL)
|
||||||
|
return ufunc;
|
||||||
|
|
||||||
fname = trans_function_name(&arg, &is_global, FALSE,
|
fname = trans_function_name(&arg, &is_global, FALSE,
|
||||||
TFN_INT | TFN_QUIET | TFN_NO_AUTOLOAD, NULL, NULL, NULL);
|
TFN_INT | TFN_QUIET | TFN_NO_AUTOLOAD | TFN_NO_DECL,
|
||||||
|
NULL, NULL, NULL);
|
||||||
|
}
|
||||||
if (fname == NULL)
|
if (fname == NULL)
|
||||||
{
|
{
|
||||||
semsg(_(e_invalid_argument_str), name);
|
semsg(_(e_invalid_argument_str), name);
|
||||||
|
@ -695,6 +695,8 @@ static char *(features[]) =
|
|||||||
|
|
||||||
static int included_patches[] =
|
static int included_patches[] =
|
||||||
{ /* Add new patch number below this line */
|
{ /* Add new patch number below this line */
|
||||||
|
/**/
|
||||||
|
1045,
|
||||||
/**/
|
/**/
|
||||||
1044,
|
1044,
|
||||||
/**/
|
/**/
|
||||||
|
@ -33,7 +33,9 @@ typedef enum {
|
|||||||
ISN_SOURCE, // source autoload script, isn_arg.number is the script ID
|
ISN_SOURCE, // source autoload script, isn_arg.number is the script ID
|
||||||
ISN_INSTR, // instructions compiled from expression
|
ISN_INSTR, // instructions compiled from expression
|
||||||
ISN_CONSTRUCT, // construct an object, using contstruct_T
|
ISN_CONSTRUCT, // construct an object, using contstruct_T
|
||||||
ISN_OBJ_MEMBER, // object member, index is isn_arg.number
|
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
|
||||||
|
|
||||||
// get and set variables
|
// get and set variables
|
||||||
ISN_LOAD, // push local variable isn_arg.number
|
ISN_LOAD, // push local variable isn_arg.number
|
||||||
|
@ -147,12 +147,30 @@ ex_class(exarg_T *eap)
|
|||||||
if (type == NULL)
|
if (type == NULL)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
char_u *expr_start = skipwhite(type_arg);
|
||||||
|
if (*expr_start == '=' && (!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);
|
||||||
|
|
||||||
|
char_u *expr_end = expr_start;
|
||||||
|
evalarg_T evalarg;
|
||||||
|
init_evalarg(&evalarg);
|
||||||
|
skip_expr(&expr_end, &evalarg);
|
||||||
|
clear_evalarg(&evalarg, NULL);
|
||||||
|
|
||||||
if (ga_grow(&objmembers, 1) == FAIL)
|
if (ga_grow(&objmembers, 1) == FAIL)
|
||||||
break;
|
break;
|
||||||
objmember_T *m = ((objmember_T *)objmembers.ga_data)
|
objmember_T *m = ((objmember_T *)objmembers.ga_data)
|
||||||
+ objmembers.ga_len;
|
+ objmembers.ga_len;
|
||||||
m->om_name = vim_strnsave(varname, varname_end - varname);
|
m->om_name = vim_strnsave(varname, varname_end - varname);
|
||||||
m->om_type = type;
|
m->om_type = type;
|
||||||
|
if (expr_end > expr_start)
|
||||||
|
m->om_init = vim_strnsave(expr_start, expr_end - expr_start);
|
||||||
++objmembers.ga_len;
|
++objmembers.ga_len;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -190,6 +208,9 @@ ex_class(exarg_T *eap)
|
|||||||
// TODO: how about errors?
|
// TODO: how about errors?
|
||||||
if (uf != NULL && ga_grow(&objmethods, 1) == OK)
|
if (uf != NULL && ga_grow(&objmethods, 1) == OK)
|
||||||
{
|
{
|
||||||
|
if (STRNCMP(uf->uf_name, "new", 3) == 0)
|
||||||
|
uf->uf_flags |= FC_NEW;
|
||||||
|
|
||||||
((ufunc_T **)objmethods.ga_data)[objmethods.ga_len] = uf;
|
((ufunc_T **)objmethods.ga_data)[objmethods.ga_len] = uf;
|
||||||
++objmethods.ga_len;
|
++objmethods.ga_len;
|
||||||
}
|
}
|
||||||
@ -333,6 +354,7 @@ cleanup:
|
|||||||
{
|
{
|
||||||
objmember_T *m = ((objmember_T *)objmembers.ga_data) + i;
|
objmember_T *m = ((objmember_T *)objmembers.ga_data) + i;
|
||||||
vim_free(m->om_name);
|
vim_free(m->om_name);
|
||||||
|
vim_free(m->om_init);
|
||||||
}
|
}
|
||||||
ga_clear(&objmembers);
|
ga_clear(&objmembers);
|
||||||
|
|
||||||
@ -519,6 +541,52 @@ class_object_index(
|
|||||||
return FAIL;
|
return FAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If "arg" points to a class or object method, return it.
|
||||||
|
* Otherwise return NULL.
|
||||||
|
*/
|
||||||
|
ufunc_T *
|
||||||
|
find_class_func(char_u **arg)
|
||||||
|
{
|
||||||
|
char_u *name = *arg;
|
||||||
|
char_u *name_end = find_name_end(name, NULL, NULL, FNE_CHECK_START);
|
||||||
|
if (name_end == name || *name_end != '.')
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
size_t len = name_end - name;
|
||||||
|
typval_T tv;
|
||||||
|
tv.v_type = VAR_UNKNOWN;
|
||||||
|
if (eval_variable(name, len, 0, &tv, NULL, EVAL_VAR_NOAUTOLOAD) == FAIL)
|
||||||
|
return NULL;
|
||||||
|
if (tv.v_type != VAR_CLASS && tv.v_type != VAR_OBJECT)
|
||||||
|
{
|
||||||
|
clear_tv(&tv);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
class_T *cl = tv.v_type == VAR_CLASS ? tv.vval.v_class
|
||||||
|
: tv.vval.v_object->obj_class;
|
||||||
|
if (cl == NULL)
|
||||||
|
return NULL;
|
||||||
|
char_u *fname = name_end + 1;
|
||||||
|
char_u *fname_end = find_name_end(fname, NULL, NULL, FNE_CHECK_START);
|
||||||
|
if (fname_end == fname)
|
||||||
|
return NULL;
|
||||||
|
len = fname_end - fname;
|
||||||
|
|
||||||
|
for (int i = 0; i < cl->class_obj_method_count; ++i)
|
||||||
|
{
|
||||||
|
ufunc_T *fp = cl->class_obj_methods[i];
|
||||||
|
// Use a separate pointer to avoid that ASAN complains about
|
||||||
|
// uf_name[] only being 4 characters.
|
||||||
|
char_u *ufname = (char_u *)fp->uf_name;
|
||||||
|
if (STRNCMP(fname, ufname, len) == 0 && ufname[len] == NUL)
|
||||||
|
return fp;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Make a copy of an object.
|
* Make a copy of an object.
|
||||||
*/
|
*/
|
||||||
@ -585,6 +653,7 @@ class_unref(class_T *cl)
|
|||||||
{
|
{
|
||||||
objmember_T *m = &cl->class_obj_members[i];
|
objmember_T *m = &cl->class_obj_members[i];
|
||||||
vim_free(m->om_name);
|
vim_free(m->om_name);
|
||||||
|
vim_free(m->om_init);
|
||||||
}
|
}
|
||||||
vim_free(cl->class_obj_members);
|
vim_free(cl->class_obj_members);
|
||||||
|
|
||||||
|
@ -2117,6 +2117,71 @@ compile_assign_unlet(
|
|||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Generate an instruction to push the default value for "vartype".
|
||||||
|
* if "dest_local" is TRUE then for some types no instruction is generated.
|
||||||
|
* "skip_store" is set to TRUE if no PUSH instruction is generated.
|
||||||
|
* Returns OK or FAIL.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
push_default_value(
|
||||||
|
cctx_T *cctx,
|
||||||
|
vartype_T vartype,
|
||||||
|
int dest_is_local,
|
||||||
|
int *skip_store)
|
||||||
|
{
|
||||||
|
int r = OK;
|
||||||
|
|
||||||
|
switch (vartype)
|
||||||
|
{
|
||||||
|
case VAR_BOOL:
|
||||||
|
r = generate_PUSHBOOL(cctx, VVAL_FALSE);
|
||||||
|
break;
|
||||||
|
case VAR_FLOAT:
|
||||||
|
r = generate_PUSHF(cctx, 0.0);
|
||||||
|
break;
|
||||||
|
case VAR_STRING:
|
||||||
|
r = generate_PUSHS(cctx, NULL);
|
||||||
|
break;
|
||||||
|
case VAR_BLOB:
|
||||||
|
r = generate_PUSHBLOB(cctx, blob_alloc());
|
||||||
|
break;
|
||||||
|
case VAR_FUNC:
|
||||||
|
r = generate_PUSHFUNC(cctx, NULL, &t_func_void, TRUE);
|
||||||
|
break;
|
||||||
|
case VAR_LIST:
|
||||||
|
r = generate_NEWLIST(cctx, 0, FALSE);
|
||||||
|
break;
|
||||||
|
case VAR_DICT:
|
||||||
|
r = generate_NEWDICT(cctx, 0, FALSE);
|
||||||
|
break;
|
||||||
|
case VAR_JOB:
|
||||||
|
r = generate_PUSHJOB(cctx);
|
||||||
|
break;
|
||||||
|
case VAR_CHANNEL:
|
||||||
|
r = generate_PUSHCHANNEL(cctx);
|
||||||
|
break;
|
||||||
|
case VAR_NUMBER:
|
||||||
|
case VAR_UNKNOWN:
|
||||||
|
case VAR_ANY:
|
||||||
|
case VAR_PARTIAL:
|
||||||
|
case VAR_VOID:
|
||||||
|
case VAR_INSTR:
|
||||||
|
case VAR_CLASS:
|
||||||
|
case VAR_OBJECT:
|
||||||
|
case VAR_SPECIAL: // cannot happen
|
||||||
|
// This is skipped for local variables, they are always
|
||||||
|
// initialized to zero. But in a "for" or "while" loop
|
||||||
|
// the value may have been changed.
|
||||||
|
if (dest_is_local && !inside_loop_scope(cctx))
|
||||||
|
*skip_store = TRUE;
|
||||||
|
else
|
||||||
|
r = generate_PUSHNR(cctx, 0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Compile declaration and assignment:
|
* Compile declaration and assignment:
|
||||||
* "let name"
|
* "let name"
|
||||||
@ -2462,62 +2527,12 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
int r = OK;
|
|
||||||
|
|
||||||
// variables are always initialized
|
// variables are always initialized
|
||||||
if (GA_GROW_FAILS(instr, 1))
|
if (GA_GROW_FAILS(instr, 1))
|
||||||
goto theend;
|
goto theend;
|
||||||
switch (lhs.lhs_member_type->tt_type)
|
instr_count = instr->ga_len;
|
||||||
{
|
int r = push_default_value(cctx, lhs.lhs_member_type->tt_type,
|
||||||
case VAR_BOOL:
|
lhs.lhs_dest == dest_local, &skip_store);
|
||||||
r = generate_PUSHBOOL(cctx, VVAL_FALSE);
|
|
||||||
break;
|
|
||||||
case VAR_FLOAT:
|
|
||||||
r = generate_PUSHF(cctx, 0.0);
|
|
||||||
break;
|
|
||||||
case VAR_STRING:
|
|
||||||
r = generate_PUSHS(cctx, NULL);
|
|
||||||
break;
|
|
||||||
case VAR_BLOB:
|
|
||||||
r = generate_PUSHBLOB(cctx, blob_alloc());
|
|
||||||
break;
|
|
||||||
case VAR_FUNC:
|
|
||||||
r = generate_PUSHFUNC(cctx, NULL, &t_func_void, TRUE);
|
|
||||||
break;
|
|
||||||
case VAR_LIST:
|
|
||||||
r = generate_NEWLIST(cctx, 0, FALSE);
|
|
||||||
break;
|
|
||||||
case VAR_DICT:
|
|
||||||
r = generate_NEWDICT(cctx, 0, FALSE);
|
|
||||||
break;
|
|
||||||
case VAR_JOB:
|
|
||||||
r = generate_PUSHJOB(cctx);
|
|
||||||
break;
|
|
||||||
case VAR_CHANNEL:
|
|
||||||
r = generate_PUSHCHANNEL(cctx);
|
|
||||||
break;
|
|
||||||
case VAR_NUMBER:
|
|
||||||
case VAR_UNKNOWN:
|
|
||||||
case VAR_ANY:
|
|
||||||
case VAR_PARTIAL:
|
|
||||||
case VAR_VOID:
|
|
||||||
case VAR_INSTR:
|
|
||||||
case VAR_CLASS:
|
|
||||||
case VAR_OBJECT:
|
|
||||||
case VAR_SPECIAL: // cannot happen
|
|
||||||
// This is skipped for local variables, they are always
|
|
||||||
// initialized to zero. But in a "for" or "while" loop
|
|
||||||
// the value may have been changed.
|
|
||||||
if (lhs.lhs_dest == dest_local
|
|
||||||
&& !inside_loop_scope(cctx))
|
|
||||||
skip_store = TRUE;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
instr_count = instr->ga_len;
|
|
||||||
r = generate_PUSHNR(cctx, 0);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (r == FAIL)
|
if (r == FAIL)
|
||||||
goto theend;
|
goto theend;
|
||||||
}
|
}
|
||||||
@ -2946,9 +2961,32 @@ compile_def_function(
|
|||||||
vim_strsave((char_u *)"this");
|
vim_strsave((char_u *)"this");
|
||||||
++dfunc->df_var_names.ga_len;
|
++dfunc->df_var_names.ga_len;
|
||||||
|
|
||||||
// In the constructor allocate memory for the object.
|
// In the constructor allocate memory for the object and initialize the
|
||||||
|
// object members.
|
||||||
if ((ufunc->uf_flags & FC_NEW) == FC_NEW)
|
if ((ufunc->uf_flags & FC_NEW) == FC_NEW)
|
||||||
|
{
|
||||||
generate_CONSTRUCT(&cctx, ufunc->uf_class);
|
generate_CONSTRUCT(&cctx, ufunc->uf_class);
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ufunc->uf_def_args.ga_len > 0)
|
if (ufunc->uf_def_args.ga_len > 0)
|
||||||
@ -3564,7 +3602,10 @@ nextline:
|
|||||||
// Return void if there is no return at the end.
|
// Return void if there is no return at the end.
|
||||||
// For a constructor return the object.
|
// For a constructor return the object.
|
||||||
if ((ufunc->uf_flags & FC_NEW) == FC_NEW)
|
if ((ufunc->uf_flags & FC_NEW) == FC_NEW)
|
||||||
|
{
|
||||||
generate_instr(&cctx, ISN_RETURN_OBJECT);
|
generate_instr(&cctx, ISN_RETURN_OBJECT);
|
||||||
|
ufunc->uf_ret_type = &ufunc->uf_class->class_object_type;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
generate_instr(&cctx, ISN_RETURN_VOID);
|
generate_instr(&cctx, ISN_RETURN_VOID);
|
||||||
}
|
}
|
||||||
|
@ -3009,7 +3009,7 @@ exec_instructions(ectx_T *ectx)
|
|||||||
iptr = &ectx->ec_instr[ectx->ec_iidx++];
|
iptr = &ectx->ec_instr[ectx->ec_iidx++];
|
||||||
switch (iptr->isn_type)
|
switch (iptr->isn_type)
|
||||||
{
|
{
|
||||||
// Constructor, new() method.
|
// Constructor, first instruction in a new() method.
|
||||||
case ISN_CONSTRUCT:
|
case ISN_CONSTRUCT:
|
||||||
// "this" is always the local variable at index zero
|
// "this" is always the local variable at index zero
|
||||||
tv = STACK_TV_VAR(0);
|
tv = STACK_TV_VAR(0);
|
||||||
@ -5114,7 +5114,7 @@ exec_instructions(ectx_T *ectx)
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ISN_OBJ_MEMBER:
|
case ISN_GET_OBJ_MEMBER:
|
||||||
{
|
{
|
||||||
tv = STACK_TV_BOT(-1);
|
tv = STACK_TV_BOT(-1);
|
||||||
if (tv->v_type != VAR_OBJECT)
|
if (tv->v_type != VAR_OBJECT)
|
||||||
@ -5143,6 +5143,18 @@ exec_instructions(ectx_T *ectx)
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case ISN_STORE_THIS:
|
||||||
|
{
|
||||||
|
int idx = iptr->isn_arg.number;
|
||||||
|
object_T *obj = STACK_TV_VAR(0)->vval.v_object;
|
||||||
|
// the members are located right after the object struct
|
||||||
|
typval_T *mtv = ((typval_T *)(obj + 1)) + idx;
|
||||||
|
clear_tv(mtv);
|
||||||
|
*mtv = *STACK_TV_BOT(-1);
|
||||||
|
--ectx->ec_stack.ga_len;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case ISN_CLEARDICT:
|
case ISN_CLEARDICT:
|
||||||
dict_stack_drop();
|
dict_stack_drop();
|
||||||
break;
|
break;
|
||||||
@ -6805,7 +6817,9 @@ list_instructions(char *pfx, isn_T *instr, int instr_count, ufunc_T *ufunc)
|
|||||||
case ISN_MEMBER: smsg("%s%4d MEMBER", pfx, current); break;
|
case ISN_MEMBER: smsg("%s%4d MEMBER", pfx, current); break;
|
||||||
case ISN_STRINGMEMBER: smsg("%s%4d MEMBER %s", pfx, current,
|
case ISN_STRINGMEMBER: smsg("%s%4d MEMBER %s", pfx, current,
|
||||||
iptr->isn_arg.string); break;
|
iptr->isn_arg.string); break;
|
||||||
case ISN_OBJ_MEMBER: smsg("%s%4d OBJ_MEMBER %d", pfx, current,
|
case ISN_GET_OBJ_MEMBER: smsg("%s%4d OBJ_MEMBER %d", pfx, current,
|
||||||
|
(int)iptr->isn_arg.number); break;
|
||||||
|
case ISN_STORE_THIS: smsg("%s%4d STORE_THIS %d", pfx, current,
|
||||||
(int)iptr->isn_arg.number); break;
|
(int)iptr->isn_arg.number); break;
|
||||||
case ISN_CLEARDICT: smsg("%s%4d CLEARDICT", pfx, current); break;
|
case ISN_CLEARDICT: smsg("%s%4d CLEARDICT", pfx, current); break;
|
||||||
case ISN_USEDICT: smsg("%s%4d USEDICT", pfx, current); break;
|
case ISN_USEDICT: smsg("%s%4d USEDICT", pfx, current); break;
|
||||||
|
@ -253,7 +253,6 @@ compile_member(int is_slice, int *keeping_dict, cctx_T *cctx)
|
|||||||
/*
|
/*
|
||||||
* Compile ".member" coming after an object or class.
|
* Compile ".member" coming after an object or class.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static int
|
static int
|
||||||
compile_class_object_index(cctx_T *cctx, char_u **arg, type_T *type)
|
compile_class_object_index(cctx_T *cctx, char_u **arg, type_T *type)
|
||||||
{
|
{
|
||||||
@ -282,7 +281,7 @@ compile_class_object_index(cctx_T *cctx, char_u **arg, type_T *type)
|
|||||||
objmember_T *m = &cl->class_obj_members[i];
|
objmember_T *m = &cl->class_obj_members[i];
|
||||||
if (STRNCMP(name, m->om_name, len) == 0 && m->om_name[len] == NUL)
|
if (STRNCMP(name, m->om_name, len) == 0 && m->om_name[len] == NUL)
|
||||||
{
|
{
|
||||||
generate_OBJ_MEMBER(cctx, i, m->om_type);
|
generate_GET_OBJ_MEMBER(cctx, i, m->om_type);
|
||||||
|
|
||||||
*arg = name_end;
|
*arg = name_end;
|
||||||
return OK;
|
return OK;
|
||||||
|
@ -132,15 +132,16 @@ generate_CONSTRUCT(cctx_T *cctx, class_T *cl)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Generate ISN_OBJ_MEMBER - access object member by indes.
|
* Generate ISN_GET_OBJ_MEMBER - access member of object at bottom of stack by
|
||||||
|
* index.
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
generate_OBJ_MEMBER(cctx_T *cctx, int idx, type_T *type)
|
generate_GET_OBJ_MEMBER(cctx_T *cctx, int idx, type_T *type)
|
||||||
{
|
{
|
||||||
RETURN_OK_IF_SKIP(cctx);
|
RETURN_OK_IF_SKIP(cctx);
|
||||||
|
|
||||||
// drop the object type
|
// drop the object type
|
||||||
isn_T *isn = generate_instr_drop(cctx, ISN_OBJ_MEMBER, 1);
|
isn_T *isn = generate_instr_drop(cctx, ISN_GET_OBJ_MEMBER, 1);
|
||||||
if (isn == NULL)
|
if (isn == NULL)
|
||||||
return FAIL;
|
return FAIL;
|
||||||
|
|
||||||
@ -148,6 +149,24 @@ generate_OBJ_MEMBER(cctx_T *cctx, int idx, type_T *type)
|
|||||||
return push_type_stack2(cctx, type, &t_any);
|
return push_type_stack2(cctx, type, &t_any);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Generate ISN_STORE_THIS - store value in member of "this" object with member
|
||||||
|
* index "idx".
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
generate_STORE_THIS(cctx_T *cctx, int idx)
|
||||||
|
{
|
||||||
|
RETURN_OK_IF_SKIP(cctx);
|
||||||
|
|
||||||
|
// drop the value type
|
||||||
|
isn_T *isn = generate_instr_drop(cctx, ISN_STORE_THIS, 1);
|
||||||
|
if (isn == NULL)
|
||||||
|
return FAIL;
|
||||||
|
|
||||||
|
isn->isn_arg.number = idx;
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If type at "offset" isn't already VAR_STRING then generate ISN_2STRING.
|
* If type at "offset" isn't already VAR_STRING then generate ISN_2STRING.
|
||||||
* But only for simple types.
|
* But only for simple types.
|
||||||
@ -2458,6 +2477,7 @@ delete_instr(isn_T *isn)
|
|||||||
case ISN_FINISH:
|
case ISN_FINISH:
|
||||||
case ISN_FOR:
|
case ISN_FOR:
|
||||||
case ISN_GETITEM:
|
case ISN_GETITEM:
|
||||||
|
case ISN_GET_OBJ_MEMBER:
|
||||||
case ISN_JUMP:
|
case ISN_JUMP:
|
||||||
case ISN_JUMP_IF_ARG_SET:
|
case ISN_JUMP_IF_ARG_SET:
|
||||||
case ISN_LISTAPPEND:
|
case ISN_LISTAPPEND:
|
||||||
@ -2477,7 +2497,6 @@ delete_instr(isn_T *isn)
|
|||||||
case ISN_NEWDICT:
|
case ISN_NEWDICT:
|
||||||
case ISN_NEWLIST:
|
case ISN_NEWLIST:
|
||||||
case ISN_NEWPARTIAL:
|
case ISN_NEWPARTIAL:
|
||||||
case ISN_OBJ_MEMBER:
|
|
||||||
case ISN_OPANY:
|
case ISN_OPANY:
|
||||||
case ISN_OPFLOAT:
|
case ISN_OPFLOAT:
|
||||||
case ISN_OPNR:
|
case ISN_OPNR:
|
||||||
@ -2495,8 +2514,8 @@ delete_instr(isn_T *isn)
|
|||||||
case ISN_REDIREND:
|
case ISN_REDIREND:
|
||||||
case ISN_REDIRSTART:
|
case ISN_REDIRSTART:
|
||||||
case ISN_RETURN:
|
case ISN_RETURN:
|
||||||
case ISN_RETURN_VOID:
|
|
||||||
case ISN_RETURN_OBJECT:
|
case ISN_RETURN_OBJECT:
|
||||||
|
case ISN_RETURN_VOID:
|
||||||
case ISN_SHUFFLE:
|
case ISN_SHUFFLE:
|
||||||
case ISN_SLICE:
|
case ISN_SLICE:
|
||||||
case ISN_SOURCE:
|
case ISN_SOURCE:
|
||||||
@ -2504,6 +2523,7 @@ delete_instr(isn_T *isn)
|
|||||||
case ISN_STOREINDEX:
|
case ISN_STOREINDEX:
|
||||||
case ISN_STORENR:
|
case ISN_STORENR:
|
||||||
case ISN_STOREOUTER:
|
case ISN_STOREOUTER:
|
||||||
|
case ISN_STORE_THIS:
|
||||||
case ISN_STORERANGE:
|
case ISN_STORERANGE:
|
||||||
case ISN_STOREREG:
|
case ISN_STOREREG:
|
||||||
case ISN_STOREV:
|
case ISN_STOREV:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user