forked from aniani/vim
patch 9.0.2019: Vim9: no support for funcrefs
Problem: Vim9: no support for funcrefs Solution: Add support for object/class funcref members closes: #11981 #12417 #12960 #12324 #13333 Signed-off-by: Christian Brabandt <cb@256bit.org> Co-authored-by: Yegappan Lakshmanan <yegappan@yahoo.com>
This commit is contained in:
committed by
Christian Brabandt
parent
69fb5afb3b
commit
29bb67f1be
11
src/eval.c
11
src/eval.c
@@ -5256,6 +5256,7 @@ partial_free(partial_T *pt)
|
||||
}
|
||||
else
|
||||
func_ptr_unref(pt->pt_func);
|
||||
object_unref(pt->pt_obj);
|
||||
|
||||
// "out_up" is no longer used, decrement refcount on partial that owns it.
|
||||
partial_unref(pt->pt_outer.out_up_partial);
|
||||
@@ -5578,6 +5579,7 @@ free_unref_items(int copyID)
|
||||
/*
|
||||
* PASS 2: free the items themselves.
|
||||
*/
|
||||
object_free_items(copyID);
|
||||
dict_free_items(copyID);
|
||||
list_free_items(copyID);
|
||||
|
||||
@@ -5818,6 +5820,15 @@ set_ref_in_item_partial(
|
||||
set_ref_in_item(&dtv, copyID, ht_stack, list_stack);
|
||||
}
|
||||
|
||||
if (pt->pt_obj != NULL)
|
||||
{
|
||||
typval_T objtv;
|
||||
|
||||
objtv.v_type = VAR_OBJECT;
|
||||
objtv.vval.v_object = pt->pt_obj;
|
||||
set_ref_in_item(&objtv, copyID, ht_stack, list_stack);
|
||||
}
|
||||
|
||||
for (int i = 0; i < pt->pt_argc; ++i)
|
||||
abort = abort || set_ref_in_item(&pt->pt_argv[i], copyID,
|
||||
ht_stack, list_stack);
|
||||
|
||||
@@ -4801,6 +4801,9 @@ common_function(typval_T *argvars, typval_T *rettv, int is_funcref)
|
||||
pt->pt_auto = arg_pt->pt_auto;
|
||||
if (pt->pt_dict != NULL)
|
||||
++pt->pt_dict->dv_refcount;
|
||||
pt->pt_obj = arg_pt->pt_obj;
|
||||
if (pt->pt_obj != NULL)
|
||||
++pt->pt_obj->obj_refcount;
|
||||
}
|
||||
|
||||
pt->pt_refcount = 1;
|
||||
|
||||
@@ -17,13 +17,14 @@ void emsg_var_cl_define(char *msg, char_u *name, size_t len, class_T *cl);
|
||||
ufunc_T *method_lookup(class_T *cl, vartype_T v_type, char_u *name, size_t namelen, int *idx);
|
||||
int inside_class(cctx_T *cctx_arg, class_T *cl);
|
||||
void copy_object(typval_T *from, typval_T *to);
|
||||
void object_unref(object_T *obj);
|
||||
void copy_class(typval_T *from, typval_T *to);
|
||||
void class_unref(class_T *cl);
|
||||
int class_free_nonref(int copyID);
|
||||
int set_ref_in_classes(int copyID);
|
||||
void object_created(object_T *obj);
|
||||
void object_unref(object_T *obj);
|
||||
int object_free_nonref(int copyID);
|
||||
void object_free_items(int copyID);
|
||||
void method_not_found_msg(class_T *cl, vartype_T v_type, char_u *name, size_t len);
|
||||
void member_not_found_msg(class_T *cl, vartype_T v_type, char_u *name, size_t len);
|
||||
void f_instanceof(typval_T *argvars, typval_T *rettv);
|
||||
|
||||
@@ -45,7 +45,7 @@ int generate_OLDSCRIPT(cctx_T *cctx, isntype_T isn_type, char_u *name, int sid,
|
||||
int generate_VIM9SCRIPT(cctx_T *cctx, isntype_T isn_type, int sid, int idx, type_T *type);
|
||||
int generate_NEWLIST(cctx_T *cctx, int count, int use_null);
|
||||
int generate_NEWDICT(cctx_T *cctx, int count, int use_null);
|
||||
int generate_FUNCREF(cctx_T *cctx, ufunc_T *ufunc, class_T *cl, int fi, int *isn_idx);
|
||||
int generate_FUNCREF(cctx_T *cctx, ufunc_T *ufunc, class_T *cl, int object_method, int fi, int *isn_idx);
|
||||
int generate_NEWFUNC(cctx_T *cctx, char_u *lambda_name, char_u *func_name);
|
||||
int generate_DEF(cctx_T *cctx, char_u *name, size_t len);
|
||||
int generate_JUMP(cctx_T *cctx, jumpwhen_T when, int where);
|
||||
|
||||
@@ -2316,6 +2316,7 @@ struct partial_S
|
||||
|
||||
int pt_copyID; // funcstack may contain pointer to partial
|
||||
dict_T *pt_dict; // dict for "self"
|
||||
object_T *pt_obj; // object method
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
|
||||
@@ -7477,4 +7477,480 @@ def Test_op_and_assignment()
|
||||
v9.CheckSourceSuccess(lines)
|
||||
enddef
|
||||
|
||||
" Test for using an object method as a funcref
|
||||
def Test_object_funcref()
|
||||
# Using object method funcref from a def function
|
||||
var lines =<< trim END
|
||||
vim9script
|
||||
class A
|
||||
def Foo(): list<number>
|
||||
return [3, 2, 1]
|
||||
enddef
|
||||
endclass
|
||||
def Bar()
|
||||
var a = A.new()
|
||||
var Fn = a.Foo
|
||||
assert_equal([3, 2, 1], Fn())
|
||||
enddef
|
||||
Bar()
|
||||
END
|
||||
v9.CheckSourceSuccess(lines)
|
||||
|
||||
# Using object method funcref at the script level
|
||||
lines =<< trim END
|
||||
vim9script
|
||||
class A
|
||||
def Foo(): dict<number>
|
||||
return {a: 1, b: 2}
|
||||
enddef
|
||||
endclass
|
||||
var a = A.new()
|
||||
var Fn = a.Foo
|
||||
assert_equal({a: 1, b: 2}, Fn())
|
||||
END
|
||||
v9.CheckSourceSuccess(lines)
|
||||
|
||||
# Using object method funcref from another object method
|
||||
lines =<< trim END
|
||||
vim9script
|
||||
class A
|
||||
def Foo(): list<number>
|
||||
return [3, 2, 1]
|
||||
enddef
|
||||
def Bar()
|
||||
var Fn = this.Foo
|
||||
assert_equal([3, 2, 1], Fn())
|
||||
enddef
|
||||
endclass
|
||||
var a = A.new()
|
||||
a.Bar()
|
||||
END
|
||||
v9.CheckSourceSuccess(lines)
|
||||
|
||||
# Using function() to get a object method funcref
|
||||
lines =<< trim END
|
||||
vim9script
|
||||
class A
|
||||
def Foo(l: list<any>): list<any>
|
||||
return l
|
||||
enddef
|
||||
endclass
|
||||
var a = A.new()
|
||||
var Fn = function(a.Foo, [[{a: 1, b: 2}, [3, 4]]])
|
||||
assert_equal([{a: 1, b: 2}, [3, 4]], Fn())
|
||||
END
|
||||
v9.CheckSourceSuccess(lines)
|
||||
|
||||
# Use an object method with a function returning a funcref and then call the
|
||||
# funcref.
|
||||
lines =<< trim END
|
||||
vim9script
|
||||
|
||||
def Map(F: func(number): number): func(number): number
|
||||
return (n: number) => F(n)
|
||||
enddef
|
||||
|
||||
class Math
|
||||
def Double(n: number): number
|
||||
return 2 * n
|
||||
enddef
|
||||
endclass
|
||||
|
||||
const math = Math.new()
|
||||
assert_equal(48, Map(math.Double)(24))
|
||||
END
|
||||
v9.CheckSourceSuccess(lines)
|
||||
|
||||
# Try using a private object method funcref from a def function
|
||||
lines =<< trim END
|
||||
vim9script
|
||||
class A
|
||||
def _Foo()
|
||||
enddef
|
||||
endclass
|
||||
def Bar()
|
||||
var a = A.new()
|
||||
var Fn = a._Foo
|
||||
enddef
|
||||
Bar()
|
||||
END
|
||||
v9.CheckSourceFailure(lines, 'E1366: Cannot access private method: _Foo', 2)
|
||||
|
||||
# Try using a private object method funcref at the script level
|
||||
lines =<< trim END
|
||||
vim9script
|
||||
class A
|
||||
def _Foo()
|
||||
enddef
|
||||
endclass
|
||||
var a = A.new()
|
||||
var Fn = a._Foo
|
||||
END
|
||||
v9.CheckSourceFailure(lines, 'E1366: Cannot access private method: _Foo', 7)
|
||||
|
||||
# Using a private object method funcref from another object method
|
||||
lines =<< trim END
|
||||
vim9script
|
||||
class A
|
||||
def _Foo(): list<number>
|
||||
return [3, 2, 1]
|
||||
enddef
|
||||
def Bar()
|
||||
var Fn = this._Foo
|
||||
assert_equal([3, 2, 1], Fn())
|
||||
enddef
|
||||
endclass
|
||||
var a = A.new()
|
||||
a.Bar()
|
||||
END
|
||||
v9.CheckSourceSuccess(lines)
|
||||
enddef
|
||||
|
||||
" Test for using a class method as a funcref
|
||||
def Test_class_funcref()
|
||||
# Using class method funcref in a def function
|
||||
var lines =<< trim END
|
||||
vim9script
|
||||
class A
|
||||
static def Foo(): list<number>
|
||||
return [3, 2, 1]
|
||||
enddef
|
||||
endclass
|
||||
def Bar()
|
||||
var Fn = A.Foo
|
||||
assert_equal([3, 2, 1], Fn())
|
||||
enddef
|
||||
Bar()
|
||||
END
|
||||
v9.CheckSourceSuccess(lines)
|
||||
|
||||
# Using class method funcref at script level
|
||||
lines =<< trim END
|
||||
vim9script
|
||||
class A
|
||||
static def Foo(): dict<number>
|
||||
return {a: 1, b: 2}
|
||||
enddef
|
||||
endclass
|
||||
var Fn = A.Foo
|
||||
assert_equal({a: 1, b: 2}, Fn())
|
||||
END
|
||||
v9.CheckSourceSuccess(lines)
|
||||
|
||||
# Using function() to get a class method funcref
|
||||
lines =<< trim END
|
||||
vim9script
|
||||
class A
|
||||
static def Foo(l: list<any>): list<any>
|
||||
return l
|
||||
enddef
|
||||
endclass
|
||||
var Fn = function(A.Foo, [[{a: 1, b: 2}, [3, 4]]])
|
||||
assert_equal([{a: 1, b: 2}, [3, 4]], Fn())
|
||||
END
|
||||
v9.CheckSourceSuccess(lines)
|
||||
|
||||
# Using a class method funcref from another class method
|
||||
lines =<< trim END
|
||||
vim9script
|
||||
class A
|
||||
static def Foo(): list<number>
|
||||
return [3, 2, 1]
|
||||
enddef
|
||||
static def Bar()
|
||||
var Fn = Foo
|
||||
assert_equal([3, 2, 1], Fn())
|
||||
enddef
|
||||
endclass
|
||||
A.Bar()
|
||||
END
|
||||
v9.CheckSourceSuccess(lines)
|
||||
|
||||
# Use a class method with a function returning a funcref and then call the
|
||||
# funcref.
|
||||
lines =<< trim END
|
||||
vim9script
|
||||
|
||||
def Map(F: func(number): number): func(number): number
|
||||
return (n: number) => F(n)
|
||||
enddef
|
||||
|
||||
class Math
|
||||
static def StaticDouble(n: number): number
|
||||
return 2 * n
|
||||
enddef
|
||||
endclass
|
||||
|
||||
assert_equal(48, Map(Math.StaticDouble)(24))
|
||||
END
|
||||
v9.CheckSourceSuccess(lines)
|
||||
|
||||
# Try using a private class method funcref in a def function
|
||||
lines =<< trim END
|
||||
vim9script
|
||||
class A
|
||||
static def _Foo()
|
||||
enddef
|
||||
endclass
|
||||
def Bar()
|
||||
var Fn = A._Foo
|
||||
enddef
|
||||
Bar()
|
||||
END
|
||||
v9.CheckSourceFailure(lines, 'E1366: Cannot access private method: _Foo', 1)
|
||||
|
||||
# Try using a private class method funcref at script level
|
||||
lines =<< trim END
|
||||
vim9script
|
||||
class A
|
||||
static def _Foo()
|
||||
enddef
|
||||
endclass
|
||||
var Fn = A._Foo
|
||||
END
|
||||
v9.CheckSourceFailure(lines, 'E1366: Cannot access private method: _Foo', 6)
|
||||
|
||||
# Using a private class method funcref from another class method
|
||||
lines =<< trim END
|
||||
vim9script
|
||||
class A
|
||||
static def _Foo(): list<number>
|
||||
return [3, 2, 1]
|
||||
enddef
|
||||
static def Bar()
|
||||
var Fn = _Foo
|
||||
assert_equal([3, 2, 1], Fn())
|
||||
enddef
|
||||
endclass
|
||||
A.Bar()
|
||||
END
|
||||
v9.CheckSourceSuccess(lines)
|
||||
enddef
|
||||
|
||||
" Test for using an object member as a funcref
|
||||
def Test_object_member_funcref()
|
||||
# Using a funcref object variable in an object method
|
||||
var lines =<< trim END
|
||||
vim9script
|
||||
def Foo(n: number): number
|
||||
return n * 10
|
||||
enddef
|
||||
|
||||
class A
|
||||
this.Cb: func(number): number = Foo
|
||||
def Bar()
|
||||
assert_equal(200, this.Cb(20))
|
||||
enddef
|
||||
endclass
|
||||
|
||||
var a = A.new()
|
||||
a.Bar()
|
||||
END
|
||||
v9.CheckSourceSuccess(lines)
|
||||
|
||||
# Using a funcref object variable in a def method
|
||||
lines =<< trim END
|
||||
vim9script
|
||||
def Foo(n: number): number
|
||||
return n * 10
|
||||
enddef
|
||||
|
||||
class A
|
||||
this.Cb: func(number): number = Foo
|
||||
endclass
|
||||
|
||||
def Bar()
|
||||
var a = A.new()
|
||||
assert_equal(200, a.Cb(20))
|
||||
enddef
|
||||
Bar()
|
||||
END
|
||||
v9.CheckSourceSuccess(lines)
|
||||
|
||||
# Using a funcref object variable at script level
|
||||
lines =<< trim END
|
||||
vim9script
|
||||
def Foo(n: number): number
|
||||
return n * 10
|
||||
enddef
|
||||
|
||||
class A
|
||||
this.Cb: func(number): number = Foo
|
||||
endclass
|
||||
|
||||
var a = A.new()
|
||||
assert_equal(200, a.Cb(20))
|
||||
END
|
||||
v9.CheckSourceSuccess(lines)
|
||||
|
||||
# Using a funcref object variable pointing to an object method in an object
|
||||
# method.
|
||||
lines =<< trim END
|
||||
vim9script
|
||||
class A
|
||||
this.Cb: func(number): number = this.Foo
|
||||
def Foo(n: number): number
|
||||
return n * 10
|
||||
enddef
|
||||
def Bar()
|
||||
assert_equal(200, this.Cb(20))
|
||||
enddef
|
||||
endclass
|
||||
|
||||
var a = A.new()
|
||||
a.Bar()
|
||||
END
|
||||
v9.CheckSourceSuccess(lines)
|
||||
|
||||
# Using a funcref object variable pointing to an object method in a def
|
||||
# method.
|
||||
lines =<< trim END
|
||||
vim9script
|
||||
class A
|
||||
this.Cb: func(number): number = this.Foo
|
||||
def Foo(n: number): number
|
||||
return n * 10
|
||||
enddef
|
||||
endclass
|
||||
|
||||
def Bar()
|
||||
var a = A.new()
|
||||
assert_equal(200, a.Cb(20))
|
||||
enddef
|
||||
Bar()
|
||||
END
|
||||
v9.CheckSourceSuccess(lines)
|
||||
|
||||
# Using a funcref object variable pointing to an object method at script
|
||||
# level.
|
||||
lines =<< trim END
|
||||
vim9script
|
||||
class A
|
||||
this.Cb = this.Foo
|
||||
def Foo(n: number): number
|
||||
return n * 10
|
||||
enddef
|
||||
endclass
|
||||
|
||||
var a = A.new()
|
||||
assert_equal(200, a.Cb(20))
|
||||
END
|
||||
v9.CheckSourceSuccess(lines)
|
||||
enddef
|
||||
|
||||
" Test for using a class member as a funcref
|
||||
def Test_class_member_funcref()
|
||||
# Using a funcref class variable in a class method
|
||||
var lines =<< trim END
|
||||
vim9script
|
||||
def Foo(n: number): number
|
||||
return n * 10
|
||||
enddef
|
||||
|
||||
class A
|
||||
static Cb = Foo
|
||||
static def Bar()
|
||||
assert_equal(200, Cb(20))
|
||||
enddef
|
||||
endclass
|
||||
|
||||
A.Bar()
|
||||
END
|
||||
v9.CheckSourceSuccess(lines)
|
||||
|
||||
# Using a funcref class variable in a def method
|
||||
lines =<< trim END
|
||||
vim9script
|
||||
def Foo(n: number): number
|
||||
return n * 10
|
||||
enddef
|
||||
|
||||
class A
|
||||
public static Cb = Foo
|
||||
endclass
|
||||
|
||||
def Bar()
|
||||
assert_equal(200, A.Cb(20))
|
||||
enddef
|
||||
Bar()
|
||||
END
|
||||
v9.CheckSourceSuccess(lines)
|
||||
|
||||
# Using a funcref class variable at script level
|
||||
lines =<< trim END
|
||||
vim9script
|
||||
def Foo(n: number): number
|
||||
return n * 10
|
||||
enddef
|
||||
|
||||
class A
|
||||
public static Cb = Foo
|
||||
endclass
|
||||
|
||||
assert_equal(200, A.Cb(20))
|
||||
END
|
||||
v9.CheckSourceSuccess(lines)
|
||||
|
||||
# Using a funcref class variable pointing to a class method in a class
|
||||
# method.
|
||||
lines =<< trim END
|
||||
vim9script
|
||||
class A
|
||||
static Cb: func(number): number
|
||||
static def Foo(n: number): number
|
||||
return n * 10
|
||||
enddef
|
||||
static def Init()
|
||||
Cb = Foo
|
||||
enddef
|
||||
static def Bar()
|
||||
assert_equal(200, Cb(20))
|
||||
enddef
|
||||
endclass
|
||||
|
||||
A.Init()
|
||||
A.Bar()
|
||||
END
|
||||
v9.CheckSourceSuccess(lines)
|
||||
|
||||
# Using a funcref class variable pointing to a class method in a def method.
|
||||
lines =<< trim END
|
||||
vim9script
|
||||
class A
|
||||
static Cb: func(number): number
|
||||
static def Foo(n: number): number
|
||||
return n * 10
|
||||
enddef
|
||||
static def Init()
|
||||
Cb = Foo
|
||||
enddef
|
||||
endclass
|
||||
|
||||
def Bar()
|
||||
A.Init()
|
||||
assert_equal(200, A.Cb(20))
|
||||
enddef
|
||||
Bar()
|
||||
END
|
||||
v9.CheckSourceSuccess(lines)
|
||||
|
||||
# Using a funcref class variable pointing to a class method at script level.
|
||||
lines =<< trim END
|
||||
vim9script
|
||||
class A
|
||||
static Cb: func(number): number
|
||||
static def Foo(n: number): number
|
||||
return n * 10
|
||||
enddef
|
||||
static def Init()
|
||||
Cb = Foo
|
||||
enddef
|
||||
endclass
|
||||
|
||||
A.Init()
|
||||
assert_equal(200, A.Cb(20))
|
||||
END
|
||||
v9.CheckSourceSuccess(lines)
|
||||
enddef
|
||||
|
||||
" vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker
|
||||
|
||||
@@ -3663,6 +3663,17 @@ def Test_partial_call()
|
||||
const Call = Foo(Expr)
|
||||
END
|
||||
v9.CheckScriptFailure(lines, 'E1031:')
|
||||
|
||||
# Test for calling a partial that takes a single argument.
|
||||
# This used to produce a "E340: Internal error" message.
|
||||
lines =<< trim END
|
||||
def Foo(n: number): number
|
||||
return n * 2
|
||||
enddef
|
||||
var Fn = function(Foo, [10])
|
||||
assert_equal(20, Fn())
|
||||
END
|
||||
v9.CheckDefAndScriptSuccess(lines)
|
||||
enddef
|
||||
|
||||
def Test_partial_double_nested()
|
||||
|
||||
@@ -704,6 +704,8 @@ static char *(features[]) =
|
||||
|
||||
static int included_patches[] =
|
||||
{ /* Add new patch number below this line */
|
||||
/**/
|
||||
2019,
|
||||
/**/
|
||||
2018,
|
||||
/**/
|
||||
|
||||
@@ -383,6 +383,7 @@ typedef struct {
|
||||
char_u *fre_func_name; // function name for legacy function
|
||||
loopvarinfo_T fre_loopvar_info; // info about variables inside loops
|
||||
class_T *fre_class; // class for a method
|
||||
int fre_object_method; // class or object method
|
||||
int fre_method_idx; // method index on "fre_class"
|
||||
} funcref_extra_T;
|
||||
|
||||
|
||||
115
src/vim9class.c
115
src/vim9class.c
@@ -2167,15 +2167,35 @@ call_oc_method(
|
||||
ufunc_T *fp;
|
||||
typval_T argvars[MAX_FUNC_ARGS + 1];
|
||||
int argcount = 0;
|
||||
ocmember_T *ocm = NULL;
|
||||
int m_idx;
|
||||
|
||||
fp = method_lookup(cl, rettv->v_type, name, len, NULL);
|
||||
if (fp == NULL)
|
||||
{
|
||||
method_not_found_msg(cl, rettv->v_type, name, len);
|
||||
return FAIL;
|
||||
// could be an object or class funcref variable
|
||||
ocm = member_lookup(cl, rettv->v_type, name, len, &m_idx);
|
||||
if (ocm == NULL || ocm->ocm_type->tt_type != VAR_FUNC)
|
||||
{
|
||||
method_not_found_msg(cl, rettv->v_type, name, len);
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
if (rettv->v_type == VAR_OBJECT)
|
||||
{
|
||||
// funcref object variable
|
||||
object_T *obj = rettv->vval.v_object;
|
||||
typval_T *tv = (typval_T *)(obj + 1) + m_idx;
|
||||
copy_tv(tv, rettv);
|
||||
}
|
||||
else
|
||||
// funcref class variable
|
||||
copy_tv(&cl->class_members_tv[m_idx], rettv);
|
||||
*arg = name_end;
|
||||
return OK;
|
||||
}
|
||||
|
||||
if (*fp->uf_name == '_')
|
||||
if (ocm == NULL && *fp->uf_name == '_')
|
||||
{
|
||||
// Cannot access a private method outside of a class
|
||||
semsg(_(e_cannot_access_private_method_str), fp->uf_name);
|
||||
@@ -2288,6 +2308,37 @@ class_object_index(
|
||||
return OK;
|
||||
}
|
||||
|
||||
// could be a class method or an object method
|
||||
int fidx;
|
||||
ufunc_T *fp = method_lookup(cl, rettv->v_type, name, len, &fidx);
|
||||
if (fp != NULL)
|
||||
{
|
||||
// Private methods are not accessible outside the class
|
||||
if (*name == '_')
|
||||
{
|
||||
semsg(_(e_cannot_access_private_method_str), fp->uf_name);
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
partial_T *pt = ALLOC_CLEAR_ONE(partial_T);
|
||||
if (pt == NULL)
|
||||
return FAIL;
|
||||
|
||||
pt->pt_refcount = 1;
|
||||
if (is_object)
|
||||
{
|
||||
pt->pt_obj = rettv->vval.v_object;
|
||||
++pt->pt_obj->obj_refcount;
|
||||
}
|
||||
pt->pt_auto = TRUE;
|
||||
pt->pt_func = fp;
|
||||
func_ptr_ref(pt->pt_func);
|
||||
rettv->v_type = VAR_PARTIAL;
|
||||
rettv->vval.v_partial = pt;
|
||||
*arg = name_end;
|
||||
return OK;
|
||||
}
|
||||
|
||||
if (did_emsg == did_emsg_save)
|
||||
member_not_found_msg(cl, is_object, name, len);
|
||||
}
|
||||
@@ -2774,8 +2825,6 @@ object_created(object_T *obj)
|
||||
first_object = obj;
|
||||
}
|
||||
|
||||
static object_T *next_nonref_obj = NULL;
|
||||
|
||||
/*
|
||||
* Call this function when an object has been cleared and is about to be freed.
|
||||
* It is removed from the list headed by "first_object".
|
||||
@@ -2789,30 +2838,35 @@ object_cleared(object_T *obj)
|
||||
obj->obj_prev_used->obj_next_used = obj->obj_next_used;
|
||||
else if (first_object == obj)
|
||||
first_object = obj->obj_next_used;
|
||||
|
||||
// update the next object to check if needed
|
||||
if (obj == next_nonref_obj)
|
||||
next_nonref_obj = obj->obj_next_used;
|
||||
}
|
||||
|
||||
/*
|
||||
* Free an object.
|
||||
* Free the contents of an object ignoring the reference count.
|
||||
*/
|
||||
static void
|
||||
object_clear(object_T *obj)
|
||||
object_free_contents(object_T *obj)
|
||||
{
|
||||
// Avoid a recursive call, it can happen if "obj" has a circular reference.
|
||||
obj->obj_refcount = INT_MAX;
|
||||
|
||||
class_T *cl = obj->obj_class;
|
||||
|
||||
if (!cl)
|
||||
return;
|
||||
|
||||
// Avoid a recursive call, it can happen if "obj" has a circular reference.
|
||||
obj->obj_refcount = INT_MAX;
|
||||
|
||||
// the member values are just after the object structure
|
||||
typval_T *tv = (typval_T *)(obj + 1);
|
||||
for (int i = 0; i < cl->class_obj_member_count; ++i)
|
||||
clear_tv(tv + i);
|
||||
}
|
||||
|
||||
static void
|
||||
object_free_object(object_T *obj)
|
||||
{
|
||||
class_T *cl = obj->obj_class;
|
||||
|
||||
if (!cl)
|
||||
return;
|
||||
|
||||
// Remove from the list headed by "first_object".
|
||||
object_cleared(obj);
|
||||
@@ -2821,6 +2875,16 @@ object_clear(object_T *obj)
|
||||
class_unref(cl);
|
||||
}
|
||||
|
||||
static void
|
||||
object_free(object_T *obj)
|
||||
{
|
||||
if (in_free_unref_items)
|
||||
return;
|
||||
|
||||
object_free_contents(obj);
|
||||
object_free_object(obj);
|
||||
}
|
||||
|
||||
/*
|
||||
* Unreference an object.
|
||||
*/
|
||||
@@ -2828,7 +2892,7 @@ object_clear(object_T *obj)
|
||||
object_unref(object_T *obj)
|
||||
{
|
||||
if (obj != NULL && --obj->obj_refcount <= 0)
|
||||
object_clear(obj);
|
||||
object_free(obj);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -2839,21 +2903,32 @@ object_free_nonref(int copyID)
|
||||
{
|
||||
int did_free = FALSE;
|
||||
|
||||
for (object_T *obj = first_object; obj != NULL; obj = next_nonref_obj)
|
||||
for (object_T *obj = first_object; obj != NULL; obj = obj->obj_next_used)
|
||||
{
|
||||
next_nonref_obj = obj->obj_next_used;
|
||||
if ((obj->obj_copyID & COPYID_MASK) != (copyID & COPYID_MASK))
|
||||
{
|
||||
// Free the object and items it contains.
|
||||
object_clear(obj);
|
||||
// Free the object contents. Object itself will be freed later.
|
||||
object_free_contents(obj);
|
||||
did_free = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
next_nonref_obj = NULL;
|
||||
return did_free;
|
||||
}
|
||||
|
||||
void
|
||||
object_free_items(int copyID)
|
||||
{
|
||||
object_T *obj_next;
|
||||
|
||||
for (object_T *obj = first_object; obj != NULL; obj = obj_next)
|
||||
{
|
||||
obj_next = obj->obj_next_used;
|
||||
if ((obj->obj_copyID & COPYID_MASK) != (copyID & COPYID_MASK))
|
||||
object_free_object(obj);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Output message which takes a variable name and the class that defines it.
|
||||
* "cl" is that class where the name was found. Search "cl"'s hierarchy to
|
||||
|
||||
@@ -1148,7 +1148,7 @@ compile_nested_function(exarg_T *eap, cctx_T *cctx, garray_T *lines_to_free)
|
||||
ASSIGN_CONST, ufunc->uf_func_type);
|
||||
if (lvar == NULL)
|
||||
goto theend;
|
||||
if (generate_FUNCREF(cctx, ufunc, NULL, 0, &funcref_isn_idx) == FAIL)
|
||||
if (generate_FUNCREF(cctx, ufunc, NULL, FALSE, 0, &funcref_isn_idx) == FAIL)
|
||||
goto theend;
|
||||
r = generate_STORE(cctx, ISN_STORE, lvar->lv_idx, NULL);
|
||||
}
|
||||
|
||||
@@ -616,6 +616,12 @@ call_dfunc(
|
||||
// the first local variable.
|
||||
if (IS_OBJECT_METHOD(ufunc))
|
||||
{
|
||||
if (obj->v_type != VAR_OBJECT)
|
||||
{
|
||||
semsg(_(e_internal_error_str), "type in stack is not an object");
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
*STACK_TV_VAR(0) = *obj;
|
||||
obj->v_type = VAR_UNKNOWN;
|
||||
}
|
||||
@@ -1497,6 +1503,23 @@ call_partial(
|
||||
partial_T *pt = tv->vval.v_partial;
|
||||
int i;
|
||||
|
||||
if (pt->pt_obj != NULL)
|
||||
{
|
||||
// partial with an object method. Push the object before the
|
||||
// function arguments.
|
||||
if (GA_GROW_FAILS(&ectx->ec_stack, 1))
|
||||
return FAIL;
|
||||
for (i = 1; i <= argcount; ++i)
|
||||
*STACK_TV_BOT(-i + 1) = *STACK_TV_BOT(-i);
|
||||
|
||||
typval_T *obj_tv = STACK_TV_BOT(-argcount);
|
||||
obj_tv->v_type = VAR_OBJECT;
|
||||
obj_tv->v_lock = 0;
|
||||
obj_tv->vval.v_object = pt->pt_obj;
|
||||
++pt->pt_obj->obj_refcount;
|
||||
++ectx->ec_stack.ga_len;
|
||||
}
|
||||
|
||||
if (pt->pt_argc > 0)
|
||||
{
|
||||
// Make space for arguments from the partial, shift the "argcount"
|
||||
@@ -4447,20 +4470,44 @@ exec_instructions(ectx_T *ectx)
|
||||
}
|
||||
if (extra != NULL && extra->fre_class != NULL)
|
||||
{
|
||||
tv = STACK_TV_BOT(-1);
|
||||
if (tv->v_type != VAR_OBJECT)
|
||||
class_T *cl;
|
||||
if (extra->fre_object_method)
|
||||
{
|
||||
object_required_error(tv);
|
||||
vim_free(pt);
|
||||
goto on_error;
|
||||
}
|
||||
object_T *obj = tv->vval.v_object;
|
||||
class_T *cl = obj->obj_class;
|
||||
tv = STACK_TV_BOT(-1);
|
||||
if (tv->v_type != VAR_OBJECT)
|
||||
{
|
||||
object_required_error(tv);
|
||||
vim_free(pt);
|
||||
goto on_error;
|
||||
}
|
||||
|
||||
// convert the interface index to the object index
|
||||
int idx = object_index_from_itf_index(extra->fre_class,
|
||||
TRUE, extra->fre_method_idx, cl);
|
||||
ufunc = cl->class_obj_methods[idx];
|
||||
object_T *obj = tv->vval.v_object;
|
||||
cl = obj->obj_class;
|
||||
// drop the value from the stack
|
||||
clear_tv(tv);
|
||||
--ectx->ec_stack.ga_len;
|
||||
|
||||
pt->pt_obj = obj;
|
||||
++obj->obj_refcount;
|
||||
}
|
||||
else
|
||||
cl = extra->fre_class;
|
||||
|
||||
if (extra->fre_object_method)
|
||||
{
|
||||
// object method
|
||||
// convert the interface index to the object index
|
||||
int idx =
|
||||
object_index_from_itf_index(extra->fre_class,
|
||||
TRUE, extra->fre_method_idx, cl);
|
||||
ufunc = cl->class_obj_methods[idx];
|
||||
}
|
||||
else
|
||||
{
|
||||
// class method
|
||||
ufunc =
|
||||
cl->class_class_functions[extra->fre_method_idx];
|
||||
}
|
||||
}
|
||||
else if (extra == NULL || extra->fre_func_name == NULL)
|
||||
{
|
||||
|
||||
@@ -281,6 +281,8 @@ inside_class_hierarchy(cctx_T *cctx_arg, class_T *cl)
|
||||
static int
|
||||
compile_class_object_index(cctx_T *cctx, char_u **arg, type_T *type)
|
||||
{
|
||||
int m_idx;
|
||||
|
||||
if (VIM_ISWHITE((*arg)[1]))
|
||||
{
|
||||
semsg(_(e_no_white_space_allowed_after_str_str), ".", *arg);
|
||||
@@ -365,17 +367,34 @@ compile_class_object_index(cctx_T *cctx, char_u **arg, type_T *type)
|
||||
break;
|
||||
}
|
||||
}
|
||||
ocmember_T *ocm = NULL;
|
||||
if (ufunc == NULL)
|
||||
{
|
||||
method_not_found_msg(cl, type->tt_type, name, len);
|
||||
return FAIL;
|
||||
// could be a funcref in a member variable
|
||||
ocm = member_lookup(cl, type->tt_type, name, len, &m_idx);
|
||||
if (ocm == NULL || ocm->ocm_type->tt_type != VAR_FUNC)
|
||||
{
|
||||
method_not_found_msg(cl, type->tt_type, name, len);
|
||||
return FAIL;
|
||||
}
|
||||
if (type->tt_type == VAR_CLASS)
|
||||
{
|
||||
if (generate_CLASSMEMBER(cctx, TRUE, cl, m_idx) == FAIL)
|
||||
return FAIL;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (generate_GET_OBJ_MEMBER(cctx, m_idx, ocm->ocm_type) ==
|
||||
FAIL)
|
||||
return FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
// A private object method can be used only inside the class where it
|
||||
// is defined or in one of the child classes.
|
||||
// A private class method can be used only in the class where it is
|
||||
// defined.
|
||||
if (*ufunc->uf_name == '_' &&
|
||||
if (ocm == NULL && *ufunc->uf_name == '_' &&
|
||||
((type->tt_type == VAR_OBJECT
|
||||
&& !inside_class_hierarchy(cctx, cl))
|
||||
|| (type->tt_type == VAR_CLASS
|
||||
@@ -393,6 +412,8 @@ compile_class_object_index(cctx_T *cctx, char_u **arg, type_T *type)
|
||||
if (compile_arguments(arg, cctx, &argcount, CA_NOT_SPECIAL) == FAIL)
|
||||
return FAIL;
|
||||
|
||||
if (ocm != NULL)
|
||||
return generate_PCALL(cctx, argcount, name, ocm->ocm_type, TRUE);
|
||||
if (type->tt_type == VAR_OBJECT
|
||||
&& (cl->class_flags & (CLASS_INTERFACE | CLASS_EXTENDED)))
|
||||
return generate_CALL(cctx, ufunc, cl, fi, argcount);
|
||||
@@ -401,7 +422,6 @@ compile_class_object_index(cctx_T *cctx, char_u **arg, type_T *type)
|
||||
|
||||
if (type->tt_type == VAR_OBJECT)
|
||||
{
|
||||
int m_idx;
|
||||
ocmember_T *m = object_member_lookup(cl, name, len, &m_idx);
|
||||
if (m_idx >= 0)
|
||||
{
|
||||
@@ -418,15 +438,21 @@ compile_class_object_index(cctx_T *cctx, char_u **arg, type_T *type)
|
||||
return generate_GET_OBJ_MEMBER(cctx, m_idx, m->ocm_type);
|
||||
}
|
||||
|
||||
// Could be a function reference: "obj.Func".
|
||||
// Could be an object method reference: "obj.Func".
|
||||
m_idx = object_method_idx(cl, name, len);
|
||||
if (m_idx >= 0)
|
||||
{
|
||||
ufunc_T *fp = cl->class_obj_methods[m_idx];
|
||||
if (type->tt_type == VAR_OBJECT
|
||||
&& (cl->class_flags & (CLASS_INTERFACE | CLASS_EXTENDED)))
|
||||
return generate_FUNCREF(cctx, fp, cl, m_idx, NULL);
|
||||
return generate_FUNCREF(cctx, fp, NULL, 0, NULL);
|
||||
// Private methods are not accessible outside the class
|
||||
if (*name == '_' && !inside_class(cctx, cl))
|
||||
{
|
||||
semsg(_(e_cannot_access_private_method_str), fp->uf_name);
|
||||
return FAIL;
|
||||
}
|
||||
*arg = name_end;
|
||||
if (type->tt_type == VAR_OBJECT)
|
||||
return generate_FUNCREF(cctx, fp, cl, TRUE, m_idx, NULL);
|
||||
return generate_FUNCREF(cctx, fp, NULL, FALSE, 0, NULL);
|
||||
}
|
||||
|
||||
member_not_found_msg(cl, VAR_OBJECT, name, len);
|
||||
@@ -451,6 +477,24 @@ compile_class_object_index(cctx_T *cctx, char_u **arg, type_T *type)
|
||||
*arg = name_end;
|
||||
return generate_CLASSMEMBER(cctx, TRUE, cl, idx);
|
||||
}
|
||||
|
||||
// Could be a class method reference: "class.Func".
|
||||
m_idx = class_method_idx(cl, name, len);
|
||||
if (m_idx >= 0)
|
||||
{
|
||||
ufunc_T *fp = cl->class_class_functions[m_idx];
|
||||
// Private methods are not accessible outside the class
|
||||
if (*name == '_' && !inside_class(cctx, cl))
|
||||
{
|
||||
semsg(_(e_cannot_access_private_method_str), fp->uf_name);
|
||||
return FAIL;
|
||||
}
|
||||
*arg = name_end;
|
||||
if (type->tt_type == VAR_CLASS)
|
||||
return generate_FUNCREF(cctx, fp, cl, FALSE, m_idx, NULL);
|
||||
return generate_FUNCREF(cctx, fp, NULL, FALSE, 0, NULL);
|
||||
}
|
||||
|
||||
member_not_found_msg(cl, VAR_CLASS, name, len);
|
||||
}
|
||||
|
||||
@@ -716,6 +760,7 @@ compile_load(
|
||||
{
|
||||
size_t len = end - *arg;
|
||||
int idx;
|
||||
int method_idx;
|
||||
int gen_load = FALSE;
|
||||
int gen_load_outer = 0;
|
||||
int outer_loop_depth = -1;
|
||||
@@ -764,13 +809,27 @@ compile_load(
|
||||
else
|
||||
gen_load = TRUE;
|
||||
}
|
||||
else if ((idx = cctx_class_member_idx(cctx, *arg, len, &cl)) >= 0)
|
||||
else if (cctx->ctx_ufunc->uf_defclass != NULL &&
|
||||
(((idx =
|
||||
cctx_class_member_idx(cctx, *arg, len, &cl)) >= 0)
|
||||
|| ((method_idx =
|
||||
cctx_class_method_idx(cctx, *arg, len, &cl)) >= 0)))
|
||||
{
|
||||
// Referencing a class variable without the class name.
|
||||
// A class variable can be referenced without the class name
|
||||
// only in the class where the function is defined.
|
||||
// Referencing a class variable or method without the class
|
||||
// name. A class variable or method can be referenced without
|
||||
// the class name only in the class where the function is
|
||||
// defined.
|
||||
if (cctx->ctx_ufunc->uf_defclass == cl)
|
||||
res = generate_CLASSMEMBER(cctx, TRUE, cl, idx);
|
||||
{
|
||||
if (idx >= 0)
|
||||
res = generate_CLASSMEMBER(cctx, TRUE, cl, idx);
|
||||
else
|
||||
{
|
||||
ufunc_T *fp = cl->class_class_functions[method_idx];
|
||||
res = generate_FUNCREF(cctx, fp, cl, FALSE, method_idx,
|
||||
NULL);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
semsg(_(e_class_variable_str_accessible_only_inside_class_str),
|
||||
@@ -1387,7 +1446,7 @@ compile_lambda(char_u **arg, cctx_T *cctx)
|
||||
// The function reference count will be 1. When the ISN_FUNCREF
|
||||
// instruction is deleted the reference count is decremented and the
|
||||
// function is freed.
|
||||
return generate_FUNCREF(cctx, ufunc, NULL, 0, NULL);
|
||||
return generate_FUNCREF(cctx, ufunc, NULL, FALSE, 0, NULL);
|
||||
}
|
||||
|
||||
func_ptr_unref(ufunc);
|
||||
|
||||
@@ -1384,6 +1384,7 @@ generate_FUNCREF(
|
||||
cctx_T *cctx,
|
||||
ufunc_T *ufunc,
|
||||
class_T *cl,
|
||||
int object_method,
|
||||
int fi,
|
||||
int *isn_idx)
|
||||
{
|
||||
@@ -1412,6 +1413,7 @@ generate_FUNCREF(
|
||||
{
|
||||
extra->fre_class = cl;
|
||||
++cl->class_refcount;
|
||||
extra->fre_object_method = object_method;
|
||||
extra->fre_method_idx = fi;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -144,7 +144,7 @@ alloc_type(type_T *type)
|
||||
if (ret->tt_member != NULL)
|
||||
ret->tt_member = alloc_type(ret->tt_member);
|
||||
|
||||
if (type->tt_args != NULL)
|
||||
if (type->tt_argcount > 0 && type->tt_args != NULL)
|
||||
{
|
||||
int i;
|
||||
|
||||
@@ -153,6 +153,8 @@ alloc_type(type_T *type)
|
||||
for (i = 0; i < type->tt_argcount; ++i)
|
||||
ret->tt_args[i] = alloc_type(type->tt_args[i]);
|
||||
}
|
||||
else
|
||||
ret->tt_args = NULL;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user