forked from aniani/vim
patch 9.0.1041: cannot define a method in a class
Problem: Cannot define a method in a class.
Solution: Implement defining an object method. Make calling an object
method work.
This commit is contained in:
@@ -3370,4 +3370,6 @@ EXTERN char e_method_not_found_on_class_str_str[]
|
|||||||
INIT(= N_("E1325: Method not found on class \"%s\": %s"));
|
INIT(= N_("E1325: Method not found on class \"%s\": %s"));
|
||||||
EXTERN char e_member_not_found_on_object_str_str[]
|
EXTERN char e_member_not_found_on_object_str_str[]
|
||||||
INIT(= N_("E1326: Member not found on object \"%s\": %s"));
|
INIT(= N_("E1326: Member not found on object \"%s\": %s"));
|
||||||
|
EXTERN char e_object_required_found_str[]
|
||||||
|
INIT(= N_("E1327: Object required, found %s"));
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -295,7 +295,7 @@ eval_expr_typval(
|
|||||||
|
|
||||||
// Shortcut to call a compiled function with minimal overhead.
|
// Shortcut to call a compiled function with minimal overhead.
|
||||||
r = call_def_function(partial->pt_func, argc, argv,
|
r = call_def_function(partial->pt_func, argc, argv,
|
||||||
DEF_USE_PT_ARGV, partial, fc, rettv);
|
DEF_USE_PT_ARGV, partial, NULL, fc, rettv);
|
||||||
if (fc_arg == NULL)
|
if (fc_arg == NULL)
|
||||||
remove_funccal();
|
remove_funccal();
|
||||||
if (r == FAIL)
|
if (r == FAIL)
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ char_u *get_scriptlocal_funcname(char_u *funcname);
|
|||||||
char_u *alloc_printable_func_name(char_u *fname);
|
char_u *alloc_printable_func_name(char_u *fname);
|
||||||
char_u *save_function_name(char_u **name, int *is_global, int skip, int flags, funcdict_T *fudi);
|
char_u *save_function_name(char_u **name, int *is_global, int skip, int flags, funcdict_T *fudi);
|
||||||
void list_functions(regmatch_T *regmatch);
|
void list_functions(regmatch_T *regmatch);
|
||||||
ufunc_T *define_function(exarg_T *eap, char_u *name_arg, garray_T *lines_to_free, class_T *class_arg);
|
ufunc_T *define_function(exarg_T *eap, char_u *name_arg, garray_T *lines_to_free, int in_class);
|
||||||
void ex_function(exarg_T *eap);
|
void ex_function(exarg_T *eap);
|
||||||
ufunc_T *find_func_by_name(char_u *name, compiletype_T *compile_type);
|
ufunc_T *find_func_by_name(char_u *name, compiletype_T *compile_type);
|
||||||
void ex_defcompile(exarg_T *eap);
|
void ex_defcompile(exarg_T *eap);
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ void restore_current_ectx(ectx_T *ectx);
|
|||||||
int add_defer_function(char_u *name, int argcount, typval_T *argvars);
|
int add_defer_function(char_u *name, int argcount, typval_T *argvars);
|
||||||
char_u *char_from_string(char_u *str, varnumber_T index);
|
char_u *char_from_string(char_u *str, varnumber_T index);
|
||||||
char_u *string_slice(char_u *str, varnumber_T first, varnumber_T last, int exclusive);
|
char_u *string_slice(char_u *str, varnumber_T first, varnumber_T last, int exclusive);
|
||||||
int fill_partial_and_closure(partial_T *pt, ufunc_T *ufunc, loopvarinfo_T *loopvarinfo, ectx_T *ectx);
|
int fill_partial_and_closure(partial_T *pt, ufunc_T *ufunc, loopvarinfo_T *lvi, ectx_T *ectx);
|
||||||
int may_load_script(int sid, int *loaded);
|
int may_load_script(int sid, int *loaded);
|
||||||
typval_T *lookup_debug_var(char_u *name);
|
typval_T *lookup_debug_var(char_u *name);
|
||||||
int may_break_in_function(ufunc_T *ufunc);
|
int may_break_in_function(ufunc_T *ufunc);
|
||||||
@@ -17,7 +17,7 @@ int loopvars_check_refcount(loopvars_T *loopvars);
|
|||||||
int set_ref_in_loopvars(int copyID);
|
int set_ref_in_loopvars(int copyID);
|
||||||
int exe_typval_instr(typval_T *tv, typval_T *rettv);
|
int exe_typval_instr(typval_T *tv, typval_T *rettv);
|
||||||
char_u *exe_substitute_instr(void);
|
char_u *exe_substitute_instr(void);
|
||||||
int call_def_function(ufunc_T *ufunc, int argc_arg, typval_T *argv, int flags, partial_T *partial, funccall_T *funccal, typval_T *rettv);
|
int call_def_function(ufunc_T *ufunc, int argc_arg, typval_T *argv, int flags, partial_T *partial, object_T *object, funccall_T *funccal, typval_T *rettv);
|
||||||
void unwind_def_callstack(ectx_T *ectx);
|
void unwind_def_callstack(ectx_T *ectx);
|
||||||
void may_invoke_defer_funcs(ectx_T *ectx);
|
void may_invoke_defer_funcs(ectx_T *ectx);
|
||||||
void set_context_in_disassemble_cmd(expand_T *xp, char_u *arg);
|
void set_context_in_disassemble_cmd(expand_T *xp, char_u *arg);
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ 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 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);
|
||||||
|
|||||||
@@ -1478,15 +1478,16 @@ struct class_S
|
|||||||
|
|
||||||
int class_obj_method_count;
|
int class_obj_method_count;
|
||||||
ufunc_T **class_obj_methods; // allocated
|
ufunc_T **class_obj_methods; // allocated
|
||||||
ufunc_T *class_new_func; // new() function that was created
|
|
||||||
|
|
||||||
garray_T class_type_list; // used for type pointers
|
garray_T class_type_list; // used for type pointers
|
||||||
type_T class_type;
|
type_T class_type;
|
||||||
|
type_T class_object_type; // same as class_type but VAR_OBJECT
|
||||||
};
|
};
|
||||||
|
|
||||||
// Used for v_object of typval of VAR_OBJECT.
|
// Used for v_object of typval of VAR_OBJECT.
|
||||||
// The member variables follow in an array of typval_T.
|
// The member variables follow in an array of typval_T.
|
||||||
struct object_S {
|
struct object_S
|
||||||
|
{
|
||||||
class_T *obj_class; // class this object is created for;
|
class_T *obj_class; // class this object is created for;
|
||||||
// pointer adds to class_refcount
|
// pointer adds to class_refcount
|
||||||
int obj_refcount;
|
int obj_refcount;
|
||||||
@@ -2123,6 +2124,7 @@ typedef struct {
|
|||||||
int fe_evaluate; // actually evaluate expressions
|
int fe_evaluate; // actually evaluate expressions
|
||||||
partial_T *fe_partial; // for extra arguments
|
partial_T *fe_partial; // for extra arguments
|
||||||
dict_T *fe_selfdict; // Dictionary for "self"
|
dict_T *fe_selfdict; // Dictionary for "self"
|
||||||
|
object_T *fe_object; // object, e.g. for "this.Func()"
|
||||||
typval_T *fe_basetv; // base for base->method()
|
typval_T *fe_basetv; // base for base->method()
|
||||||
type_T *fe_check_type; // type from funcref or NULL
|
type_T *fe_check_type; // type from funcref or NULL
|
||||||
int fe_found_var; // if the function is not found then give an
|
int fe_found_var; // if the function is not found then give an
|
||||||
|
|||||||
@@ -130,12 +130,19 @@ def Test_class_basic()
|
|||||||
class TextPosition
|
class TextPosition
|
||||||
this.lnum: number
|
this.lnum: number
|
||||||
this.col: number
|
this.col: number
|
||||||
|
|
||||||
|
def ToString(): string
|
||||||
|
return $'({this.lnum}, {this.col})'
|
||||||
|
enddef
|
||||||
endclass
|
endclass
|
||||||
|
|
||||||
# use the automatically generated new() method
|
# use the automatically generated new() method
|
||||||
var pos = TextPosition.new(2, 12)
|
var pos = TextPosition.new(2, 12)
|
||||||
assert_equal(2, pos.lnum)
|
assert_equal(2, pos.lnum)
|
||||||
assert_equal(12, pos.col)
|
assert_equal(12, pos.col)
|
||||||
|
|
||||||
|
# call an object method
|
||||||
|
assert_equal('(2, 12)', pos.ToString())
|
||||||
END
|
END
|
||||||
v9.CheckScriptSuccess(lines)
|
v9.CheckScriptSuccess(lines)
|
||||||
enddef
|
enddef
|
||||||
|
|||||||
@@ -188,6 +188,7 @@ get_function_line(
|
|||||||
if (theline != NULL)
|
if (theline != NULL)
|
||||||
{
|
{
|
||||||
if (lines_to_free->ga_len > 0
|
if (lines_to_free->ga_len > 0
|
||||||
|
&& eap->cmdlinep != NULL
|
||||||
&& *eap->cmdlinep == ((char_u **)lines_to_free->ga_data)
|
&& *eap->cmdlinep == ((char_u **)lines_to_free->ga_data)
|
||||||
[lines_to_free->ga_len - 1])
|
[lines_to_free->ga_len - 1])
|
||||||
*eap->cmdlinep = theline;
|
*eap->cmdlinep = theline;
|
||||||
@@ -214,7 +215,7 @@ get_function_args(
|
|||||||
garray_T *default_args,
|
garray_T *default_args,
|
||||||
int skip,
|
int skip,
|
||||||
exarg_T *eap, // can be NULL
|
exarg_T *eap, // can be NULL
|
||||||
class_T *class_arg,
|
int in_class, // TRUE when inside a class
|
||||||
garray_T *newlines, // function body lines
|
garray_T *newlines, // function body lines
|
||||||
garray_T *lines_to_free)
|
garray_T *lines_to_free)
|
||||||
{
|
{
|
||||||
@@ -294,7 +295,7 @@ get_function_args(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (class_arg != NULL && STRNCMP(p, "this.", 5) == 0)
|
else if (in_class && STRNCMP(p, "this.", 5) == 0)
|
||||||
{
|
{
|
||||||
// this.memberName
|
// this.memberName
|
||||||
p += 5;
|
p += 5;
|
||||||
@@ -1436,7 +1437,7 @@ get_lambda_tv(
|
|||||||
s = *arg + 1;
|
s = *arg + 1;
|
||||||
ret = get_function_args(&s, equal_arrow ? ')' : '-', NULL,
|
ret = get_function_args(&s, equal_arrow ? ')' : '-', NULL,
|
||||||
types_optional ? &argtypes : NULL, types_optional, evalarg,
|
types_optional ? &argtypes : NULL, types_optional, evalarg,
|
||||||
NULL, &default_args, TRUE, NULL, NULL, NULL, NULL);
|
NULL, &default_args, TRUE, NULL, FALSE, NULL, NULL);
|
||||||
if (ret == FAIL || skip_arrow(s, equal_arrow, &ret_type, NULL) == NULL)
|
if (ret == FAIL || skip_arrow(s, equal_arrow, &ret_type, NULL) == NULL)
|
||||||
{
|
{
|
||||||
if (types_optional)
|
if (types_optional)
|
||||||
@@ -1453,7 +1454,7 @@ get_lambda_tv(
|
|||||||
ret = get_function_args(arg, equal_arrow ? ')' : '-', pnewargs,
|
ret = get_function_args(arg, equal_arrow ? ')' : '-', pnewargs,
|
||||||
types_optional ? &argtypes : NULL, types_optional, evalarg,
|
types_optional ? &argtypes : NULL, types_optional, evalarg,
|
||||||
&varargs, &default_args,
|
&varargs, &default_args,
|
||||||
FALSE, NULL, NULL, NULL, NULL);
|
FALSE, NULL, FALSE, NULL, NULL);
|
||||||
if (ret == FAIL
|
if (ret == FAIL
|
||||||
|| (s = skip_arrow(*arg, equal_arrow, &ret_type,
|
|| (s = skip_arrow(*arg, equal_arrow, &ret_type,
|
||||||
equal_arrow || vim9script ? &white_error : NULL)) == NULL)
|
equal_arrow || vim9script ? &white_error : NULL)) == NULL)
|
||||||
@@ -2733,8 +2734,8 @@ call_user_func(
|
|||||||
profile_may_start_func(&profile_info, fp, caller);
|
profile_may_start_func(&profile_info, fp, caller);
|
||||||
#endif
|
#endif
|
||||||
sticky_cmdmod_flags = 0;
|
sticky_cmdmod_flags = 0;
|
||||||
call_def_function(fp, argcount, argvars, 0, funcexe->fe_partial,
|
call_def_function(fp, argcount, argvars, 0,
|
||||||
fc, rettv);
|
funcexe->fe_partial, funcexe->fe_object, fc, rettv);
|
||||||
funcdepth_decrement();
|
funcdepth_decrement();
|
||||||
#ifdef FEAT_PROFILE
|
#ifdef FEAT_PROFILE
|
||||||
if (do_profiling == PROF_YES && (fp->uf_profiling
|
if (do_profiling == PROF_YES && (fp->uf_profiling
|
||||||
@@ -3957,6 +3958,7 @@ list_func_head(ufunc_T *fp, int indent)
|
|||||||
* "is_global" is NULL.
|
* "is_global" is NULL.
|
||||||
* flags:
|
* flags:
|
||||||
* TFN_INT: internal function name OK
|
* TFN_INT: internal function name OK
|
||||||
|
* TFN_IN_CLASS: function in a class
|
||||||
* TFN_QUIET: be quiet
|
* TFN_QUIET: be quiet
|
||||||
* TFN_NO_AUTOLOAD: do not use script autoloading
|
* TFN_NO_AUTOLOAD: do not use script autoloading
|
||||||
* TFN_NO_DEREF: do not dereference a Funcref
|
* TFN_NO_DEREF: do not dereference a Funcref
|
||||||
@@ -4172,8 +4174,9 @@ trans_function_name(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// In Vim9 script a user function is script-local by default, unless it
|
// In Vim9 script a user function is script-local by default, unless it
|
||||||
// starts with a lower case character: dict.func().
|
// starts with a lower case character: dict.func(). Or when in a class.
|
||||||
vim9_local = ASCII_ISUPPER(*start) && vim9script;
|
vim9_local = ASCII_ISUPPER(*start) && vim9script
|
||||||
|
&& (flags & TFN_IN_CLASS) == 0;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copy the function name to allocated memory.
|
* Copy the function name to allocated memory.
|
||||||
@@ -4211,8 +4214,10 @@ trans_function_name(
|
|||||||
lead += (int)STRLEN(sid_buf);
|
lead += (int)STRLEN(sid_buf);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (!(flags & TFN_INT) && (builtin_function(lv.ll_name, len)
|
else if (!(flags & TFN_INT)
|
||||||
|| (vim9script && *lv.ll_name == '_')))
|
&& (builtin_function(lv.ll_name, len)
|
||||||
|
|| (vim9script && *lv.ll_name == '_'))
|
||||||
|
&& !((flags & TFN_IN_CLASS) && STRNCMP(lv.ll_name, "new", 3) == 0))
|
||||||
{
|
{
|
||||||
semsg(_(vim9script ? e_function_name_must_start_with_capital_str
|
semsg(_(vim9script ? e_function_name_must_start_with_capital_str
|
||||||
: e_function_name_must_start_with_capital_or_s_str),
|
: e_function_name_must_start_with_capital_or_s_str),
|
||||||
@@ -4417,7 +4422,7 @@ list_functions(regmatch_T *regmatch)
|
|||||||
* When "name_arg" is not NULL this is a nested function, using "name_arg" for
|
* When "name_arg" is not NULL this is a nested function, using "name_arg" for
|
||||||
* the function name.
|
* the function name.
|
||||||
* "lines_to_free" is a list of strings to be freed later.
|
* "lines_to_free" is a list of strings to be freed later.
|
||||||
* If "class_arg" is not NULL then the function is defined in this class.
|
* If "in_class" is TRUE then the function is defined inside a class.
|
||||||
* Returns a pointer to the function or NULL if no function defined.
|
* Returns a pointer to the function or NULL if no function defined.
|
||||||
*/
|
*/
|
||||||
ufunc_T *
|
ufunc_T *
|
||||||
@@ -4425,7 +4430,7 @@ define_function(
|
|||||||
exarg_T *eap,
|
exarg_T *eap,
|
||||||
char_u *name_arg,
|
char_u *name_arg,
|
||||||
garray_T *lines_to_free,
|
garray_T *lines_to_free,
|
||||||
class_T *class_arg)
|
int in_class)
|
||||||
{
|
{
|
||||||
int j;
|
int j;
|
||||||
int c;
|
int c;
|
||||||
@@ -4500,7 +4505,7 @@ define_function(
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* Get the function name. There are these situations:
|
* Get the function name. There are these situations:
|
||||||
* func normal function name
|
* func normal function name, also when "in_class" is TRUE
|
||||||
* "name" == func, "fudi.fd_dict" == NULL
|
* "name" == func, "fudi.fd_dict" == NULL
|
||||||
* dict.func new dictionary entry
|
* dict.func new dictionary entry
|
||||||
* "name" == NULL, "fudi.fd_dict" set,
|
* "name" == NULL, "fudi.fd_dict" set,
|
||||||
@@ -4541,7 +4546,7 @@ define_function(
|
|||||||
}
|
}
|
||||||
|
|
||||||
int tfn_flags = TFN_NO_AUTOLOAD | TFN_NEW_FUNC
|
int tfn_flags = TFN_NO_AUTOLOAD | TFN_NEW_FUNC
|
||||||
| (class_arg == 0 ? 0 : TFN_INT);
|
| (in_class ? TFN_IN_CLASS : 0);
|
||||||
name = save_function_name(&p, &is_global, eap->skip, tfn_flags, &fudi);
|
name = save_function_name(&p, &is_global, eap->skip, tfn_flags, &fudi);
|
||||||
paren = (vim_strchr(p, '(') != NULL);
|
paren = (vim_strchr(p, '(') != NULL);
|
||||||
if (name == NULL && (fudi.fd_dict == NULL || !paren) && !eap->skip)
|
if (name == NULL && (fudi.fd_dict == NULL || !paren) && !eap->skip)
|
||||||
@@ -4743,7 +4748,7 @@ define_function(
|
|||||||
if (get_function_args(&p, ')', &newargs,
|
if (get_function_args(&p, ')', &newargs,
|
||||||
eap->cmdidx == CMD_def ? &argtypes : NULL, FALSE,
|
eap->cmdidx == CMD_def ? &argtypes : NULL, FALSE,
|
||||||
NULL, &varargs, &default_args, eap->skip,
|
NULL, &varargs, &default_args, eap->skip,
|
||||||
eap, class_arg, &newlines, lines_to_free) == FAIL)
|
eap, in_class, &newlines, lines_to_free) == FAIL)
|
||||||
goto errret_2;
|
goto errret_2;
|
||||||
whitep = p;
|
whitep = p;
|
||||||
|
|
||||||
@@ -4860,7 +4865,35 @@ define_function(
|
|||||||
/*
|
/*
|
||||||
* If there are no errors, add the function
|
* If there are no errors, add the function
|
||||||
*/
|
*/
|
||||||
if (fudi.fd_dict == NULL)
|
if (fudi.fd_dict != NULL)
|
||||||
|
{
|
||||||
|
char numbuf[20];
|
||||||
|
|
||||||
|
fp = NULL;
|
||||||
|
if (fudi.fd_newkey == NULL && !eap->forceit)
|
||||||
|
{
|
||||||
|
emsg(_(e_dictionary_entry_already_exists));
|
||||||
|
goto erret;
|
||||||
|
}
|
||||||
|
if (fudi.fd_di == NULL)
|
||||||
|
{
|
||||||
|
// Can't add a function to a locked dictionary
|
||||||
|
if (value_check_lock(fudi.fd_dict->dv_lock, eap->arg, FALSE))
|
||||||
|
goto erret;
|
||||||
|
}
|
||||||
|
// Can't change an existing function if it is locked
|
||||||
|
else if (value_check_lock(fudi.fd_di->di_tv.v_lock, eap->arg, FALSE))
|
||||||
|
goto erret;
|
||||||
|
|
||||||
|
// Give the function a sequential number. Can only be used with a
|
||||||
|
// Funcref!
|
||||||
|
vim_free(name);
|
||||||
|
sprintf(numbuf, "%d", ++func_nr);
|
||||||
|
name = vim_strsave((char_u *)numbuf);
|
||||||
|
if (name == NULL)
|
||||||
|
goto erret;
|
||||||
|
}
|
||||||
|
else if (!in_class)
|
||||||
{
|
{
|
||||||
hashtab_T *ht;
|
hashtab_T *ht;
|
||||||
char_u *find_name = name;
|
char_u *find_name = name;
|
||||||
@@ -4967,34 +5000,6 @@ define_function(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
char numbuf[20];
|
|
||||||
|
|
||||||
fp = NULL;
|
|
||||||
if (fudi.fd_newkey == NULL && !eap->forceit)
|
|
||||||
{
|
|
||||||
emsg(_(e_dictionary_entry_already_exists));
|
|
||||||
goto erret;
|
|
||||||
}
|
|
||||||
if (fudi.fd_di == NULL)
|
|
||||||
{
|
|
||||||
// Can't add a function to a locked dictionary
|
|
||||||
if (value_check_lock(fudi.fd_dict->dv_lock, eap->arg, FALSE))
|
|
||||||
goto erret;
|
|
||||||
}
|
|
||||||
// Can't change an existing function if it is locked
|
|
||||||
else if (value_check_lock(fudi.fd_di->di_tv.v_lock, eap->arg, FALSE))
|
|
||||||
goto erret;
|
|
||||||
|
|
||||||
// Give the function a sequential number. Can only be used with a
|
|
||||||
// Funcref!
|
|
||||||
vim_free(name);
|
|
||||||
sprintf(numbuf, "%d", ++func_nr);
|
|
||||||
name = vim_strsave((char_u *)numbuf);
|
|
||||||
if (name == NULL)
|
|
||||||
goto erret;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fp == NULL)
|
if (fp == NULL)
|
||||||
{
|
{
|
||||||
@@ -5113,7 +5118,8 @@ define_function(
|
|||||||
hi = hash_find(&func_hashtab, name);
|
hi = hash_find(&func_hashtab, name);
|
||||||
hi->hi_key = UF2HIKEY(fp);
|
hi->hi_key = UF2HIKEY(fp);
|
||||||
}
|
}
|
||||||
else if (hash_add(&func_hashtab, UF2HIKEY(fp), "add function") == FAIL)
|
else if (!in_class && hash_add(&func_hashtab,
|
||||||
|
UF2HIKEY(fp), "add function") == FAIL)
|
||||||
{
|
{
|
||||||
free_fp = TRUE;
|
free_fp = TRUE;
|
||||||
goto erret;
|
goto erret;
|
||||||
@@ -5198,7 +5204,7 @@ ex_function(exarg_T *eap)
|
|||||||
garray_T lines_to_free;
|
garray_T lines_to_free;
|
||||||
|
|
||||||
ga_init2(&lines_to_free, sizeof(char_u *), 50);
|
ga_init2(&lines_to_free, sizeof(char_u *), 50);
|
||||||
(void)define_function(eap, NULL, &lines_to_free, NULL);
|
(void)define_function(eap, NULL, &lines_to_free, FALSE);
|
||||||
ga_clear_strings(&lines_to_free);
|
ga_clear_strings(&lines_to_free);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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 */
|
||||||
|
/**/
|
||||||
|
1041,
|
||||||
/**/
|
/**/
|
||||||
1040,
|
1040,
|
||||||
/**/
|
/**/
|
||||||
|
|||||||
@@ -2676,7 +2676,8 @@ typedef enum {
|
|||||||
#define TFN_NO_DECL 0x20 // only used for GLV_NO_DECL
|
#define TFN_NO_DECL 0x20 // only used for GLV_NO_DECL
|
||||||
#define TFN_COMPILING 0x40 // only used for GLV_COMPILING
|
#define TFN_COMPILING 0x40 // only used for GLV_COMPILING
|
||||||
#define TFN_NEW_FUNC 0x80 // defining a new function
|
#define TFN_NEW_FUNC 0x80 // defining a new function
|
||||||
#define TFN_ASSIGN_WITH_OP 0x100 // only for GLV_ASSIGN_WITH_OP
|
#define TFN_ASSIGN_WITH_OP 0x100 // only for GLV_ASSIGN_WITH_OP
|
||||||
|
#define TFN_IN_CLASS 0x200 // function in a class
|
||||||
|
|
||||||
// Values for get_lval() flags argument:
|
// Values for get_lval() flags argument:
|
||||||
#define GLV_QUIET TFN_QUIET // no error messages
|
#define GLV_QUIET TFN_QUIET // no error messages
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ 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
|
||||||
|
|
||||||
// get and set variables
|
// get and set variables
|
||||||
ISN_LOAD, // push local variable isn_arg.number
|
ISN_LOAD, // push local variable isn_arg.number
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ ex_class(exarg_T *eap)
|
|||||||
|
|
||||||
// Growarray with object methods declared in the class.
|
// Growarray with object methods declared in the class.
|
||||||
garray_T objmethods;
|
garray_T objmethods;
|
||||||
ga_init2(&objmethods, sizeof(ufunc_T), 10);
|
ga_init2(&objmethods, sizeof(ufunc_T *), 10);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Go over the body of the class until "endclass" is found.
|
* Go over the body of the class until "endclass" is found.
|
||||||
@@ -97,22 +97,6 @@ ex_class(exarg_T *eap)
|
|||||||
// static varname
|
// static varname
|
||||||
// public static varname
|
// public static varname
|
||||||
// static _varname
|
// static _varname
|
||||||
//
|
|
||||||
// constructors:
|
|
||||||
// def new()
|
|
||||||
// enddef
|
|
||||||
// def newOther()
|
|
||||||
// enddef
|
|
||||||
//
|
|
||||||
// methods (object, class, generics):
|
|
||||||
// def someMethod()
|
|
||||||
// enddef
|
|
||||||
// static def someMethod()
|
|
||||||
// enddef
|
|
||||||
// def <Tval> someMethod()
|
|
||||||
// enddef
|
|
||||||
// static def <Tval> someMethod()
|
|
||||||
// enddef
|
|
||||||
|
|
||||||
char_u *p = line;
|
char_u *p = line;
|
||||||
if (checkforcmd(&p, "endclass", 4))
|
if (checkforcmd(&p, "endclass", 4))
|
||||||
@@ -172,6 +156,45 @@ ex_class(exarg_T *eap)
|
|||||||
++objmembers.ga_len;
|
++objmembers.ga_len;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// constructors:
|
||||||
|
// def new()
|
||||||
|
// enddef
|
||||||
|
// def newOther()
|
||||||
|
// enddef
|
||||||
|
// methods:
|
||||||
|
// 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;
|
||||||
|
garray_T lines_to_free;
|
||||||
|
|
||||||
|
CLEAR_FIELD(ea);
|
||||||
|
ea.cmd = line;
|
||||||
|
ea.arg = p;
|
||||||
|
ea.cmdidx = CMD_def;
|
||||||
|
ea.getline = eap->getline;
|
||||||
|
ea.cookie = eap->cookie;
|
||||||
|
|
||||||
|
ga_init2(&lines_to_free, sizeof(char_u *), 50);
|
||||||
|
ufunc_T *uf = define_function(&ea, NULL, &lines_to_free, TRUE);
|
||||||
|
ga_clear_strings(&lines_to_free);
|
||||||
|
|
||||||
|
// TODO: how about errors?
|
||||||
|
if (uf != NULL && ga_grow(&objmethods, 1) == OK)
|
||||||
|
{
|
||||||
|
((ufunc_T **)objmethods.ga_data)[objmethods.ga_len] = uf;
|
||||||
|
++objmethods.ga_len;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
semsg(_(e_not_valid_command_in_class_str), line);
|
semsg(_(e_not_valid_command_in_class_str), line);
|
||||||
@@ -206,7 +229,7 @@ ex_class(exarg_T *eap)
|
|||||||
|
|
||||||
int have_new = FALSE;
|
int have_new = FALSE;
|
||||||
for (int i = 0; i < objmethods.ga_len; ++i)
|
for (int i = 0; i < objmethods.ga_len; ++i)
|
||||||
if (STRCMP((((ufunc_T *)objmethods.ga_data) + i)->uf_name,
|
if (STRCMP(((ufunc_T **)objmethods.ga_data)[i]->uf_name,
|
||||||
"new") == 0)
|
"new") == 0)
|
||||||
{
|
{
|
||||||
have_new = TRUE;
|
have_new = TRUE;
|
||||||
@@ -237,7 +260,7 @@ ex_class(exarg_T *eap)
|
|||||||
garray_T lines_to_free;
|
garray_T lines_to_free;
|
||||||
ga_init2(&lines_to_free, sizeof(char_u *), 50);
|
ga_init2(&lines_to_free, sizeof(char_u *), 50);
|
||||||
|
|
||||||
ufunc_T *nf = define_function(&fea, NULL, &lines_to_free, cl);
|
ufunc_T *nf = define_function(&fea, NULL, &lines_to_free, TRUE);
|
||||||
|
|
||||||
ga_clear_strings(&lines_to_free);
|
ga_clear_strings(&lines_to_free);
|
||||||
vim_free(fga.ga_data);
|
vim_free(fga.ga_data);
|
||||||
@@ -248,7 +271,6 @@ ex_class(exarg_T *eap)
|
|||||||
++objmethods.ga_len;
|
++objmethods.ga_len;
|
||||||
|
|
||||||
nf->uf_flags |= FC_NEW;
|
nf->uf_flags |= FC_NEW;
|
||||||
nf->uf_class = cl;
|
|
||||||
nf->uf_ret_type = get_type_ptr(&type_list);
|
nf->uf_ret_type = get_type_ptr(&type_list);
|
||||||
if (nf->uf_ret_type != NULL)
|
if (nf->uf_ret_type != NULL)
|
||||||
{
|
{
|
||||||
@@ -257,7 +279,6 @@ ex_class(exarg_T *eap)
|
|||||||
nf->uf_ret_type->tt_argcount = 0;
|
nf->uf_ret_type->tt_argcount = 0;
|
||||||
nf->uf_ret_type->tt_args = NULL;
|
nf->uf_ret_type->tt_args = NULL;
|
||||||
}
|
}
|
||||||
cl->class_new_func = nf;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -275,8 +296,18 @@ ex_class(exarg_T *eap)
|
|||||||
sizeof(ufunc_T *) * objmethods.ga_len);
|
sizeof(ufunc_T *) * objmethods.ga_len);
|
||||||
vim_free(objmethods.ga_data);
|
vim_free(objmethods.ga_data);
|
||||||
|
|
||||||
|
// Set the class pointer on all the object methods.
|
||||||
|
for (int i = 0; i < objmethods.ga_len; ++i)
|
||||||
|
{
|
||||||
|
ufunc_T *fp = cl->class_obj_methods[i];
|
||||||
|
fp->uf_class = cl;
|
||||||
|
fp->uf_flags |= FC_OBJECT; // TODO: not for class method
|
||||||
|
}
|
||||||
|
|
||||||
cl->class_type.tt_type = VAR_CLASS;
|
cl->class_type.tt_type = VAR_CLASS;
|
||||||
cl->class_type.tt_member = (type_T *)cl;
|
cl->class_type.tt_member = (type_T *)cl;
|
||||||
|
cl->class_object_type.tt_type = VAR_OBJECT;
|
||||||
|
cl->class_object_type.tt_member = (type_T *)cl;
|
||||||
cl->class_type_list = type_list;
|
cl->class_type_list = type_list;
|
||||||
|
|
||||||
// TODO:
|
// TODO:
|
||||||
@@ -305,6 +336,11 @@ cleanup:
|
|||||||
}
|
}
|
||||||
ga_clear(&objmembers);
|
ga_clear(&objmembers);
|
||||||
|
|
||||||
|
for (int i = 0; i < objmethods.ga_len; ++i)
|
||||||
|
{
|
||||||
|
ufunc_T *uf = ((ufunc_T **)objmethods.ga_data)[i];
|
||||||
|
func_clear_free(uf, FALSE);
|
||||||
|
}
|
||||||
ga_clear(&objmethods);
|
ga_clear(&objmethods);
|
||||||
clear_type_list(&type_list);
|
clear_type_list(&type_list);
|
||||||
}
|
}
|
||||||
@@ -419,6 +455,11 @@ class_object_index(
|
|||||||
funcexe_T funcexe;
|
funcexe_T funcexe;
|
||||||
CLEAR_FIELD(funcexe);
|
CLEAR_FIELD(funcexe);
|
||||||
funcexe.fe_evaluate = TRUE;
|
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
|
// Clear the class or object after calling the function, in
|
||||||
// case the refcount is one.
|
// case the refcount is one.
|
||||||
@@ -426,7 +467,6 @@ class_object_index(
|
|||||||
rettv->v_type = VAR_UNKNOWN;
|
rettv->v_type = VAR_UNKNOWN;
|
||||||
|
|
||||||
// Call the user function. Result goes into rettv;
|
// Call the user function. Result goes into rettv;
|
||||||
// TODO: pass the object
|
|
||||||
int error = call_user_func_check(fp, argcount, argvars,
|
int error = call_user_func_check(fp, argcount, argvars,
|
||||||
rettv, &funcexe, NULL);
|
rettv, &funcexe, NULL);
|
||||||
|
|
||||||
@@ -545,11 +585,13 @@ class_unref(class_T *cl)
|
|||||||
}
|
}
|
||||||
vim_free(cl->class_obj_members);
|
vim_free(cl->class_obj_members);
|
||||||
|
|
||||||
|
for (int i = 0; i < cl->class_obj_method_count; ++i)
|
||||||
|
{
|
||||||
|
ufunc_T *uf = cl->class_obj_methods[i];
|
||||||
|
func_clear_free(uf, FALSE);
|
||||||
|
}
|
||||||
vim_free(cl->class_obj_methods);
|
vim_free(cl->class_obj_methods);
|
||||||
|
|
||||||
if (cl->class_new_func != NULL)
|
|
||||||
func_ptr_unref(cl->class_new_func);
|
|
||||||
|
|
||||||
clear_type_list(&cl->class_type_list);
|
clear_type_list(&cl->class_type_list);
|
||||||
|
|
||||||
vim_free(cl);
|
vim_free(cl);
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ lookup_local(char_u *name, size_t len, lvar_T *lvar, cctx_T *cctx)
|
|||||||
CLEAR_POINTER(lvar);
|
CLEAR_POINTER(lvar);
|
||||||
lvar->lv_name = (char_u *)"this";
|
lvar->lv_name = (char_u *)"this";
|
||||||
if (cctx->ctx_ufunc->uf_class != NULL)
|
if (cctx->ctx_ufunc->uf_class != NULL)
|
||||||
lvar->lv_type = &cctx->ctx_ufunc->uf_class->class_type;
|
lvar->lv_type = &cctx->ctx_ufunc->uf_class->class_object_type;
|
||||||
}
|
}
|
||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
@@ -975,7 +975,7 @@ compile_nested_function(exarg_T *eap, cctx_T *cctx, garray_T *lines_to_free)
|
|||||||
goto theend;
|
goto theend;
|
||||||
}
|
}
|
||||||
|
|
||||||
ufunc = define_function(eap, lambda_name, lines_to_free, NULL);
|
ufunc = define_function(eap, lambda_name, lines_to_free, FALSE);
|
||||||
if (ufunc == NULL)
|
if (ufunc == NULL)
|
||||||
{
|
{
|
||||||
r = eap->skip ? OK : FAIL;
|
r = eap->skip ? OK : FAIL;
|
||||||
|
|||||||
@@ -4225,7 +4225,7 @@ exec_instructions(ectx_T *ectx)
|
|||||||
CLEAR_FIELD(ea);
|
CLEAR_FIELD(ea);
|
||||||
ea.cmd = ea.arg = iptr->isn_arg.string;
|
ea.cmd = ea.arg = iptr->isn_arg.string;
|
||||||
ga_init2(&lines_to_free, sizeof(char_u *), 50);
|
ga_init2(&lines_to_free, sizeof(char_u *), 50);
|
||||||
define_function(&ea, NULL, &lines_to_free, NULL);
|
define_function(&ea, NULL, &lines_to_free, FALSE);
|
||||||
ga_clear_strings(&lines_to_free);
|
ga_clear_strings(&lines_to_free);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -5114,6 +5114,35 @@ exec_instructions(ectx_T *ectx)
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case ISN_OBJ_MEMBER:
|
||||||
|
{
|
||||||
|
tv = STACK_TV_BOT(-1);
|
||||||
|
if (tv->v_type != VAR_OBJECT)
|
||||||
|
{
|
||||||
|
SOURCING_LNUM = iptr->isn_lnum;
|
||||||
|
garray_T type_list;
|
||||||
|
ga_init2(&type_list, sizeof(type_T *), 10);
|
||||||
|
type_T *type = typval2type(tv, get_copyID(),
|
||||||
|
&type_list, TVTT_DO_MEMBER);
|
||||||
|
char *tofree = NULL;
|
||||||
|
char *typename = type_name(type, &tofree);
|
||||||
|
semsg(_(e_object_required_found_str), typename);
|
||||||
|
vim_free(tofree);
|
||||||
|
clear_type_list(&type_list);
|
||||||
|
goto on_error;
|
||||||
|
}
|
||||||
|
int idx = iptr->isn_arg.number;
|
||||||
|
object_T *obj = tv->vval.v_object;
|
||||||
|
// the members are located right after the object struct
|
||||||
|
typval_T *mtv = ((typval_T *)(obj + 1)) + idx;
|
||||||
|
*tv = *mtv;
|
||||||
|
|
||||||
|
// Unreference the object after getting the member, it may
|
||||||
|
// be freed.
|
||||||
|
object_unref(obj);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case ISN_CLEARDICT:
|
case ISN_CLEARDICT:
|
||||||
dict_stack_drop();
|
dict_stack_drop();
|
||||||
break;
|
break;
|
||||||
@@ -5577,6 +5606,7 @@ call_def_function(
|
|||||||
typval_T *argv, // arguments
|
typval_T *argv, // arguments
|
||||||
int flags, // DEF_ flags
|
int flags, // DEF_ flags
|
||||||
partial_T *partial, // optional partial for context
|
partial_T *partial, // optional partial for context
|
||||||
|
object_T *object, // object, e.g. for this.Func()
|
||||||
funccall_T *funccal,
|
funccall_T *funccal,
|
||||||
typval_T *rettv) // return value
|
typval_T *rettv) // return value
|
||||||
{
|
{
|
||||||
@@ -5818,6 +5848,15 @@ call_def_function(
|
|||||||
STACK_TV_VAR(idx)->vval.v_number = 0;
|
STACK_TV_VAR(idx)->vval.v_number = 0;
|
||||||
}
|
}
|
||||||
ectx.ec_stack.ga_len += dfunc->df_varcount;
|
ectx.ec_stack.ga_len += dfunc->df_varcount;
|
||||||
|
|
||||||
|
if (object != NULL)
|
||||||
|
{
|
||||||
|
// the object is always the variable at index zero
|
||||||
|
tv = STACK_TV_VAR(0);
|
||||||
|
tv->v_type = VAR_OBJECT;
|
||||||
|
tv->vval.v_object = object;
|
||||||
|
}
|
||||||
|
|
||||||
if (dfunc->df_has_closure)
|
if (dfunc->df_has_closure)
|
||||||
{
|
{
|
||||||
// Initialize the variable that counts how many closures were
|
// Initialize the variable that counts how many closures were
|
||||||
@@ -6766,6 +6805,8 @@ 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,
|
||||||
|
(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;
|
||||||
|
|
||||||
|
|||||||
@@ -250,6 +250,56 @@ compile_member(int is_slice, int *keeping_dict, cctx_T *cctx)
|
|||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Compile ".member" coming after an object or class.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static int
|
||||||
|
compile_class_object_index(cctx_T *cctx, char_u **arg, type_T *type)
|
||||||
|
{
|
||||||
|
if (VIM_ISWHITE((*arg)[1]))
|
||||||
|
{
|
||||||
|
semsg(_(e_no_white_space_allowed_after_str_str), ".", *arg);
|
||||||
|
return FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
++*arg;
|
||||||
|
char_u *name = *arg;
|
||||||
|
char_u *name_end = find_name_end(name, NULL, NULL, FNE_CHECK_START);
|
||||||
|
if (name_end == name)
|
||||||
|
return FAIL;
|
||||||
|
size_t len = name_end - name;
|
||||||
|
|
||||||
|
class_T *cl = (class_T *)type->tt_member;
|
||||||
|
if (*name_end == '(')
|
||||||
|
{
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
else if (type->tt_type == VAR_OBJECT)
|
||||||
|
{
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
generate_OBJ_MEMBER(cctx, i, m->om_type);
|
||||||
|
|
||||||
|
*arg = name_end;
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
semsg(_(e_member_not_found_on_object_str_str), cl->class_name, name);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// TODO: class member
|
||||||
|
emsg("compile_class_object_index(): not handled");
|
||||||
|
}
|
||||||
|
|
||||||
|
return FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Generate an instruction to load script-local variable "name", without the
|
* Generate an instruction to load script-local variable "name", without the
|
||||||
* leading "s:".
|
* leading "s:".
|
||||||
@@ -1797,6 +1847,7 @@ compile_subscript(
|
|||||||
for (;;)
|
for (;;)
|
||||||
{
|
{
|
||||||
char_u *p = skipwhite(*arg);
|
char_u *p = skipwhite(*arg);
|
||||||
|
type_T *type;
|
||||||
|
|
||||||
if (*p == NUL || (VIM_ISWHITE(**arg) && vim9_comment_start(p)))
|
if (*p == NUL || (VIM_ISWHITE(**arg) && vim9_comment_start(p)))
|
||||||
{
|
{
|
||||||
@@ -1824,7 +1875,6 @@ compile_subscript(
|
|||||||
// is not a function call.
|
// is not a function call.
|
||||||
if (**arg == '(')
|
if (**arg == '(')
|
||||||
{
|
{
|
||||||
type_T *type;
|
|
||||||
int argcount = 0;
|
int argcount = 0;
|
||||||
|
|
||||||
if (generate_ppconst(cctx, ppconst) == FAIL)
|
if (generate_ppconst(cctx, ppconst) == FAIL)
|
||||||
@@ -1911,7 +1961,6 @@ compile_subscript(
|
|||||||
int argcount = 1;
|
int argcount = 1;
|
||||||
garray_T *stack = &cctx->ctx_type_stack;
|
garray_T *stack = &cctx->ctx_type_stack;
|
||||||
int type_idx_start = stack->ga_len;
|
int type_idx_start = stack->ga_len;
|
||||||
type_T *type;
|
|
||||||
int expr_isn_start = cctx->ctx_instr.ga_len;
|
int expr_isn_start = cctx->ctx_instr.ga_len;
|
||||||
int expr_isn_end;
|
int expr_isn_end;
|
||||||
int arg_isn_count;
|
int arg_isn_count;
|
||||||
@@ -2097,6 +2146,18 @@ compile_subscript(
|
|||||||
if (compile_member(is_slice, &keeping_dict, cctx) == FAIL)
|
if (compile_member(is_slice, &keeping_dict, cctx) == FAIL)
|
||||||
return FAIL;
|
return FAIL;
|
||||||
}
|
}
|
||||||
|
else if (*p == '.'
|
||||||
|
&& (type = get_type_on_stack(cctx, 0)) != &t_unknown
|
||||||
|
&& (type->tt_type == VAR_CLASS || type->tt_type == VAR_OBJECT))
|
||||||
|
{
|
||||||
|
// class member: SomeClass.varname
|
||||||
|
// class method: SomeClass.SomeMethod()
|
||||||
|
// class constructor: SomeClass.new()
|
||||||
|
// object member: someObject.varname, this.varname
|
||||||
|
// object method: someObject.SomeMethod(), this.SomeMethod()
|
||||||
|
if (compile_class_object_index(cctx, arg, type) == FAIL)
|
||||||
|
return FAIL;
|
||||||
|
}
|
||||||
else if (*p == '.' && p[1] != '.')
|
else if (*p == '.' && p[1] != '.')
|
||||||
{
|
{
|
||||||
// dictionary member: dict.name
|
// dictionary member: dict.name
|
||||||
|
|||||||
@@ -131,6 +131,23 @@ generate_CONSTRUCT(cctx_T *cctx, class_T *cl)
|
|||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Generate ISN_OBJ_MEMBER - access object member by indes.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
generate_OBJ_MEMBER(cctx_T *cctx, int idx, type_T *type)
|
||||||
|
{
|
||||||
|
RETURN_OK_IF_SKIP(cctx);
|
||||||
|
|
||||||
|
// drop the object type
|
||||||
|
isn_T *isn = generate_instr_drop(cctx, ISN_OBJ_MEMBER, 1);
|
||||||
|
if (isn == NULL)
|
||||||
|
return FAIL;
|
||||||
|
|
||||||
|
isn->isn_arg.number = idx;
|
||||||
|
return push_type_stack2(cctx, type, &t_any);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 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.
|
||||||
@@ -2460,6 +2477,7 @@ 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:
|
||||||
|
|||||||
@@ -1585,10 +1585,7 @@ f_typename(typval_T *argvars, typval_T *rettv)
|
|||||||
if (tofree != NULL)
|
if (tofree != NULL)
|
||||||
rettv->vval.v_string = (char_u *)tofree;
|
rettv->vval.v_string = (char_u *)tofree;
|
||||||
else
|
else
|
||||||
{
|
|
||||||
rettv->vval.v_string = vim_strsave((char_u *)name);
|
rettv->vval.v_string = vim_strsave((char_u *)name);
|
||||||
vim_free(tofree);
|
|
||||||
}
|
|
||||||
clear_type_list(&type_list);
|
clear_type_list(&type_list);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user