mirror of
https://github.com/vim/vim.git
synced 2025-09-24 03:44:06 -04:00
patch 9.1.0160: Vim9: Add support for using a class type of itself in an object method
Problem: Add support for using a class type of itself in an object method (thinca) Solution: Vim9: Add support for using a class type of itself in an object method (Yegappan Lakshmanan) fixes: #12369 closes: #14165 Signed-off-by: Yegappan Lakshmanan <yegappan@yahoo.com> Signed-off-by: Christian Brabandt <cb@256bit.org>
This commit is contained in:
committed by
Christian Brabandt
parent
b2ec0da080
commit
35b867b685
@@ -3830,7 +3830,7 @@ set_var(
|
|||||||
* If the variable already exists and "is_const" is FALSE the value is updated.
|
* If the variable already exists and "is_const" is FALSE the value is updated.
|
||||||
* Otherwise the variable is created.
|
* Otherwise the variable is created.
|
||||||
*/
|
*/
|
||||||
void
|
int
|
||||||
set_var_const(
|
set_var_const(
|
||||||
char_u *name,
|
char_u *name,
|
||||||
scid_T sid,
|
scid_T sid,
|
||||||
@@ -3854,6 +3854,7 @@ set_var_const(
|
|||||||
int var_in_autoload = FALSE;
|
int var_in_autoload = FALSE;
|
||||||
int flags = flags_arg;
|
int flags = flags_arg;
|
||||||
int free_tv_arg = !copy; // free tv_arg if not used
|
int free_tv_arg = !copy; // free tv_arg if not used
|
||||||
|
int rc = FAIL;
|
||||||
|
|
||||||
if (sid != 0)
|
if (sid != 0)
|
||||||
{
|
{
|
||||||
@@ -4127,10 +4128,14 @@ set_var_const(
|
|||||||
// values.
|
// values.
|
||||||
item_lock(dest_tv, DICT_MAXNEST, TRUE, TRUE);
|
item_lock(dest_tv, DICT_MAXNEST, TRUE, TRUE);
|
||||||
|
|
||||||
|
rc = OK;
|
||||||
|
|
||||||
failed:
|
failed:
|
||||||
vim_free(name_tofree);
|
vim_free(name_tofree);
|
||||||
if (free_tv_arg)
|
if (free_tv_arg)
|
||||||
clear_tv(tv_arg);
|
clear_tv(tv_arg);
|
||||||
|
|
||||||
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@@ -77,7 +77,7 @@ void vars_clear_ext(hashtab_T *ht, int free_val);
|
|||||||
void delete_var(hashtab_T *ht, hashitem_T *hi);
|
void delete_var(hashtab_T *ht, hashitem_T *hi);
|
||||||
int before_set_vvar(char_u *varname, dictitem_T *di, typval_T *tv, int copy, int *type_error);
|
int before_set_vvar(char_u *varname, dictitem_T *di, typval_T *tv, int copy, int *type_error);
|
||||||
void set_var(char_u *name, typval_T *tv, int copy);
|
void set_var(char_u *name, typval_T *tv, int copy);
|
||||||
void set_var_const(char_u *name, scid_T sid, type_T *type_arg, typval_T *tv_arg, int copy, int flags_arg, int var_idx);
|
int set_var_const(char_u *name, scid_T sid, type_T *type_arg, typval_T *tv_arg, int copy, int flags_arg, int var_idx);
|
||||||
int var_check_permission(dictitem_T *di, char_u *name);
|
int var_check_permission(dictitem_T *di, char_u *name);
|
||||||
int var_check_ro(int flags, char_u *name, int use_gettext);
|
int var_check_ro(int flags, char_u *name, int use_gettext);
|
||||||
int var_check_lock(int flags, char_u *name, int use_gettext);
|
int var_check_lock(int flags, char_u *name, int use_gettext);
|
||||||
|
@@ -301,7 +301,7 @@ func GetVimCommand(...)
|
|||||||
let cmd .= ' --not-a-term'
|
let cmd .= ' --not-a-term'
|
||||||
let cmd .= ' --gui-dialog-file guidialogfile'
|
let cmd .= ' --gui-dialog-file guidialogfile'
|
||||||
" remove any environment variables
|
" remove any environment variables
|
||||||
let cmd = substitute(cmd, '[A-Z_]*=\S\+ *', '', 'g')
|
let cmd = substitute(cmd, '[A-Z_]\+=\S\+ *', '', 'g')
|
||||||
|
|
||||||
" If using valgrind, make sure every run uses a different log file.
|
" If using valgrind, make sure every run uses a different log file.
|
||||||
if cmd =~ 'valgrind.*--log-file='
|
if cmd =~ 'valgrind.*--log-file='
|
||||||
|
@@ -67,6 +67,22 @@ def Test_class_basic()
|
|||||||
END
|
END
|
||||||
v9.CheckSourceFailure(lines, "E488: Trailing characters: | echo 'done'", 3)
|
v9.CheckSourceFailure(lines, "E488: Trailing characters: | echo 'done'", 3)
|
||||||
|
|
||||||
|
# Try to define a class with the same name as an existing variable
|
||||||
|
lines =<< trim END
|
||||||
|
vim9script
|
||||||
|
var Something: list<number> = [1]
|
||||||
|
class Thing
|
||||||
|
endclass
|
||||||
|
interface Api
|
||||||
|
endinterface
|
||||||
|
class Something extends Thing implements Api
|
||||||
|
var v1: string = ''
|
||||||
|
def Foo()
|
||||||
|
enddef
|
||||||
|
endclass
|
||||||
|
END
|
||||||
|
v9.CheckSourceFailure(lines, 'E1041: Redefining script item: "Something"', 7)
|
||||||
|
|
||||||
# Use old "this." prefixed member variable declaration syntax (without initialization)
|
# Use old "this." prefixed member variable declaration syntax (without initialization)
|
||||||
lines =<< trim END
|
lines =<< trim END
|
||||||
vim9script
|
vim9script
|
||||||
@@ -10272,4 +10288,65 @@ func Test_object_string()
|
|||||||
call v9.CheckSourceSuccess(lines)
|
call v9.CheckSourceSuccess(lines)
|
||||||
endfunc
|
endfunc
|
||||||
|
|
||||||
|
" Test for using a class in the class definition
|
||||||
|
def Test_Ref_Class_Within_Same_Class()
|
||||||
|
var lines =<< trim END
|
||||||
|
vim9script
|
||||||
|
class A
|
||||||
|
var n: number = 0
|
||||||
|
def Equals(other: A): bool
|
||||||
|
return this.n == other.n
|
||||||
|
enddef
|
||||||
|
endclass
|
||||||
|
|
||||||
|
var a1 = A.new(10)
|
||||||
|
var a2 = A.new(10)
|
||||||
|
var a3 = A.new(20)
|
||||||
|
assert_equal(true, a1.Equals(a2))
|
||||||
|
assert_equal(false, a2.Equals(a3))
|
||||||
|
END
|
||||||
|
v9.CheckScriptSuccess(lines)
|
||||||
|
|
||||||
|
lines =<< trim END
|
||||||
|
vim9script
|
||||||
|
|
||||||
|
class Foo
|
||||||
|
var num: number
|
||||||
|
def Clone(): Foo
|
||||||
|
return Foo.new(this.num)
|
||||||
|
enddef
|
||||||
|
endclass
|
||||||
|
|
||||||
|
var f1 = Foo.new(1)
|
||||||
|
|
||||||
|
def F()
|
||||||
|
var f2: Foo = f1.Clone()
|
||||||
|
assert_equal(false, f2 is f1)
|
||||||
|
assert_equal(true, f2.num == f1.num)
|
||||||
|
enddef
|
||||||
|
F()
|
||||||
|
|
||||||
|
var f3: Foo = f1.Clone()
|
||||||
|
assert_equal(false, f3 is f1)
|
||||||
|
assert_equal(true, f3.num == f1.num)
|
||||||
|
END
|
||||||
|
v9.CheckScriptSuccess(lines)
|
||||||
|
|
||||||
|
# Test for trying to use a class to extend when defining the same class
|
||||||
|
lines =<< trim END
|
||||||
|
vim9script
|
||||||
|
class A extends A
|
||||||
|
endclass
|
||||||
|
END
|
||||||
|
v9.CheckScriptFailure(lines, 'E1354: Cannot extend A', 3)
|
||||||
|
|
||||||
|
# Test for trying to use a class to implement when defining the same class
|
||||||
|
lines =<< trim END
|
||||||
|
vim9script
|
||||||
|
class A implements A
|
||||||
|
endclass
|
||||||
|
END
|
||||||
|
v9.CheckScriptFailure(lines, 'E1347: Not a valid interface: A', 3)
|
||||||
|
enddef
|
||||||
|
|
||||||
" vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker
|
" vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker
|
||||||
|
@@ -704,6 +704,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 */
|
||||||
|
/**/
|
||||||
|
160,
|
||||||
/**/
|
/**/
|
||||||
159,
|
159,
|
||||||
/**/
|
/**/
|
||||||
|
@@ -208,7 +208,7 @@ add_member(
|
|||||||
* "parent_count" is the number of members in the parent class
|
* "parent_count" is the number of members in the parent class
|
||||||
* "members" will be set to the newly allocated array of members and
|
* "members" will be set to the newly allocated array of members and
|
||||||
* "member_count" set to the number of members.
|
* "member_count" set to the number of members.
|
||||||
* Returns OK or FAIL.
|
* Returns OK on success and FAIL on memory allocation failure.
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
add_members_to_class(
|
add_members_to_class(
|
||||||
@@ -301,6 +301,7 @@ object_index_from_itf_index(class_T *itf, int is_method, int idx, class_T *cl)
|
|||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
validate_extends_class(
|
validate_extends_class(
|
||||||
|
class_T *cl,
|
||||||
char_u *extends_name,
|
char_u *extends_name,
|
||||||
class_T **extends_clp,
|
class_T **extends_clp,
|
||||||
int is_class)
|
int is_class)
|
||||||
@@ -308,6 +309,12 @@ validate_extends_class(
|
|||||||
typval_T tv;
|
typval_T tv;
|
||||||
int success = FALSE;
|
int success = FALSE;
|
||||||
|
|
||||||
|
if (STRCMP(cl->class_name, extends_name) == 0)
|
||||||
|
{
|
||||||
|
semsg(_(e_cannot_extend_str), extends_name);
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
tv.v_type = VAR_UNKNOWN;
|
tv.v_type = VAR_UNKNOWN;
|
||||||
if (eval_variable_import(extends_name, &tv) == FAIL)
|
if (eval_variable_import(extends_name, &tv) == FAIL)
|
||||||
{
|
{
|
||||||
@@ -1642,6 +1649,36 @@ early_ret:
|
|||||||
garray_T objmethods;
|
garray_T objmethods;
|
||||||
ga_init2(&objmethods, sizeof(ufunc_T *), 10);
|
ga_init2(&objmethods, sizeof(ufunc_T *), 10);
|
||||||
|
|
||||||
|
class_T *cl = NULL;
|
||||||
|
class_T *extends_cl = NULL; // class from "extends" argument
|
||||||
|
class_T **intf_classes = NULL;
|
||||||
|
|
||||||
|
cl = ALLOC_CLEAR_ONE(class_T);
|
||||||
|
if (cl == NULL)
|
||||||
|
goto cleanup;
|
||||||
|
if (!is_class)
|
||||||
|
cl->class_flags = CLASS_INTERFACE;
|
||||||
|
else if (is_abstract)
|
||||||
|
cl->class_flags = CLASS_ABSTRACT;
|
||||||
|
|
||||||
|
cl->class_refcount = 1;
|
||||||
|
cl->class_name = vim_strnsave(name_start, name_end - name_start);
|
||||||
|
if (cl->class_name == NULL)
|
||||||
|
goto cleanup;
|
||||||
|
|
||||||
|
// Add the class to the script-local variables.
|
||||||
|
// TODO: handle other context, e.g. in a function
|
||||||
|
// TODO: does uf_hash need to be cleared?
|
||||||
|
typval_T tv;
|
||||||
|
tv.v_type = VAR_CLASS;
|
||||||
|
tv.vval.v_class = cl;
|
||||||
|
is_export = class_export;
|
||||||
|
SOURCING_LNUM = start_lnum;
|
||||||
|
int rc = set_var_const(cl->class_name, current_sctx.sc_sid,
|
||||||
|
NULL, &tv, FALSE, 0, 0);
|
||||||
|
if (rc == FAIL)
|
||||||
|
goto cleanup;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Go over the body of the class/interface until "endclass" or
|
* Go over the body of the class/interface until "endclass" or
|
||||||
* "endinterface" is found.
|
* "endinterface" is found.
|
||||||
@@ -1981,15 +2018,13 @@ early_ret:
|
|||||||
}
|
}
|
||||||
vim_free(theline);
|
vim_free(theline);
|
||||||
|
|
||||||
class_T *extends_cl = NULL; // class from "extends" argument
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Check a few things before defining the class.
|
* Check a few things before defining the class.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// 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, is_class);
|
success = validate_extends_class(cl, extends, &extends_cl, is_class);
|
||||||
VIM_CLEAR(extends);
|
VIM_CLEAR(extends);
|
||||||
|
|
||||||
// Check the new object methods to make sure their access (public or
|
// Check the new object methods to make sure their access (public or
|
||||||
@@ -2016,8 +2051,6 @@ early_ret:
|
|||||||
success = validate_abstract_class_methods(&classfunctions,
|
success = validate_abstract_class_methods(&classfunctions,
|
||||||
&objmethods, extends_cl);
|
&objmethods, extends_cl);
|
||||||
|
|
||||||
class_T **intf_classes = NULL;
|
|
||||||
|
|
||||||
// Check all "implements" entries are valid.
|
// Check all "implements" entries are valid.
|
||||||
if (success && ga_impl.ga_len > 0)
|
if (success && ga_impl.ga_len > 0)
|
||||||
{
|
{
|
||||||
@@ -2032,24 +2065,10 @@ early_ret:
|
|||||||
success = check_func_arg_names(&classfunctions, &objmethods,
|
success = check_func_arg_names(&classfunctions, &objmethods,
|
||||||
&classmembers);
|
&classmembers);
|
||||||
|
|
||||||
class_T *cl = NULL;
|
|
||||||
if (success)
|
if (success)
|
||||||
{
|
{
|
||||||
// "endclass" encountered without failures: Create the class.
|
// "endclass" encountered without failures: Create the class.
|
||||||
|
|
||||||
cl = ALLOC_CLEAR_ONE(class_T);
|
|
||||||
if (cl == NULL)
|
|
||||||
goto cleanup;
|
|
||||||
if (!is_class)
|
|
||||||
cl->class_flags = CLASS_INTERFACE;
|
|
||||||
else if (is_abstract)
|
|
||||||
cl->class_flags = CLASS_ABSTRACT;
|
|
||||||
|
|
||||||
cl->class_refcount = 1;
|
|
||||||
cl->class_name = vim_strnsave(name_start, name_end - name_start);
|
|
||||||
if (cl->class_name == NULL)
|
|
||||||
goto cleanup;
|
|
||||||
|
|
||||||
if (extends_cl != NULL)
|
if (extends_cl != NULL)
|
||||||
{
|
{
|
||||||
cl->class_extends = extends_cl;
|
cl->class_extends = extends_cl;
|
||||||
@@ -2136,41 +2155,10 @@ early_ret:
|
|||||||
// TODO:
|
// TODO:
|
||||||
// - Fill hashtab with object members and methods ?
|
// - Fill hashtab with object members and methods ?
|
||||||
|
|
||||||
// Add the class to the script-local variables.
|
|
||||||
// TODO: handle other context, e.g. in a function
|
|
||||||
// TODO: does uf_hash need to be cleared?
|
|
||||||
typval_T tv;
|
|
||||||
tv.v_type = VAR_CLASS;
|
|
||||||
tv.vval.v_class = cl;
|
|
||||||
is_export = class_export;
|
|
||||||
SOURCING_LNUM = start_lnum;
|
|
||||||
set_var_const(cl->class_name, current_sctx.sc_sid,
|
|
||||||
NULL, &tv, FALSE, 0, 0);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
cleanup:
|
cleanup:
|
||||||
if (cl != NULL)
|
|
||||||
{
|
|
||||||
vim_free(cl->class_name);
|
|
||||||
vim_free(cl->class_class_functions);
|
|
||||||
if (cl->class_interfaces != NULL)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < cl->class_interface_count; ++i)
|
|
||||||
vim_free(cl->class_interfaces[i]);
|
|
||||||
vim_free(cl->class_interfaces);
|
|
||||||
}
|
|
||||||
if (cl->class_interfaces_cl != NULL)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < cl->class_interface_count; ++i)
|
|
||||||
class_unref(cl->class_interfaces_cl[i]);
|
|
||||||
vim_free(cl->class_interfaces_cl);
|
|
||||||
}
|
|
||||||
vim_free(cl->class_obj_members);
|
|
||||||
vim_free(cl->class_obj_methods);
|
|
||||||
vim_free(cl);
|
|
||||||
}
|
|
||||||
|
|
||||||
vim_free(extends);
|
vim_free(extends);
|
||||||
class_unref(extends_cl);
|
class_unref(extends_cl);
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user