mirror of
https://github.com/vim/vim.git
synced 2025-07-26 11:04:33 -04:00
patch 9.0.1123: class function not implemented yet
Problem: Class function not implemented yet. Solution: Implement defining and calling a class function.
This commit is contained in:
parent
9f2d97efe2
commit
6bafdd41cb
@ -6,6 +6,7 @@ void ex_enum(exarg_T *eap);
|
|||||||
void ex_type(exarg_T *eap);
|
void ex_type(exarg_T *eap);
|
||||||
int class_object_index(char_u **arg, typval_T *rettv, evalarg_T *evalarg, int verbose);
|
int class_object_index(char_u **arg, typval_T *rettv, evalarg_T *evalarg, int verbose);
|
||||||
ufunc_T *find_class_func(char_u **arg);
|
ufunc_T *find_class_func(char_u **arg);
|
||||||
|
int class_member_exists(char_u *name, class_T **cl_ret, int *idx_ret, cctx_T *cctx);
|
||||||
void copy_object(typval_T *from, typval_T *to);
|
void copy_object(typval_T *from, typval_T *to);
|
||||||
void object_unref(object_T *obj);
|
void object_unref(object_T *obj);
|
||||||
void copy_class(typval_T *from, typval_T *to);
|
void copy_class(typval_T *from, typval_T *to);
|
||||||
|
@ -1493,9 +1493,9 @@ struct class_S
|
|||||||
ocmember_T *class_class_members; // allocated
|
ocmember_T *class_class_members; // allocated
|
||||||
typval_T *class_members_tv; // allocated array of class member vals
|
typval_T *class_members_tv; // allocated array of class member vals
|
||||||
|
|
||||||
// class methods: "static def SomeMethod()"
|
// class functions: "static def SomeMethod()"
|
||||||
int class_class_method_count;
|
int class_class_function_count;
|
||||||
ufunc_T **class_class_methods; // allocated
|
ufunc_T **class_class_functions; // allocated
|
||||||
|
|
||||||
// object members: "this.varname"
|
// object members: "this.varname"
|
||||||
int class_obj_member_count;
|
int class_obj_member_count;
|
||||||
|
@ -377,7 +377,7 @@ def Test_class_member_access()
|
|||||||
static _secret = 7
|
static _secret = 7
|
||||||
public static anybody = 42
|
public static anybody = 42
|
||||||
|
|
||||||
def AddToCounter(nr: number)
|
static def AddToCounter(nr: number)
|
||||||
counter += nr
|
counter += nr
|
||||||
enddef
|
enddef
|
||||||
endclass
|
endclass
|
||||||
@ -403,6 +403,32 @@ def Test_class_member_access()
|
|||||||
v9.CheckScriptSuccess(lines)
|
v9.CheckScriptSuccess(lines)
|
||||||
enddef
|
enddef
|
||||||
|
|
||||||
|
def Test_class_function()
|
||||||
|
var lines =<< trim END
|
||||||
|
vim9script
|
||||||
|
class Value
|
||||||
|
this.value = 0
|
||||||
|
static objects = 0
|
||||||
|
|
||||||
|
def new(v: number)
|
||||||
|
this.value = v
|
||||||
|
++objects
|
||||||
|
enddef
|
||||||
|
|
||||||
|
static def GetCount(): number
|
||||||
|
return objects
|
||||||
|
enddef
|
||||||
|
endclass
|
||||||
|
|
||||||
|
assert_equal(0, Value.GetCount())
|
||||||
|
var v1 = Value.new(2)
|
||||||
|
assert_equal(1, Value.GetCount())
|
||||||
|
var v2 = Value.new(7)
|
||||||
|
assert_equal(2, Value.GetCount())
|
||||||
|
END
|
||||||
|
v9.CheckScriptSuccess(lines)
|
||||||
|
enddef
|
||||||
|
|
||||||
def Test_class_object_to_string()
|
def Test_class_object_to_string()
|
||||||
var lines =<< trim END
|
var lines =<< trim END
|
||||||
vim9script
|
vim9script
|
||||||
|
@ -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 */
|
||||||
|
/**/
|
||||||
|
1123,
|
||||||
/**/
|
/**/
|
||||||
1122,
|
1122,
|
||||||
/**/
|
/**/
|
||||||
|
202
src/vim9class.c
202
src/vim9class.c
@ -223,9 +223,9 @@ ex_class(exarg_T *eap)
|
|||||||
garray_T classmembers;
|
garray_T classmembers;
|
||||||
ga_init2(&classmembers, sizeof(ocmember_T), 10);
|
ga_init2(&classmembers, sizeof(ocmember_T), 10);
|
||||||
|
|
||||||
// Growarray with object methods declared in the class.
|
// Growarray with functions declared in the class.
|
||||||
garray_T classmethods;
|
garray_T classfunctions;
|
||||||
ga_init2(&classmethods, sizeof(ufunc_T *), 10);
|
ga_init2(&classfunctions, sizeof(ufunc_T *), 10);
|
||||||
|
|
||||||
// Growarray with object members declared in the class.
|
// Growarray with object members declared in the class.
|
||||||
garray_T objmembers;
|
garray_T objmembers;
|
||||||
@ -288,6 +288,19 @@ ex_class(exarg_T *eap)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int has_static = FALSE;
|
||||||
|
char_u *ps = p;
|
||||||
|
if (checkforcmd(&p, "static", 4))
|
||||||
|
{
|
||||||
|
if (STRNCMP(ps, "static", 6) != 0)
|
||||||
|
{
|
||||||
|
semsg(_(e_command_cannot_be_shortened_str), ps);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
has_static = TRUE;
|
||||||
|
p = skipwhite(ps + 6);
|
||||||
|
}
|
||||||
|
|
||||||
// object members (public, read access, private):
|
// object members (public, read access, private):
|
||||||
// "this._varname"
|
// "this._varname"
|
||||||
// "this.varname"
|
// "this.varname"
|
||||||
@ -314,47 +327,15 @@ ex_class(exarg_T *eap)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// class members and methods
|
|
||||||
else if (checkforcmd(&p, "static", 6))
|
|
||||||
{
|
|
||||||
p = skipwhite(p);
|
|
||||||
if (checkforcmd(&p, "def", 3))
|
|
||||||
{
|
|
||||||
// TODO: class method
|
|
||||||
// static def someMethod()
|
|
||||||
// enddef
|
|
||||||
// static def <Tval> someMethod()
|
|
||||||
// enddef
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// class members (public, read access, private):
|
|
||||||
// "static _varname"
|
|
||||||
// "static varname"
|
|
||||||
// "public static varname"
|
|
||||||
char_u *varname = p;
|
|
||||||
char_u *varname_end = NULL;
|
|
||||||
type_T *type = NULL;
|
|
||||||
char_u *init_expr = NULL;
|
|
||||||
if (parse_member(eap, line, varname, has_public,
|
|
||||||
&varname_end, &type_list, &type, &init_expr) == FAIL)
|
|
||||||
break;
|
|
||||||
if (add_member(&classmembers, varname, varname_end,
|
|
||||||
has_public, type, init_expr) == FAIL)
|
|
||||||
{
|
|
||||||
vim_free(init_expr);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// constructors:
|
// constructors:
|
||||||
// def new()
|
// def new()
|
||||||
// enddef
|
// enddef
|
||||||
// def newOther()
|
// def newOther()
|
||||||
// enddef
|
// enddef
|
||||||
// methods:
|
// object methods and class functions:
|
||||||
// def someMethod()
|
// def SomeMethod()
|
||||||
|
// enddef
|
||||||
|
// static def ClassFunction()
|
||||||
// enddef
|
// enddef
|
||||||
// TODO:
|
// TODO:
|
||||||
// def <Tval> someMethod()
|
// def <Tval> someMethod()
|
||||||
@ -364,6 +345,8 @@ ex_class(exarg_T *eap)
|
|||||||
exarg_T ea;
|
exarg_T ea;
|
||||||
garray_T lines_to_free;
|
garray_T lines_to_free;
|
||||||
|
|
||||||
|
// TODO: error for "public static def Func()"?
|
||||||
|
|
||||||
CLEAR_FIELD(ea);
|
CLEAR_FIELD(ea);
|
||||||
ea.cmd = line;
|
ea.cmd = line;
|
||||||
ea.arg = p;
|
ea.arg = p;
|
||||||
@ -376,13 +359,38 @@ ex_class(exarg_T *eap)
|
|||||||
ga_clear_strings(&lines_to_free);
|
ga_clear_strings(&lines_to_free);
|
||||||
|
|
||||||
// TODO: how about errors?
|
// TODO: how about errors?
|
||||||
if (uf != NULL && ga_grow(&objmethods, 1) == OK)
|
int is_new = STRNCMP(uf->uf_name, "new", 3) == 0;
|
||||||
|
garray_T *fgap = has_static || is_new
|
||||||
|
? &classfunctions : &objmethods;
|
||||||
|
if (uf != NULL && ga_grow(fgap, 1) == OK)
|
||||||
{
|
{
|
||||||
if (STRNCMP(uf->uf_name, "new", 3) == 0)
|
if (is_new)
|
||||||
uf->uf_flags |= FC_NEW;
|
uf->uf_flags |= FC_NEW;
|
||||||
|
|
||||||
((ufunc_T **)objmethods.ga_data)[objmethods.ga_len] = uf;
|
((ufunc_T **)fgap->ga_data)[fgap->ga_len] = uf;
|
||||||
++objmethods.ga_len;
|
++fgap->ga_len;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// class members
|
||||||
|
else if (has_static)
|
||||||
|
{
|
||||||
|
// class members (public, read access, private):
|
||||||
|
// "static _varname"
|
||||||
|
// "static varname"
|
||||||
|
// "public static varname"
|
||||||
|
char_u *varname = p;
|
||||||
|
char_u *varname_end = NULL;
|
||||||
|
type_T *type = NULL;
|
||||||
|
char_u *init_expr = NULL;
|
||||||
|
if (parse_member(eap, line, varname, has_public,
|
||||||
|
&varname_end, &type_list, &type, &init_expr) == FAIL)
|
||||||
|
break;
|
||||||
|
if (add_member(&classmembers, varname, varname_end,
|
||||||
|
has_public, type, init_expr) == FAIL)
|
||||||
|
{
|
||||||
|
vim_free(init_expr);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -445,8 +453,8 @@ 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 < classfunctions.ga_len; ++i)
|
||||||
if (STRCMP(((ufunc_T **)objmethods.ga_data)[i]->uf_name,
|
if (STRCMP(((ufunc_T **)classfunctions.ga_data)[i]->uf_name,
|
||||||
"new") == 0)
|
"new") == 0)
|
||||||
{
|
{
|
||||||
have_new = TRUE;
|
have_new = TRUE;
|
||||||
@ -483,10 +491,10 @@ ex_class(exarg_T *eap)
|
|||||||
ga_clear_strings(&lines_to_free);
|
ga_clear_strings(&lines_to_free);
|
||||||
vim_free(fga.ga_data);
|
vim_free(fga.ga_data);
|
||||||
|
|
||||||
if (nf != NULL && ga_grow(&objmethods, 1) == OK)
|
if (nf != NULL && ga_grow(&classfunctions, 1) == OK)
|
||||||
{
|
{
|
||||||
((ufunc_T **)objmethods.ga_data)[objmethods.ga_len] = nf;
|
((ufunc_T **)classfunctions.ga_data)[classfunctions.ga_len] = nf;
|
||||||
++objmethods.ga_len;
|
++classfunctions.ga_len;
|
||||||
|
|
||||||
nf->uf_flags |= FC_NEW;
|
nf->uf_flags |= FC_NEW;
|
||||||
nf->uf_ret_type = get_type_ptr(&type_list);
|
nf->uf_ret_type = get_type_ptr(&type_list);
|
||||||
@ -500,21 +508,35 @@ ex_class(exarg_T *eap)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: class methods
|
// loop 1: class functions, loop 2: object methods
|
||||||
cl->class_obj_method_count = objmethods.ga_len;
|
for (int loop = 1; loop <= 2; ++loop)
|
||||||
cl->class_obj_methods = ALLOC_MULT(ufunc_T *, objmethods.ga_len);
|
|
||||||
if (cl->class_obj_methods == NULL)
|
|
||||||
goto cleanup;
|
|
||||||
mch_memmove(cl->class_obj_methods, objmethods.ga_data,
|
|
||||||
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];
|
garray_T *gap = loop == 1 ? &classfunctions : &objmethods;
|
||||||
fp->uf_class = cl;
|
int *fcount = loop == 1 ? &cl->class_class_function_count
|
||||||
fp->uf_flags |= FC_OBJECT; // TODO: not for class method
|
: &cl->class_obj_method_count;
|
||||||
|
ufunc_T ***fup = loop == 1 ? &cl->class_class_functions
|
||||||
|
: &cl->class_obj_methods;
|
||||||
|
|
||||||
|
*fcount = gap->ga_len;
|
||||||
|
if (gap->ga_len == 0)
|
||||||
|
{
|
||||||
|
*fup = NULL;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
*fup = ALLOC_MULT(ufunc_T *, gap->ga_len);
|
||||||
|
if (*fup == NULL)
|
||||||
|
goto cleanup;
|
||||||
|
mch_memmove(*fup, gap->ga_data, sizeof(ufunc_T *) * gap->ga_len);
|
||||||
|
vim_free(gap->ga_data);
|
||||||
|
|
||||||
|
// Set the class pointer on all the object methods.
|
||||||
|
for (int i = 0; i < gap->ga_len; ++i)
|
||||||
|
{
|
||||||
|
ufunc_T *fp = (*fup)[i];
|
||||||
|
fp->uf_class = cl;
|
||||||
|
if (loop == 2)
|
||||||
|
fp->uf_flags |= FC_OBJECT;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cl->class_type.tt_type = VAR_CLASS;
|
cl->class_type.tt_type = VAR_CLASS;
|
||||||
@ -539,6 +561,7 @@ cleanup:
|
|||||||
if (cl != NULL)
|
if (cl != NULL)
|
||||||
{
|
{
|
||||||
vim_free(cl->class_name);
|
vim_free(cl->class_name);
|
||||||
|
vim_free(cl->class_class_functions);
|
||||||
vim_free(cl->class_obj_members);
|
vim_free(cl->class_obj_members);
|
||||||
vim_free(cl->class_obj_methods);
|
vim_free(cl->class_obj_methods);
|
||||||
vim_free(cl);
|
vim_free(cl);
|
||||||
@ -565,6 +588,14 @@ cleanup:
|
|||||||
func_clear_free(uf, FALSE);
|
func_clear_free(uf, FALSE);
|
||||||
}
|
}
|
||||||
ga_clear(&objmethods);
|
ga_clear(&objmethods);
|
||||||
|
|
||||||
|
for (int i = 0; i < classfunctions.ga_len; ++i)
|
||||||
|
{
|
||||||
|
ufunc_T *uf = ((ufunc_T **)classfunctions.ga_data)[i];
|
||||||
|
func_clear_free(uf, FALSE);
|
||||||
|
}
|
||||||
|
ga_clear(&classfunctions);
|
||||||
|
|
||||||
clear_type_list(&type_list);
|
clear_type_list(&type_list);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -627,7 +658,7 @@ ex_type(exarg_T *eap UNUSED)
|
|||||||
/*
|
/*
|
||||||
* Evaluate what comes after a class:
|
* Evaluate what comes after a class:
|
||||||
* - class member: SomeClass.varname
|
* - class member: SomeClass.varname
|
||||||
* - class method: SomeClass.SomeMethod()
|
* - class function: SomeClass.SomeMethod()
|
||||||
* - class constructor: SomeClass.new()
|
* - class constructor: SomeClass.new()
|
||||||
* - object member: someObject.varname
|
* - object member: someObject.varname
|
||||||
* - object method: someObject.SomeMethod()
|
* - object method: someObject.SomeMethod()
|
||||||
@ -664,9 +695,13 @@ class_object_index(
|
|||||||
: rettv->vval.v_object->obj_class;
|
: rettv->vval.v_object->obj_class;
|
||||||
if (*name_end == '(')
|
if (*name_end == '(')
|
||||||
{
|
{
|
||||||
for (int i = 0; i < cl->class_obj_method_count; ++i)
|
int on_class = rettv->v_type == VAR_CLASS;
|
||||||
|
int count = on_class ? cl->class_class_function_count
|
||||||
|
: cl->class_obj_method_count;
|
||||||
|
for (int i = 0; i < count; ++i)
|
||||||
{
|
{
|
||||||
ufunc_T *fp = cl->class_obj_methods[i];
|
ufunc_T *fp = on_class ? cl->class_class_functions[i]
|
||||||
|
: cl->class_obj_methods[i];
|
||||||
// Use a separate pointer to avoid that ASAN complains about
|
// Use a separate pointer to avoid that ASAN complains about
|
||||||
// uf_name[] only being 4 characters.
|
// uf_name[] only being 4 characters.
|
||||||
char_u *ufname = (char_u *)fp->uf_name;
|
char_u *ufname = (char_u *)fp->uf_name;
|
||||||
@ -805,9 +840,13 @@ find_class_func(char_u **arg)
|
|||||||
goto fail_after_eval;
|
goto fail_after_eval;
|
||||||
len = fname_end - fname;
|
len = fname_end - fname;
|
||||||
|
|
||||||
for (int i = 0; i < cl->class_obj_method_count; ++i)
|
int count = tv.v_type == VAR_CLASS ? cl->class_class_function_count
|
||||||
|
: cl->class_obj_method_count;
|
||||||
|
ufunc_T **funcs = tv.v_type == VAR_CLASS ? cl->class_class_functions
|
||||||
|
: cl->class_obj_methods;
|
||||||
|
for (int i = 0; i < count; ++i)
|
||||||
{
|
{
|
||||||
ufunc_T *fp = cl->class_obj_methods[i];
|
ufunc_T *fp = funcs[i];
|
||||||
// Use a separate pointer to avoid that ASAN complains about
|
// Use a separate pointer to avoid that ASAN complains about
|
||||||
// uf_name[] only being 4 characters.
|
// uf_name[] only being 4 characters.
|
||||||
char_u *ufname = (char_u *)fp->uf_name;
|
char_u *ufname = (char_u *)fp->uf_name;
|
||||||
@ -823,6 +862,35 @@ fail_after_eval:
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If "cctx->ctx_ufunc" indicates we are in a class, check if "name" is a class
|
||||||
|
* member. If it is then return TRUE and set "cl_ret" and "idx_ret".
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
class_member_exists(
|
||||||
|
char_u *name,
|
||||||
|
class_T **cl_ret,
|
||||||
|
int *idx_ret,
|
||||||
|
cctx_T *cctx)
|
||||||
|
{
|
||||||
|
if (cctx->ctx_ufunc == NULL || cctx->ctx_ufunc->uf_class == NULL)
|
||||||
|
return FALSE;
|
||||||
|
class_T *cl = cctx->ctx_ufunc->uf_class;
|
||||||
|
|
||||||
|
for (int idx = 0; idx < cl->class_class_member_count; ++idx)
|
||||||
|
{
|
||||||
|
ocmember_T *m = &cl->class_class_members[idx];
|
||||||
|
if (STRCMP(m->ocm_name, name) == 0)
|
||||||
|
{
|
||||||
|
*cl_ret = cl;
|
||||||
|
*idx_ret = idx;
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Make a copy of an object.
|
* Make a copy of an object.
|
||||||
*/
|
*/
|
||||||
|
@ -587,7 +587,8 @@ compile_load(
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
lvar_T lvar;
|
lvar_T lvar;
|
||||||
|
class_T *cl = NULL;
|
||||||
|
|
||||||
if (lookup_local(*arg, len, &lvar, cctx) == OK)
|
if (lookup_local(*arg, len, &lvar, cctx) == OK)
|
||||||
{
|
{
|
||||||
@ -602,6 +603,10 @@ compile_load(
|
|||||||
else
|
else
|
||||||
gen_load = TRUE;
|
gen_load = TRUE;
|
||||||
}
|
}
|
||||||
|
else if (class_member_exists(name, &cl, &idx, cctx))
|
||||||
|
{
|
||||||
|
res = generate_CLASSMEMBER(cctx, TRUE, cl, idx);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// "var" can be script-local even without using "s:" if it
|
// "var" can be script-local even without using "s:" if it
|
||||||
|
Loading…
x
Reference in New Issue
Block a user