mirror of
https://github.com/vim/vim.git
synced 2025-09-25 03:54:15 -04:00
patch 9.0.1757: ex_class() function is too long
Problem: ex_class() function is too long Solution: refactor it closes: #12858 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
e1eaae27f4
commit
4b1cc7906f
@@ -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 */
|
||||||
|
/**/
|
||||||
|
1757,
|
||||||
/**/
|
/**/
|
||||||
1756,
|
1756,
|
||||||
/**/
|
/**/
|
||||||
|
888
src/vim9class.c
888
src/vim9class.c
@@ -230,6 +230,276 @@ object_index_from_itf_index(class_T *itf, int is_method, int idx, class_T *cl)
|
|||||||
return table[idx];
|
return table[idx];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check whether a class named "extends_name" is present. If the class is
|
||||||
|
* valid, then "extends_clp" is set with the class pointer.
|
||||||
|
* Returns TRUE if the class name "extends_names" is a valid class.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
validate_extends_class(char_u *extends_name, class_T **extends_clp)
|
||||||
|
{
|
||||||
|
typval_T tv;
|
||||||
|
int success = FALSE;
|
||||||
|
|
||||||
|
tv.v_type = VAR_UNKNOWN;
|
||||||
|
if (eval_variable_import(extends_name, &tv) == FAIL)
|
||||||
|
{
|
||||||
|
semsg(_(e_class_name_not_found_str), extends_name);
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (tv.v_type != VAR_CLASS
|
||||||
|
|| tv.vval.v_class == NULL
|
||||||
|
|| (tv.vval.v_class->class_flags & CLASS_INTERFACE) != 0)
|
||||||
|
semsg(_(e_cannot_extend_str), extends_name);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
class_T *extends_cl = tv.vval.v_class;
|
||||||
|
++extends_cl->class_refcount;
|
||||||
|
*extends_clp = extends_cl;
|
||||||
|
success = TRUE;
|
||||||
|
}
|
||||||
|
clear_tv(&tv);
|
||||||
|
}
|
||||||
|
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check the members of the interface class "ifcl" match the class members
|
||||||
|
* ("classmembers_gap") and object members ("objmembers_gap") of a class.
|
||||||
|
* Returns TRUE if the class and object member names are valid.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
validate_interface_members(
|
||||||
|
char_u *intf_class_name,
|
||||||
|
class_T *ifcl,
|
||||||
|
garray_T *classmembers_gap,
|
||||||
|
garray_T *objmembers_gap)
|
||||||
|
{
|
||||||
|
int success = TRUE;
|
||||||
|
|
||||||
|
for (int loop = 1; loop <= 2 && success; ++loop)
|
||||||
|
{
|
||||||
|
// loop == 1: check class members
|
||||||
|
// loop == 2: check object members
|
||||||
|
int if_count = loop == 1 ? ifcl->class_class_member_count
|
||||||
|
: ifcl->class_obj_member_count;
|
||||||
|
if (if_count == 0)
|
||||||
|
continue;
|
||||||
|
ocmember_T *if_ms = loop == 1 ? ifcl->class_class_members
|
||||||
|
: ifcl->class_obj_members;
|
||||||
|
ocmember_T *cl_ms = (ocmember_T *)(loop == 1
|
||||||
|
? classmembers_gap->ga_data
|
||||||
|
: objmembers_gap->ga_data);
|
||||||
|
int cl_count = loop == 1 ? classmembers_gap->ga_len
|
||||||
|
: objmembers_gap->ga_len;
|
||||||
|
for (int if_i = 0; if_i < if_count; ++if_i)
|
||||||
|
{
|
||||||
|
int cl_i;
|
||||||
|
for (cl_i = 0; cl_i < cl_count; ++cl_i)
|
||||||
|
{
|
||||||
|
ocmember_T *m = &cl_ms[cl_i];
|
||||||
|
where_T where = WHERE_INIT;
|
||||||
|
|
||||||
|
if (STRCMP(if_ms[if_i].ocm_name, m->ocm_name) != 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Ensure the type is matching.
|
||||||
|
where.wt_func_name = (char *)m->ocm_name;
|
||||||
|
where.wt_kind = WT_MEMBER;
|
||||||
|
if (check_type_maybe(if_ms[if_i].ocm_type, m->ocm_type, TRUE,
|
||||||
|
where) != OK)
|
||||||
|
success = FALSE;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (cl_i == cl_count)
|
||||||
|
{
|
||||||
|
semsg(_(e_member_str_of_interface_str_not_implemented),
|
||||||
|
if_ms[if_i].ocm_name, intf_class_name);
|
||||||
|
success = FALSE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check the functions/methods of the interface class "ifcl" match the class
|
||||||
|
* methods ("classfunctions_gap") and object functions ("objmemthods_gap") of a
|
||||||
|
* class.
|
||||||
|
* Returns TRUE if the class and object member names are valid.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
validate_interface_methods(
|
||||||
|
char_u *intf_class_name,
|
||||||
|
class_T *ifcl,
|
||||||
|
garray_T *classfunctions_gap,
|
||||||
|
garray_T *objmethods_gap)
|
||||||
|
{
|
||||||
|
int success = TRUE;
|
||||||
|
|
||||||
|
for (int loop = 1; loop <= 2 && success; ++loop)
|
||||||
|
{
|
||||||
|
// loop == 1: check class functions
|
||||||
|
// loop == 2: check object methods
|
||||||
|
int if_count = loop == 1 ? ifcl->class_class_function_count
|
||||||
|
: ifcl->class_obj_method_count;
|
||||||
|
if (if_count == 0)
|
||||||
|
continue;
|
||||||
|
ufunc_T **if_fp = loop == 1 ? ifcl->class_class_functions
|
||||||
|
: ifcl->class_obj_methods;
|
||||||
|
ufunc_T **cl_fp = (ufunc_T **)(loop == 1
|
||||||
|
? classfunctions_gap->ga_data
|
||||||
|
: objmethods_gap->ga_data);
|
||||||
|
int cl_count = loop == 1 ? classfunctions_gap->ga_len
|
||||||
|
: objmethods_gap->ga_len;
|
||||||
|
for (int if_i = 0; if_i < if_count; ++if_i)
|
||||||
|
{
|
||||||
|
char_u *if_name = if_fp[if_i]->uf_name;
|
||||||
|
int cl_i;
|
||||||
|
for (cl_i = 0; cl_i < cl_count; ++cl_i)
|
||||||
|
{
|
||||||
|
char_u *cl_name = cl_fp[cl_i]->uf_name;
|
||||||
|
if (STRCMP(if_name, cl_name) == 0)
|
||||||
|
{
|
||||||
|
where_T where = WHERE_INIT;
|
||||||
|
|
||||||
|
// Ensure the type is matching.
|
||||||
|
where.wt_func_name = (char *)if_name;
|
||||||
|
where.wt_kind = WT_METHOD;
|
||||||
|
if (check_type_maybe(if_fp[if_i]->uf_func_type,
|
||||||
|
cl_fp[cl_i]->uf_func_type, TRUE, where) != OK)
|
||||||
|
success = FALSE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (cl_i == cl_count)
|
||||||
|
{
|
||||||
|
semsg(_(e_function_str_of_interface_str_not_implemented),
|
||||||
|
if_name, intf_class_name);
|
||||||
|
success = FALSE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Validate all the "implements" classes when creating a new class. The
|
||||||
|
* classes are returned in "intf_classes". The class functions, class methods,
|
||||||
|
* object methods and object members in the new class are in
|
||||||
|
* "classfunctions_gap", "classmembers_gap", "objmethods_gap", and
|
||||||
|
* "objmembers_gap" respectively.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
validate_implements_classes(
|
||||||
|
garray_T *impl_gap,
|
||||||
|
class_T **intf_classes,
|
||||||
|
garray_T *classfunctions_gap,
|
||||||
|
garray_T *classmembers_gap,
|
||||||
|
garray_T *objmethods_gap,
|
||||||
|
garray_T *objmembers_gap)
|
||||||
|
{
|
||||||
|
int success = TRUE;
|
||||||
|
|
||||||
|
for (int i = 0; i < impl_gap->ga_len && success; ++i)
|
||||||
|
{
|
||||||
|
char_u *impl = ((char_u **)impl_gap->ga_data)[i];
|
||||||
|
typval_T tv;
|
||||||
|
tv.v_type = VAR_UNKNOWN;
|
||||||
|
if (eval_variable_import(impl, &tv) == FAIL)
|
||||||
|
{
|
||||||
|
semsg(_(e_interface_name_not_found_str), impl);
|
||||||
|
success = FALSE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tv.v_type != VAR_CLASS
|
||||||
|
|| tv.vval.v_class == NULL
|
||||||
|
|| (tv.vval.v_class->class_flags & CLASS_INTERFACE) == 0)
|
||||||
|
{
|
||||||
|
semsg(_(e_not_valid_interface_str), impl);
|
||||||
|
success = FALSE;
|
||||||
|
clear_tv(&tv);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
class_T *ifcl = tv.vval.v_class;
|
||||||
|
intf_classes[i] = ifcl;
|
||||||
|
++ifcl->class_refcount;
|
||||||
|
|
||||||
|
// check the members of the interface match the members of the class
|
||||||
|
success = validate_interface_members(impl, ifcl, classmembers_gap,
|
||||||
|
objmembers_gap);
|
||||||
|
|
||||||
|
// check the functions/methods of the interface match the
|
||||||
|
// functions/methods of the class
|
||||||
|
success = validate_interface_methods(impl, ifcl, classfunctions_gap,
|
||||||
|
objmethods_gap);
|
||||||
|
clear_tv(&tv);
|
||||||
|
}
|
||||||
|
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check no function argument name is used as a class member.
|
||||||
|
* (Object members are always accessed with "this." prefix, so no need
|
||||||
|
* to check them.)
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
check_func_arg_names(
|
||||||
|
garray_T *classfunctions_gap,
|
||||||
|
garray_T *objmethods_gap,
|
||||||
|
garray_T *classmembers_gap)
|
||||||
|
{
|
||||||
|
int success = TRUE;
|
||||||
|
|
||||||
|
// loop 1: class functions, loop 2: object methods
|
||||||
|
for (int loop = 1; loop <= 2 && success; ++loop)
|
||||||
|
{
|
||||||
|
garray_T *gap = loop == 1 ? classfunctions_gap : objmethods_gap;
|
||||||
|
|
||||||
|
for (int fi = 0; fi < gap->ga_len && success; ++fi)
|
||||||
|
{
|
||||||
|
ufunc_T *uf = ((ufunc_T **)gap->ga_data)[fi];
|
||||||
|
|
||||||
|
for (int i = 0; i < uf->uf_args.ga_len && success; ++i)
|
||||||
|
{
|
||||||
|
char_u *aname = ((char_u **)uf->uf_args.ga_data)[i];
|
||||||
|
garray_T *mgap = classmembers_gap;
|
||||||
|
|
||||||
|
// Check all the class member names
|
||||||
|
for (int mi = 0; mi < mgap->ga_len; ++mi)
|
||||||
|
{
|
||||||
|
char_u *mname = ((ocmember_T *)mgap->ga_data + mi)
|
||||||
|
->ocm_name;
|
||||||
|
if (STRCMP(aname, mname) == 0)
|
||||||
|
{
|
||||||
|
success = FALSE;
|
||||||
|
|
||||||
|
if (uf->uf_script_ctx.sc_sid > 0)
|
||||||
|
SOURCING_LNUM = uf->uf_script_ctx.sc_lnum;
|
||||||
|
|
||||||
|
semsg(_(e_argument_already_declared_in_class_str),
|
||||||
|
aname);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Update the interface class lookup table for the member index on the
|
* Update the interface class lookup table for the member index on the
|
||||||
* interface to the member index in the class implementing the interface.
|
* interface to the member index in the class implementing the interface.
|
||||||
@@ -341,6 +611,238 @@ update_member_method_lookup_table(
|
|||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Update the member and object method lookup tables for a new class in the
|
||||||
|
* interface class.
|
||||||
|
* For each interface add a lookup table for the member index on the interface
|
||||||
|
* to the member index in the new class. And a lookup table for the object
|
||||||
|
* method index on the interface to the object method index in the new class.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
add_lookup_tables(class_T *cl, class_T *extends_cl, garray_T *objmethods_gap)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < cl->class_interface_count; ++i)
|
||||||
|
{
|
||||||
|
class_T *ifcl = cl->class_interfaces_cl[i];
|
||||||
|
|
||||||
|
if (update_member_method_lookup_table(ifcl, cl, objmethods_gap,
|
||||||
|
0, TRUE) == FAIL)
|
||||||
|
return FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the lookup table for the extended class, if nay
|
||||||
|
if (extends_cl != NULL)
|
||||||
|
{
|
||||||
|
class_T *pclass = extends_cl;
|
||||||
|
int pobj_method_offset = objmethods_gap->ga_len;
|
||||||
|
|
||||||
|
// Update the entire lineage of extended classes.
|
||||||
|
while (pclass != NULL)
|
||||||
|
{
|
||||||
|
if (update_member_method_lookup_table(pclass, cl,
|
||||||
|
objmethods_gap, pobj_method_offset, FALSE) == FAIL)
|
||||||
|
return FAIL;
|
||||||
|
|
||||||
|
pobj_method_offset += pclass->class_obj_method_count_child;
|
||||||
|
pclass = pclass->class_extends;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Add class members to a new class. Allocate a typval for each class member
|
||||||
|
* and initialize it.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
add_class_members(class_T *cl, exarg_T *eap)
|
||||||
|
{
|
||||||
|
// Allocate a typval for each class member and initialize it.
|
||||||
|
cl->class_members_tv = ALLOC_CLEAR_MULT(typval_T,
|
||||||
|
cl->class_class_member_count);
|
||||||
|
if (cl->class_members_tv == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (int i = 0; i < cl->class_class_member_count; ++i)
|
||||||
|
{
|
||||||
|
ocmember_T *m = &cl->class_class_members[i];
|
||||||
|
typval_T *tv = &cl->class_members_tv[i];
|
||||||
|
if (m->ocm_init != NULL)
|
||||||
|
{
|
||||||
|
typval_T *etv = eval_expr(m->ocm_init, eap);
|
||||||
|
if (etv != NULL)
|
||||||
|
{
|
||||||
|
*tv = *etv;
|
||||||
|
vim_free(etv);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// TODO: proper default value
|
||||||
|
tv->v_type = m->ocm_type->tt_type;
|
||||||
|
tv->vval.v_string = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Add a default constructor to the class "cl".
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
add_default_constructor(
|
||||||
|
class_T *cl,
|
||||||
|
garray_T *classfunctions_gap,
|
||||||
|
garray_T *type_list_gap)
|
||||||
|
{
|
||||||
|
garray_T fga;
|
||||||
|
|
||||||
|
ga_init2(&fga, 1, 1000);
|
||||||
|
ga_concat(&fga, (char_u *)"new(");
|
||||||
|
for (int i = 0; i < cl->class_obj_member_count; ++i)
|
||||||
|
{
|
||||||
|
if (i > 0)
|
||||||
|
ga_concat(&fga, (char_u *)", ");
|
||||||
|
ga_concat(&fga, (char_u *)"this.");
|
||||||
|
ocmember_T *m = cl->class_obj_members + i;
|
||||||
|
ga_concat(&fga, (char_u *)m->ocm_name);
|
||||||
|
ga_concat(&fga, (char_u *)" = v:none");
|
||||||
|
}
|
||||||
|
ga_concat(&fga, (char_u *)")\nenddef\n");
|
||||||
|
ga_append(&fga, NUL);
|
||||||
|
|
||||||
|
exarg_T fea;
|
||||||
|
CLEAR_FIELD(fea);
|
||||||
|
fea.cmdidx = CMD_def;
|
||||||
|
fea.cmd = fea.arg = fga.ga_data;
|
||||||
|
|
||||||
|
garray_T lines_to_free;
|
||||||
|
ga_init2(&lines_to_free, sizeof(char_u *), 50);
|
||||||
|
|
||||||
|
ufunc_T *nf = define_function(&fea, NULL, &lines_to_free, CF_CLASS);
|
||||||
|
|
||||||
|
ga_clear_strings(&lines_to_free);
|
||||||
|
vim_free(fga.ga_data);
|
||||||
|
|
||||||
|
if (nf != NULL && ga_grow(classfunctions_gap, 1) == OK)
|
||||||
|
{
|
||||||
|
((ufunc_T **)classfunctions_gap->ga_data)[classfunctions_gap->ga_len]
|
||||||
|
= nf;
|
||||||
|
++classfunctions_gap->ga_len;
|
||||||
|
|
||||||
|
nf->uf_flags |= FC_NEW;
|
||||||
|
nf->uf_ret_type = get_type_ptr(type_list_gap);
|
||||||
|
if (nf->uf_ret_type != NULL)
|
||||||
|
{
|
||||||
|
nf->uf_ret_type->tt_type = VAR_OBJECT;
|
||||||
|
nf->uf_ret_type->tt_class = cl;
|
||||||
|
nf->uf_ret_type->tt_argcount = 0;
|
||||||
|
nf->uf_ret_type->tt_args = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Add the class functions and object methods to the new class "cl".
|
||||||
|
* When extending a class, add the functions and methods from the parent class
|
||||||
|
* also.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
add_classfuncs_objmethods(
|
||||||
|
class_T *cl,
|
||||||
|
class_T *extends_cl,
|
||||||
|
garray_T *classfunctions_gap,
|
||||||
|
garray_T *objmethods_gap)
|
||||||
|
{
|
||||||
|
// loop 1: class functions, loop 2: object methods
|
||||||
|
for (int loop = 1; loop <= 2; ++loop)
|
||||||
|
{
|
||||||
|
garray_T *gap = loop == 1 ? classfunctions_gap : objmethods_gap;
|
||||||
|
int *fcount = loop == 1 ? &cl->class_class_function_count
|
||||||
|
: &cl->class_obj_method_count;
|
||||||
|
ufunc_T ***fup = loop == 1 ? &cl->class_class_functions
|
||||||
|
: &cl->class_obj_methods;
|
||||||
|
|
||||||
|
int parent_count = 0;
|
||||||
|
if (extends_cl != NULL)
|
||||||
|
// Include functions from the parent.
|
||||||
|
parent_count = loop == 1
|
||||||
|
? extends_cl->class_class_function_count
|
||||||
|
: extends_cl->class_obj_method_count;
|
||||||
|
|
||||||
|
*fcount = parent_count + gap->ga_len;
|
||||||
|
if (*fcount == 0)
|
||||||
|
{
|
||||||
|
*fup = NULL;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
*fup = ALLOC_MULT(ufunc_T *, *fcount);
|
||||||
|
if (*fup == NULL)
|
||||||
|
return FAIL;
|
||||||
|
|
||||||
|
if (gap->ga_len != 0)
|
||||||
|
mch_memmove(*fup, gap->ga_data, sizeof(ufunc_T *) * gap->ga_len);
|
||||||
|
vim_free(gap->ga_data);
|
||||||
|
if (loop == 1)
|
||||||
|
cl->class_class_function_count_child = gap->ga_len;
|
||||||
|
else
|
||||||
|
cl->class_obj_method_count_child = gap->ga_len;
|
||||||
|
|
||||||
|
int skipped = 0;
|
||||||
|
for (int i = 0; i < parent_count; ++i)
|
||||||
|
{
|
||||||
|
// Copy functions from the parent. Can't use the same
|
||||||
|
// function, because "uf_class" is different and compilation
|
||||||
|
// will have a different result.
|
||||||
|
// Put them after the functions in the current class, object
|
||||||
|
// methods may be overruled, then "super.Method()" is used to
|
||||||
|
// find a method from the parent.
|
||||||
|
// Skip "new" functions. TODO: not all of them.
|
||||||
|
if (loop == 1 && STRNCMP(
|
||||||
|
extends_cl->class_class_functions[i]->uf_name,
|
||||||
|
"new", 3) == 0)
|
||||||
|
++skipped;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ufunc_T *pf = (loop == 1
|
||||||
|
? extends_cl->class_class_functions
|
||||||
|
: extends_cl->class_obj_methods)[i];
|
||||||
|
(*fup)[gap->ga_len + i - skipped] = copy_function(pf);
|
||||||
|
|
||||||
|
// If the child class overrides a function from the parent
|
||||||
|
// the signature must be equal.
|
||||||
|
char_u *pname = pf->uf_name;
|
||||||
|
for (int ci = 0; ci < gap->ga_len; ++ci)
|
||||||
|
{
|
||||||
|
ufunc_T *cf = (*fup)[ci];
|
||||||
|
char_u *cname = cf->uf_name;
|
||||||
|
if (STRCMP(pname, cname) == 0)
|
||||||
|
{
|
||||||
|
where_T where = WHERE_INIT;
|
||||||
|
where.wt_func_name = (char *)pname;
|
||||||
|
where.wt_kind = WT_METHOD;
|
||||||
|
(void)check_type(pf->uf_func_type, cf->uf_func_type,
|
||||||
|
TRUE, where);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*fcount -= skipped;
|
||||||
|
|
||||||
|
// Set the class pointer on all the functions and object methods.
|
||||||
|
for (int i = 0; i < *fcount; ++i)
|
||||||
|
{
|
||||||
|
ufunc_T *fp = (*fup)[i];
|
||||||
|
fp->uf_class = cl;
|
||||||
|
if (loop == 2)
|
||||||
|
fp->uf_flags |= FC_OBJECT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Handle ":class" and ":abstract class" up to ":endclass".
|
* Handle ":class" and ":abstract class" up to ":endclass".
|
||||||
* Handle ":interface" up to ":endinterface".
|
* Handle ":interface" up to ":endinterface".
|
||||||
@@ -714,31 +1216,7 @@ early_ret:
|
|||||||
|
|
||||||
// Check the "extends" class is valid.
|
// Check the "extends" class is valid.
|
||||||
if (success && extends != NULL)
|
if (success && extends != NULL)
|
||||||
{
|
success = validate_extends_class(extends, &extends_cl);
|
||||||
typval_T tv;
|
|
||||||
tv.v_type = VAR_UNKNOWN;
|
|
||||||
if (eval_variable_import(extends, &tv) == FAIL)
|
|
||||||
{
|
|
||||||
semsg(_(e_class_name_not_found_str), extends);
|
|
||||||
success = FALSE;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (tv.v_type != VAR_CLASS
|
|
||||||
|| tv.vval.v_class == NULL
|
|
||||||
|| (tv.vval.v_class->class_flags & CLASS_INTERFACE) != 0)
|
|
||||||
{
|
|
||||||
semsg(_(e_cannot_extend_str), extends);
|
|
||||||
success = FALSE;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
extends_cl = tv.vval.v_class;
|
|
||||||
++extends_cl->class_refcount;
|
|
||||||
}
|
|
||||||
clear_tv(&tv);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
VIM_CLEAR(extends);
|
VIM_CLEAR(extends);
|
||||||
|
|
||||||
class_T **intf_classes = NULL;
|
class_T **intf_classes = NULL;
|
||||||
@@ -748,168 +1226,15 @@ early_ret:
|
|||||||
{
|
{
|
||||||
intf_classes = ALLOC_CLEAR_MULT(class_T *, ga_impl.ga_len);
|
intf_classes = ALLOC_CLEAR_MULT(class_T *, ga_impl.ga_len);
|
||||||
|
|
||||||
for (int i = 0; i < ga_impl.ga_len && success; ++i)
|
success = validate_implements_classes(&ga_impl, intf_classes,
|
||||||
{
|
&classfunctions, &classmembers,
|
||||||
char_u *impl = ((char_u **)ga_impl.ga_data)[i];
|
&objmethods, &objmembers);
|
||||||
typval_T tv;
|
|
||||||
tv.v_type = VAR_UNKNOWN;
|
|
||||||
if (eval_variable_import(impl, &tv) == FAIL)
|
|
||||||
{
|
|
||||||
semsg(_(e_interface_name_not_found_str), impl);
|
|
||||||
success = FALSE;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tv.v_type != VAR_CLASS
|
|
||||||
|| tv.vval.v_class == NULL
|
|
||||||
|| (tv.vval.v_class->class_flags & CLASS_INTERFACE) == 0)
|
|
||||||
{
|
|
||||||
semsg(_(e_not_valid_interface_str), impl);
|
|
||||||
success = FALSE;
|
|
||||||
clear_tv(&tv);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
class_T *ifcl = tv.vval.v_class;
|
|
||||||
intf_classes[i] = ifcl;
|
|
||||||
++ifcl->class_refcount;
|
|
||||||
|
|
||||||
// check the members of the interface match the members of the class
|
|
||||||
for (int loop = 1; loop <= 2 && success; ++loop)
|
|
||||||
{
|
|
||||||
// loop == 1: check class members
|
|
||||||
// loop == 2: check object members
|
|
||||||
int if_count = loop == 1 ? ifcl->class_class_member_count
|
|
||||||
: ifcl->class_obj_member_count;
|
|
||||||
if (if_count == 0)
|
|
||||||
continue;
|
|
||||||
ocmember_T *if_ms = loop == 1 ? ifcl->class_class_members
|
|
||||||
: ifcl->class_obj_members;
|
|
||||||
ocmember_T *cl_ms = (ocmember_T *)(loop == 1
|
|
||||||
? classmembers.ga_data
|
|
||||||
: objmembers.ga_data);
|
|
||||||
int cl_count = loop == 1 ? classmembers.ga_len
|
|
||||||
: objmembers.ga_len;
|
|
||||||
for (int if_i = 0; if_i < if_count; ++if_i)
|
|
||||||
{
|
|
||||||
int cl_i;
|
|
||||||
for (cl_i = 0; cl_i < cl_count; ++cl_i)
|
|
||||||
{
|
|
||||||
ocmember_T *m = &cl_ms[cl_i];
|
|
||||||
where_T where = WHERE_INIT;
|
|
||||||
|
|
||||||
if (STRCMP(if_ms[if_i].ocm_name, m->ocm_name) != 0)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// Ensure the type is matching.
|
|
||||||
where.wt_func_name = (char *)m->ocm_name;
|
|
||||||
where.wt_kind = WT_MEMBER;
|
|
||||||
if (check_type_maybe(if_ms[if_i].ocm_type, m->ocm_type, TRUE,
|
|
||||||
where) != OK)
|
|
||||||
success = FALSE;
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (cl_i == cl_count)
|
|
||||||
{
|
|
||||||
semsg(_(e_member_str_of_interface_str_not_implemented),
|
|
||||||
if_ms[if_i].ocm_name, impl);
|
|
||||||
success = FALSE;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// check the functions/methods of the interface match the
|
|
||||||
// functions/methods of the class
|
|
||||||
for (int loop = 1; loop <= 2 && success; ++loop)
|
|
||||||
{
|
|
||||||
// loop == 1: check class functions
|
|
||||||
// loop == 2: check object methods
|
|
||||||
int if_count = loop == 1 ? ifcl->class_class_function_count
|
|
||||||
: ifcl->class_obj_method_count;
|
|
||||||
if (if_count == 0)
|
|
||||||
continue;
|
|
||||||
ufunc_T **if_fp = loop == 1 ? ifcl->class_class_functions
|
|
||||||
: ifcl->class_obj_methods;
|
|
||||||
ufunc_T **cl_fp = (ufunc_T **)(loop == 1
|
|
||||||
? classfunctions.ga_data
|
|
||||||
: objmethods.ga_data);
|
|
||||||
int cl_count = loop == 1 ? classfunctions.ga_len
|
|
||||||
: objmethods.ga_len;
|
|
||||||
for (int if_i = 0; if_i < if_count; ++if_i)
|
|
||||||
{
|
|
||||||
char_u *if_name = if_fp[if_i]->uf_name;
|
|
||||||
int cl_i;
|
|
||||||
for (cl_i = 0; cl_i < cl_count; ++cl_i)
|
|
||||||
{
|
|
||||||
char_u *cl_name = cl_fp[cl_i]->uf_name;
|
|
||||||
if (STRCMP(if_name, cl_name) == 0)
|
|
||||||
{
|
|
||||||
where_T where = WHERE_INIT;
|
|
||||||
|
|
||||||
// Ensure the type is matching.
|
|
||||||
where.wt_func_name = (char *)if_name;
|
|
||||||
where.wt_kind = WT_METHOD;
|
|
||||||
if (check_type_maybe(if_fp[if_i]->uf_func_type,
|
|
||||||
cl_fp[cl_i]->uf_func_type, TRUE, where) != OK)
|
|
||||||
success = FALSE;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (cl_i == cl_count)
|
|
||||||
{
|
|
||||||
semsg(_(e_function_str_of_interface_str_not_implemented),
|
|
||||||
if_name, impl);
|
|
||||||
success = FALSE;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
clear_tv(&tv);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (success)
|
|
||||||
{
|
|
||||||
// Check no function argument name is used as a class member.
|
// Check no function argument name is used as a class member.
|
||||||
// (Object members are always accessed with "this." prefix, so no need
|
if (success)
|
||||||
// to check them.)
|
success = check_func_arg_names(&classfunctions, &objmethods,
|
||||||
for (int loop = 1; loop <= 2 && success; ++loop)
|
&classmembers);
|
||||||
{
|
|
||||||
garray_T *gap = loop == 1 ? &classfunctions : &objmethods;
|
|
||||||
|
|
||||||
for (int fi = 0; fi < gap->ga_len && success; ++fi)
|
|
||||||
{
|
|
||||||
ufunc_T *uf = ((ufunc_T **)gap->ga_data)[fi];
|
|
||||||
|
|
||||||
for (int i = 0; i < uf->uf_args.ga_len && success; ++i)
|
|
||||||
{
|
|
||||||
char_u *aname = ((char_u **)uf->uf_args.ga_data)[i];
|
|
||||||
garray_T *mgap = &classmembers;
|
|
||||||
|
|
||||||
for (int mi = 0; mi < mgap->ga_len; ++mi)
|
|
||||||
{
|
|
||||||
char_u *mname = ((ocmember_T *)mgap->ga_data + mi)
|
|
||||||
->ocm_name;
|
|
||||||
if (STRCMP(aname, mname) == 0)
|
|
||||||
{
|
|
||||||
success = FALSE;
|
|
||||||
|
|
||||||
if (uf->uf_script_ctx.sc_sid > 0)
|
|
||||||
SOURCING_LNUM = uf->uf_script_ctx.sc_lnum;
|
|
||||||
|
|
||||||
semsg(_(e_argument_already_declared_in_class_str),
|
|
||||||
aname);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class_T *cl = NULL;
|
class_T *cl = NULL;
|
||||||
if (success)
|
if (success)
|
||||||
@@ -968,66 +1293,15 @@ early_ret:
|
|||||||
|
|
||||||
if (cl->class_interface_count > 0 || extends_cl != NULL)
|
if (cl->class_interface_count > 0 || extends_cl != NULL)
|
||||||
{
|
{
|
||||||
// For each interface add a lookup table for the member index on
|
// Add a method and member lookup table to each of the interface
|
||||||
// the interface to the member index in this class.
|
// classes.
|
||||||
// And a lookup table for the object method index on the interface
|
if (add_lookup_tables(cl, extends_cl, &objmethods) == FAIL)
|
||||||
// to the object method index in this class.
|
|
||||||
for (int i = 0; i < cl->class_interface_count; ++i)
|
|
||||||
{
|
|
||||||
class_T *ifcl = cl->class_interfaces_cl[i];
|
|
||||||
|
|
||||||
if (update_member_method_lookup_table(ifcl, cl, &objmethods,
|
|
||||||
0, TRUE) == FAIL)
|
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the lookup table for the extended class, if nay
|
|
||||||
if (extends_cl != NULL)
|
|
||||||
{
|
|
||||||
class_T *pclass = extends_cl;
|
|
||||||
int pobj_method_offset = objmethods.ga_len;
|
|
||||||
|
|
||||||
// Update the entire lineage of extended classes.
|
|
||||||
while (pclass != NULL)
|
|
||||||
{
|
|
||||||
if (update_member_method_lookup_table(pclass, cl,
|
|
||||||
&objmethods, pobj_method_offset, FALSE) ==
|
|
||||||
FAIL)
|
|
||||||
goto cleanup;
|
|
||||||
|
|
||||||
pobj_method_offset += pclass->class_obj_method_count_child;
|
|
||||||
pclass = pclass->class_extends;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (is_class && cl->class_class_member_count > 0)
|
|
||||||
{
|
|
||||||
// Allocate a typval for each class member and initialize it.
|
// Allocate a typval for each class member and initialize it.
|
||||||
cl->class_members_tv = ALLOC_CLEAR_MULT(typval_T,
|
if (is_class && cl->class_class_member_count > 0)
|
||||||
cl->class_class_member_count);
|
add_class_members(cl, eap);
|
||||||
if (cl->class_members_tv != NULL)
|
|
||||||
for (int i = 0; i < cl->class_class_member_count; ++i)
|
|
||||||
{
|
|
||||||
ocmember_T *m = &cl->class_class_members[i];
|
|
||||||
typval_T *tv = &cl->class_members_tv[i];
|
|
||||||
if (m->ocm_init != NULL)
|
|
||||||
{
|
|
||||||
typval_T *etv = eval_expr(m->ocm_init, eap);
|
|
||||||
if (etv != NULL)
|
|
||||||
{
|
|
||||||
*tv = *etv;
|
|
||||||
vim_free(etv);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// TODO: proper default value
|
|
||||||
tv->v_type = m->ocm_type->tt_type;
|
|
||||||
tv->vval.v_string = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int have_new = FALSE;
|
int have_new = FALSE;
|
||||||
for (int i = 0; i < classfunctions.ga_len; ++i)
|
for (int i = 0; i < classfunctions.ga_len; ++i)
|
||||||
@@ -1038,142 +1312,14 @@ early_ret:
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (is_class && !is_abstract && !have_new)
|
if (is_class && !is_abstract && !have_new)
|
||||||
{
|
|
||||||
// No new() method was defined, add the default constructor.
|
// No new() method was defined, add the default constructor.
|
||||||
garray_T fga;
|
add_default_constructor(cl, &classfunctions, &type_list);
|
||||||
ga_init2(&fga, 1, 1000);
|
|
||||||
ga_concat(&fga, (char_u *)"new(");
|
|
||||||
for (int i = 0; i < cl->class_obj_member_count; ++i)
|
|
||||||
{
|
|
||||||
if (i > 0)
|
|
||||||
ga_concat(&fga, (char_u *)", ");
|
|
||||||
ga_concat(&fga, (char_u *)"this.");
|
|
||||||
ocmember_T *m = cl->class_obj_members + i;
|
|
||||||
ga_concat(&fga, (char_u *)m->ocm_name);
|
|
||||||
ga_concat(&fga, (char_u *)" = v:none");
|
|
||||||
}
|
|
||||||
ga_concat(&fga, (char_u *)")\nenddef\n");
|
|
||||||
ga_append(&fga, NUL);
|
|
||||||
|
|
||||||
exarg_T fea;
|
|
||||||
CLEAR_FIELD(fea);
|
|
||||||
fea.cmdidx = CMD_def;
|
|
||||||
fea.cmd = fea.arg = fga.ga_data;
|
|
||||||
|
|
||||||
garray_T lines_to_free;
|
|
||||||
ga_init2(&lines_to_free, sizeof(char_u *), 50);
|
|
||||||
|
|
||||||
ufunc_T *nf = define_function(&fea, NULL, &lines_to_free, CF_CLASS);
|
|
||||||
|
|
||||||
ga_clear_strings(&lines_to_free);
|
|
||||||
vim_free(fga.ga_data);
|
|
||||||
|
|
||||||
if (nf != NULL && ga_grow(&classfunctions, 1) == OK)
|
|
||||||
{
|
|
||||||
((ufunc_T **)classfunctions.ga_data)[classfunctions.ga_len]
|
|
||||||
= nf;
|
|
||||||
++classfunctions.ga_len;
|
|
||||||
|
|
||||||
nf->uf_flags |= FC_NEW;
|
|
||||||
nf->uf_ret_type = get_type_ptr(&type_list);
|
|
||||||
if (nf->uf_ret_type != NULL)
|
|
||||||
{
|
|
||||||
nf->uf_ret_type->tt_type = VAR_OBJECT;
|
|
||||||
nf->uf_ret_type->tt_class = cl;
|
|
||||||
nf->uf_ret_type->tt_argcount = 0;
|
|
||||||
nf->uf_ret_type->tt_args = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Move all the functions into the created class.
|
// Move all the functions into the created class.
|
||||||
// loop 1: class functions, loop 2: object methods
|
if (add_classfuncs_objmethods(cl, extends_cl, &classfunctions,
|
||||||
for (int loop = 1; loop <= 2; ++loop)
|
&objmethods) == FAIL)
|
||||||
{
|
|
||||||
garray_T *gap = loop == 1 ? &classfunctions : &objmethods;
|
|
||||||
int *fcount = loop == 1 ? &cl->class_class_function_count
|
|
||||||
: &cl->class_obj_method_count;
|
|
||||||
ufunc_T ***fup = loop == 1 ? &cl->class_class_functions
|
|
||||||
: &cl->class_obj_methods;
|
|
||||||
|
|
||||||
int parent_count = 0;
|
|
||||||
if (extends_cl != NULL)
|
|
||||||
// Include functions from the parent.
|
|
||||||
parent_count = loop == 1
|
|
||||||
? extends_cl->class_class_function_count
|
|
||||||
: extends_cl->class_obj_method_count;
|
|
||||||
|
|
||||||
*fcount = parent_count + gap->ga_len;
|
|
||||||
if (*fcount == 0)
|
|
||||||
{
|
|
||||||
*fup = NULL;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
*fup = ALLOC_MULT(ufunc_T *, *fcount);
|
|
||||||
if (*fup == NULL)
|
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
|
|
||||||
if (gap->ga_len != 0)
|
|
||||||
mch_memmove(*fup, gap->ga_data,
|
|
||||||
sizeof(ufunc_T *) * gap->ga_len);
|
|
||||||
vim_free(gap->ga_data);
|
|
||||||
if (loop == 1)
|
|
||||||
cl->class_class_function_count_child = gap->ga_len;
|
|
||||||
else
|
|
||||||
cl->class_obj_method_count_child = gap->ga_len;
|
|
||||||
|
|
||||||
int skipped = 0;
|
|
||||||
for (int i = 0; i < parent_count; ++i)
|
|
||||||
{
|
|
||||||
// Copy functions from the parent. Can't use the same
|
|
||||||
// function, because "uf_class" is different and compilation
|
|
||||||
// will have a different result.
|
|
||||||
// Put them after the functions in the current class, object
|
|
||||||
// methods may be overruled, then "super.Method()" is used to
|
|
||||||
// find a method from the parent.
|
|
||||||
// Skip "new" functions. TODO: not all of them.
|
|
||||||
if (loop == 1 && STRNCMP(
|
|
||||||
extends_cl->class_class_functions[i]->uf_name,
|
|
||||||
"new", 3) == 0)
|
|
||||||
++skipped;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ufunc_T *pf = (loop == 1
|
|
||||||
? extends_cl->class_class_functions
|
|
||||||
: extends_cl->class_obj_methods)[i];
|
|
||||||
(*fup)[gap->ga_len + i - skipped] = copy_function(pf);
|
|
||||||
|
|
||||||
// If the child class overrides a function from the parent
|
|
||||||
// the signature must be equal.
|
|
||||||
char_u *pname = pf->uf_name;
|
|
||||||
for (int ci = 0; ci < gap->ga_len; ++ci)
|
|
||||||
{
|
|
||||||
ufunc_T *cf = (*fup)[ci];
|
|
||||||
char_u *cname = cf->uf_name;
|
|
||||||
if (STRCMP(pname, cname) == 0)
|
|
||||||
{
|
|
||||||
where_T where = WHERE_INIT;
|
|
||||||
where.wt_func_name = (char *)pname;
|
|
||||||
where.wt_kind = WT_METHOD;
|
|
||||||
(void)check_type(pf->uf_func_type, cf->uf_func_type,
|
|
||||||
TRUE, where);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
*fcount -= skipped;
|
|
||||||
|
|
||||||
// Set the class pointer on all the functions and object methods.
|
|
||||||
for (int i = 0; i < *fcount; ++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;
|
||||||
cl->class_type.tt_class = cl;
|
cl->class_type.tt_class = cl;
|
||||||
cl->class_object_type.tt_type = VAR_OBJECT;
|
cl->class_object_type.tt_type = VAR_OBJECT;
|
||||||
|
Reference in New Issue
Block a user