Problem: Cannot define a method in a class.
Solution: Implement defining an object method. Make calling an object
method work.
INIT(= N_("E1325: Method not found on class \"%s\": %s"));
EXTERN char e_member_not_found_on_object_str_str[]
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
// Shortcut to call a compiled function with minimal overhead.
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)
remove_funccal();
if (r == FAIL)
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);
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);
ufunc_T *find_func_by_name(char_u *name, compiletype_T *compile_type);
void ex_defcompile(exarg_T *eap);
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 *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);
typval_T *lookup_debug_var(char_u *name);
int may_break_in_function(ufunc_T *ufunc);
int set_ref_in_loopvars(int copyID);
int exe_typval_instr(typval_T *tv, typval_T *rettv);
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 may_invoke_defer_funcs(ectx_T *ectx);
void set_context_in_disassemble_cmd(expand_T *xp, char_u *arg);
isn_T *generate_instr_type(cctx_T *cctx, isntype_T isn_type, type_T *type);
isn_T *generate_instr_debug(cctx_T *cctx);
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 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);
int class_obj_method_count;
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
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.
// 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;
// pointer adds to class_refcount
int obj_refcount;
int fe_evaluate; // actually evaluate expressions
partial_T *fe_partial; // for extra arguments
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()
type_T *fe_check_type; // type from funcref or NULL
int fe_found_var; // if the function is not found then give an
class TextPosition
this.lnum: number
this.col: number
+
+ def ToString(): string
+ return $'({this.lnum}, {this.col})'
+ enddef
endclass
# use the automatically generated new() method
var pos = TextPosition.new(2, 12)
assert_equal(2, pos.lnum)
assert_equal(12, pos.col)
+
+ # call an object method
+ assert_equal('(2, 12)', pos.ToString())
END
v9.CheckScriptSuccess(lines)
enddef
if (theline != NULL)
{
if (lines_to_free->ga_len > 0
+ && eap->cmdlinep != NULL
&& *eap->cmdlinep == ((char_u **)lines_to_free->ga_data)
[lines_to_free->ga_len - 1])
*eap->cmdlinep = theline;
garray_T *default_args,
int skip,
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 *lines_to_free)
{
}
}
}
- else if (class_arg != NULL && STRNCMP(p, "this.", 5) == 0)
+ else if (in_class && STRNCMP(p, "this.", 5) == 0)
{
// this.memberName
p += 5;
s = *arg + 1;
ret = get_function_args(&s, equal_arrow ? ')' : '-', NULL,
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 (types_optional)
ret = get_function_args(arg, equal_arrow ? ')' : '-', pnewargs,
types_optional ? &argtypes : NULL, types_optional, evalarg,
&varargs, &default_args,
- FALSE, NULL, NULL, NULL, NULL);
+ FALSE, NULL, FALSE, NULL, NULL);
if (ret == FAIL
|| (s = skip_arrow(*arg, equal_arrow, &ret_type,
equal_arrow || vim9script ? &white_error : NULL)) == NULL)
profile_may_start_func(&profile_info, fp, caller);
#endif
sticky_cmdmod_flags = 0;
- call_def_function(fp, argcount, argvars, 0, funcexe->fe_partial,
- fc, rettv);
+ call_def_function(fp, argcount, argvars, 0,
+ funcexe->fe_partial, funcexe->fe_object, fc, rettv);
funcdepth_decrement();
#ifdef FEAT_PROFILE
if (do_profiling == PROF_YES && (fp->uf_profiling
* "is_global" is NULL.
* flags:
* TFN_INT: internal function name OK
+ * TFN_IN_CLASS: function in a class
* TFN_QUIET: be quiet
* TFN_NO_AUTOLOAD: do not use script autoloading
* TFN_NO_DEREF: do not dereference a Funcref
}
// In Vim9 script a user function is script-local by default, unless it
- // starts with a lower case character: dict.func().
- vim9_local = ASCII_ISUPPER(*start) && vim9script;
+ // starts with a lower case character: dict.func(). Or when in a class.
+ vim9_local = ASCII_ISUPPER(*start) && vim9script
+ && (flags & TFN_IN_CLASS) == 0;
/*
* Copy the function name to allocated memory.
lead += (int)STRLEN(sid_buf);
}
}
- else if (!(flags & TFN_INT) && (builtin_function(lv.ll_name, len)
- || (vim9script && *lv.ll_name == '_')))
+ else if (!(flags & TFN_INT)
+ && (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
: e_function_name_must_start_with_capital_or_s_str),
* When "name_arg" is not NULL this is a nested function, using "name_arg" for
* the function name.
* "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.
*/
ufunc_T *
exarg_T *eap,
char_u *name_arg,
garray_T *lines_to_free,
- class_T *class_arg)
+ int in_class)
{
int j;
int c;
/*
* 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
* dict.func new dictionary entry
* "name" == NULL, "fudi.fd_dict" set,
}
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);
paren = (vim_strchr(p, '(') != NULL);
if (name == NULL && (fudi.fd_dict == NULL || !paren) && !eap->skip)
if (get_function_args(&p, ')', &newargs,
eap->cmdidx == CMD_def ? &argtypes : NULL, FALSE,
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;
whitep = p;
/*
* 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;
char_u *find_name = name;
}
}
}
- 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)
{
hi = hash_find(&func_hashtab, name);
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;
goto erret;
garray_T lines_to_free;
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);
}
static int included_patches[] =
{ /* Add new patch number below this line */
+/**/
+ 1041,
/**/
1040,
/**/
#define TFN_NO_DECL 0x20 // only used for GLV_NO_DECL
#define TFN_COMPILING 0x40 // only used for GLV_COMPILING
#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:
#define GLV_QUIET TFN_QUIET // no error messages
ISN_SOURCE, // source autoload script, isn_arg.number is the script ID
ISN_INSTR, // instructions compiled from expression
ISN_CONSTRUCT, // construct an object, using contstruct_T
+ ISN_OBJ_MEMBER, // object member, index is isn_arg.number
// get and set variables
ISN_LOAD, // push local variable isn_arg.number
// Growarray with object methods declared in the class.
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.
// static varname
// public 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;
if (checkforcmd(&p, "endclass", 4))
++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
{
semsg(_(e_not_valid_command_in_class_str), line);
int have_new = FALSE;
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)
{
have_new = TRUE;
garray_T lines_to_free;
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);
vim_free(fga.ga_data);
++objmethods.ga_len;
nf->uf_flags |= FC_NEW;
- nf->uf_class = cl;
nf->uf_ret_type = get_type_ptr(&type_list);
if (nf->uf_ret_type != NULL)
{
nf->uf_ret_type->tt_argcount = 0;
nf->uf_ret_type->tt_args = NULL;
}
- cl->class_new_func = nf;
}
}
sizeof(ufunc_T *) * objmethods.ga_len);
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_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;
// TODO:
}
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);
clear_type_list(&type_list);
}
funcexe_T funcexe;
CLEAR_FIELD(funcexe);
funcexe.fe_evaluate = TRUE;
+ if (rettv->v_type == VAR_OBJECT)
+ {
+ funcexe.fe_object = rettv->vval.v_object;
+ ++funcexe.fe_object->obj_refcount;
+ }
// Clear the class or object after calling the function, in
// case the refcount is one.
rettv->v_type = VAR_UNKNOWN;
// Call the user function. Result goes into rettv;
- // TODO: pass the object
int error = call_user_func_check(fp, argcount, argvars,
rettv, &funcexe, NULL);
}
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);
- if (cl->class_new_func != NULL)
- func_ptr_unref(cl->class_new_func);
-
clear_type_list(&cl->class_type_list);
vim_free(cl);
CLEAR_POINTER(lvar);
lvar->lv_name = (char_u *)"this";
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;
}
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)
{
r = eap->skip ? OK : FAIL;
CLEAR_FIELD(ea);
ea.cmd = ea.arg = iptr->isn_arg.string;
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);
}
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:
dict_stack_drop();
break;
typval_T *argv, // arguments
int flags, // DEF_ flags
partial_T *partial, // optional partial for context
+ object_T *object, // object, e.g. for this.Func()
funccall_T *funccal,
typval_T *rettv) // return value
{
STACK_TV_VAR(idx)->vval.v_number = 0;
}
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)
{
// Initialize the variable that counts how many closures were
case ISN_MEMBER: smsg("%s%4d MEMBER", pfx, current); break;
case ISN_STRINGMEMBER: smsg("%s%4d MEMBER %s", pfx, current,
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_USEDICT: smsg("%s%4d USEDICT", pfx, current); break;
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
* leading "s:".
for (;;)
{
char_u *p = skipwhite(*arg);
+ type_T *type;
if (*p == NUL || (VIM_ISWHITE(**arg) && vim9_comment_start(p)))
{
// is not a function call.
if (**arg == '(')
{
- type_T *type;
int argcount = 0;
if (generate_ppconst(cctx, ppconst) == FAIL)
int argcount = 1;
garray_T *stack = &cctx->ctx_type_stack;
int type_idx_start = stack->ga_len;
- type_T *type;
int expr_isn_start = cctx->ctx_instr.ga_len;
int expr_isn_end;
int arg_isn_count;
if (compile_member(is_slice, &keeping_dict, cctx) == 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] != '.')
{
// dictionary member: dict.name
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.
* But only for simple types.
case ISN_NEWDICT:
case ISN_NEWLIST:
case ISN_NEWPARTIAL:
+ case ISN_OBJ_MEMBER:
case ISN_OPANY:
case ISN_OPFLOAT:
case ISN_OPNR:
if (tofree != NULL)
rettv->vval.v_string = (char_u *)tofree;
else
- {
rettv->vval.v_string = vim_strsave((char_u *)name);
- vim_free(tofree);
- }
clear_type_list(&type_list);
}