0
0
mirror of https://github.com/vim/vim.git synced 2025-09-23 03:43:49 -04:00

patch 9.0.1152: class "implements" argument not implemented

Problem:    Class "implements" argument not implemented.
Solution:   Implement "implements" argument.  Add basic checks for when a
            class implements an interface.
This commit is contained in:
Bram Moolenaar
2023-01-06 18:42:20 +00:00
parent 5bcd29b84e
commit 94674f2223
8 changed files with 249 additions and 14 deletions

View File

@@ -227,15 +227,50 @@ ex_class(exarg_T *eap)
semsg(_(e_white_space_required_after_name_str), arg);
return;
}
char_u *name_start = arg;
// TODO:
// generics: <Tkey, Tentry>
// extends SomeClass
// implements SomeInterface
// specifies SomeInterface
// check that nothing follows
// handle "is_export" if it is set
// Names for "implements SomeInterface"
garray_T ga_impl;
ga_init2(&ga_impl, sizeof(char_u *), 5);
arg = skipwhite(name_end);
while (*arg != NUL && *arg != '#' && *arg != '\n')
{
// TODO:
// extends SomeClass
// specifies SomeInterface
if (STRNCMP(arg, "implements", 10) == 0 && IS_WHITE_OR_NUL(arg[10]))
{
arg = skipwhite(arg + 10);
char_u *impl_end = find_name_end(arg, NULL, NULL, FNE_CHECK_START);
if (!IS_WHITE_OR_NUL(*impl_end))
{
semsg(_(e_white_space_required_after_name_str), arg);
goto early_ret;
}
char_u *iname = vim_strnsave(arg, impl_end - arg);
if (iname == NULL)
goto early_ret;
if (ga_add_string(&ga_impl, iname) == FAIL)
{
vim_free(iname);
goto early_ret;
}
arg = skipwhite(impl_end);
}
else
{
semsg(_(e_trailing_characters_str), arg);
early_ret:
ga_clear_strings(&ga_impl);
return;
}
}
garray_T type_list; // list of pointers to allocated types
ga_init2(&type_list, sizeof(type_T *), 10);
@@ -438,6 +473,114 @@ ex_class(exarg_T *eap)
}
vim_free(theline);
// Check a few things before defining the class.
if (success && ga_impl.ga_len > 0)
{
// Check all "implements" entries are valid and correct.
for (int i = 0; i < ga_impl.ga_len && success; ++i)
{
char_u *impl = ((char_u **)ga_impl.ga_data)[i];
typval_T tv;
tv.v_type = VAR_UNKNOWN;
if (eval_variable(impl, 0, 0, &tv, NULL,
EVAL_VAR_VERBOSE + EVAL_VAR_IMPORT) == 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;
}
// check the members of the interface match the members of the class
class_T *ifcl = tv.vval.v_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];
if (STRCMP(if_ms[if_i].ocm_name, m->ocm_name) == 0)
{
// TODO: check type
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)
{
// TODO: check return and argument types
break;
}
}
if (cl_i == cl_count)
{
semsg(_(e_function_str_of_interface_str_not_implemented),
if_name, impl);
success = FALSE;
break;
}
}
}
clear_tv(&tv);
}
}
class_T *cl = NULL;
if (success)
{
@@ -450,10 +593,23 @@ ex_class(exarg_T *eap)
cl->class_flags = CLASS_INTERFACE;
cl->class_refcount = 1;
cl->class_name = vim_strnsave(arg, name_end - arg);
cl->class_name = vim_strnsave(name_start, name_end - name_start);
if (cl->class_name == NULL)
goto cleanup;
if (ga_impl.ga_len > 0)
{
// Move the "implements" names into the class.
cl->class_interface_count = ga_impl.ga_len;
cl->class_interfaces = ALLOC_MULT(char_u *, ga_impl.ga_len);
if (cl->class_interfaces == NULL)
goto cleanup;
for (int i = 0; i < ga_impl.ga_len; ++i)
cl->class_interfaces[i] = ((char_u **)ga_impl.ga_data)[i];
CLEAR_POINTER(ga_impl.ga_data);
ga_impl.ga_len = 0;
}
// Add class and object members to "cl".
if (add_members_to_class(&classmembers,
&cl->class_class_members,
@@ -499,7 +655,7 @@ ex_class(exarg_T *eap)
have_new = TRUE;
break;
}
if (!have_new)
if (is_class && !have_new)
{
// No new() method was defined, add the default constructor.
garray_T fga;
@@ -589,6 +745,7 @@ ex_class(exarg_T *eap)
// - Fill hashtab with object members and methods ?
// Add the class to the script-local variables.
// TODO: handle other context, e.g. in a function
typval_T tv;
tv.v_type = VAR_CLASS;
tv.vval.v_class = cl;
@@ -607,6 +764,8 @@ cleanup:
vim_free(cl);
}
ga_clear_strings(&ga_impl);
for (int round = 1; round <= 2; ++round)
{
garray_T *gap = round == 1 ? &classmembers : &objmembers;
@@ -986,6 +1145,10 @@ class_unref(class_T *cl)
// be freed.
VIM_CLEAR(cl->class_name);
for (int i = 0; i < cl->class_interface_count; ++i)
vim_free(((char_u **)cl->class_interfaces)[i]);
vim_free(cl->class_interfaces);
for (int i = 0; i < cl->class_class_member_count; ++i)
{
ocmember_T *m = &cl->class_class_members[i];