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.
|
||||
* Otherwise the variable is created.
|
||||
*/
|
||||
void
|
||||
int
|
||||
set_var_const(
|
||||
char_u *name,
|
||||
scid_T sid,
|
||||
@@ -3854,6 +3854,7 @@ set_var_const(
|
||||
int var_in_autoload = FALSE;
|
||||
int flags = flags_arg;
|
||||
int free_tv_arg = !copy; // free tv_arg if not used
|
||||
int rc = FAIL;
|
||||
|
||||
if (sid != 0)
|
||||
{
|
||||
@@ -4127,10 +4128,14 @@ set_var_const(
|
||||
// values.
|
||||
item_lock(dest_tv, DICT_MAXNEST, TRUE, TRUE);
|
||||
|
||||
rc = OK;
|
||||
|
||||
failed:
|
||||
vim_free(name_tofree);
|
||||
if (free_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);
|
||||
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_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_ro(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 .= ' --gui-dialog-file guidialogfile'
|
||||
" 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 cmd =~ 'valgrind.*--log-file='
|
||||
|
@@ -67,6 +67,22 @@ def Test_class_basic()
|
||||
END
|
||||
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)
|
||||
lines =<< trim END
|
||||
vim9script
|
||||
@@ -10272,4 +10288,65 @@ func Test_object_string()
|
||||
call v9.CheckSourceSuccess(lines)
|
||||
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
|
||||
|
@@ -704,6 +704,8 @@ static char *(features[]) =
|
||||
|
||||
static int included_patches[] =
|
||||
{ /* Add new patch number below this line */
|
||||
/**/
|
||||
160,
|
||||
/**/
|
||||
159,
|
||||
/**/
|
||||
|
@@ -208,7 +208,7 @@ add_member(
|
||||
* "parent_count" is the number of members in the parent class
|
||||
* "members" will be set to the newly allocated array of members and
|
||||
* "member_count" set to the number of members.
|
||||
* Returns OK or FAIL.
|
||||
* Returns OK on success and FAIL on memory allocation failure.
|
||||
*/
|
||||
static int
|
||||
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
|
||||
validate_extends_class(
|
||||
class_T *cl,
|
||||
char_u *extends_name,
|
||||
class_T **extends_clp,
|
||||
int is_class)
|
||||
@@ -308,6 +309,12 @@ validate_extends_class(
|
||||
typval_T tv;
|
||||
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;
|
||||
if (eval_variable_import(extends_name, &tv) == FAIL)
|
||||
{
|
||||
@@ -1642,6 +1649,36 @@ early_ret:
|
||||
garray_T objmethods;
|
||||
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
|
||||
* "endinterface" is found.
|
||||
@@ -1981,15 +2018,13 @@ early_ret:
|
||||
}
|
||||
vim_free(theline);
|
||||
|
||||
class_T *extends_cl = NULL; // class from "extends" argument
|
||||
|
||||
/*
|
||||
* Check a few things before defining the class.
|
||||
*/
|
||||
|
||||
// Check the "extends" class is valid.
|
||||
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);
|
||||
|
||||
// Check the new object methods to make sure their access (public or
|
||||
@@ -2016,8 +2051,6 @@ early_ret:
|
||||
success = validate_abstract_class_methods(&classfunctions,
|
||||
&objmethods, extends_cl);
|
||||
|
||||
class_T **intf_classes = NULL;
|
||||
|
||||
// Check all "implements" entries are valid.
|
||||
if (success && ga_impl.ga_len > 0)
|
||||
{
|
||||
@@ -2032,24 +2065,10 @@ early_ret:
|
||||
success = check_func_arg_names(&classfunctions, &objmethods,
|
||||
&classmembers);
|
||||
|
||||
class_T *cl = NULL;
|
||||
if (success)
|
||||
{
|
||||
// "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)
|
||||
{
|
||||
cl->class_extends = extends_cl;
|
||||
@@ -2136,41 +2155,10 @@ early_ret:
|
||||
// TODO:
|
||||
// - 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;
|
||||
}
|
||||
|
||||
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);
|
||||
class_unref(extends_cl);
|
||||
|
||||
|
Reference in New Issue
Block a user