0
0
mirror of https://github.com/vim/vim.git synced 2025-09-27 04:14:06 -04:00

patch 9.0.1804: Vim9: no support for private object methods

Problem:  Vim9: no support for private object methods
Solution: Add support for private object/class methods

closes: #12920

Signed-off-by: Christian Brabandt <cb@256bit.org>
Co-authored-by: Yegappan Lakshmanan <yegappan@yahoo.com>
This commit is contained in:
Yegappan Lakshmanan
2023-08-27 19:18:23 +02:00
committed by Christian Brabandt
parent 03e44a1d70
commit cd7293bf6c
9 changed files with 691 additions and 5 deletions

View File

@@ -3484,6 +3484,11 @@ EXTERN char e_warning_pointer_block_corrupted[]
INIT(= N_("E1364: Warning: Pointer block corrupted"));
EXTERN char e_cannot_use_a_return_type_with_new[]
INIT(= N_("E1365: Cannot use a return type with the \"new\" function"));
EXTERN char e_cannot_access_private_method_str[]
INIT(= N_("E1366: Cannot access private method: %s"));
EXTERN char e_interface_str_and_class_str_function_access_not_same[]
INIT(= N_("E1367: Access type of class method %s differs from interface method %s"));
EXTERN char e_cannot_mix_positional_and_non_positional_str[]
INIT(= N_("E1400: Cannot mix positional and non-positional arguments: %s"));
EXTERN char e_fmt_arg_nr_unused_str[]
@@ -3501,4 +3506,4 @@ EXTERN char e_member_str_type_mismatch_expected_str_but_got_str[]
EXTERN char e_method_str_type_mismatch_expected_str_but_got_str[]
INIT(= N_("E1407: Member \"%s\": type mismatch, expected %s but got %s"));
// E1366 - E1399 unused
// E1368 - E1399 unused

View File

@@ -1489,8 +1489,8 @@ typedef struct {
#define TTFLAG_SUPER 0x40 // object from "super".
typedef enum {
VIM_ACCESS_PRIVATE, // read/write only inside th class
VIM_ACCESS_READ, // read everywhere, write only inside th class
VIM_ACCESS_PRIVATE, // read/write only inside the class
VIM_ACCESS_READ, // read everywhere, write only inside the class
VIM_ACCESS_ALL // read/write everywhere
} omacc_T;
@@ -1790,6 +1790,7 @@ struct ufunc_S
class_T *uf_class; // for object method and constructor; does not
// count for class_refcount
int uf_private; // TRUE if class or object private method
garray_T uf_args; // arguments, including optional arguments
garray_T uf_def_args; // default argument expressions

View File

@@ -2801,4 +2801,594 @@ def Test_object_lockvar()
v9.CheckScriptSuccess(lines)
enddef
" Test for a private object method
def Test_private_object_method()
# Try calling a private method using an object (at the script level)
var lines =<< trim END
vim9script
class A
def _Foo(): number
return 1234
enddef
endclass
var a = A.new()
a._Foo()
END
v9.CheckScriptFailure(lines, 'E1366: Cannot access private method: _Foo()')
# Try calling a private method using an object (from a def function)
lines =<< trim END
vim9script
class A
def _Foo(): number
return 1234
enddef
endclass
def T()
var a = A.new()
a._Foo()
enddef
T()
END
v9.CheckScriptFailure(lines, 'E1366: Cannot access private method: _Foo()')
# Use a private method from another object method (in script context)
lines =<< trim END
vim9script
class A
def _Foo(): number
return 1234
enddef
def Bar(): number
return this._Foo()
enddef
endclass
var a = A.new()
assert_equal(1234, a.Bar())
END
v9.CheckScriptSuccess(lines)
# Use a private method from another object method (def function context)
lines =<< trim END
vim9script
class A
def _Foo(): number
return 1234
enddef
def Bar(): number
return this._Foo()
enddef
endclass
def T()
var a = A.new()
assert_equal(1234, a.Bar())
enddef
T()
END
v9.CheckScriptSuccess(lines)
# Try calling a private method without the "this" prefix
lines =<< trim END
vim9script
class A
def _Foo(): number
return 1234
enddef
def Bar(): number
return _Foo()
enddef
endclass
var a = A.new()
a.Bar()
END
v9.CheckScriptFailure(lines, 'E117: Unknown function: _Foo')
# Try calling a private method using the class name
lines =<< trim END
vim9script
class A
def _Foo(): number
return 1234
enddef
endclass
A._Foo()
END
v9.CheckScriptFailure(lines, 'E1325: Method not found on class "A": _Foo()')
# Try to use "public" keyword when defining a private method
lines =<< trim END
vim9script
class A
public def _Foo()
enddef
endclass
var a = A.new()
a._Foo()
END
v9.CheckScriptFailure(lines, 'E1331: Public must be followed by "this" or "static"')
# Define two private methods with the same name
lines =<< trim END
vim9script
class A
def _Foo()
enddef
def _Foo()
enddef
endclass
var a = A.new()
END
v9.CheckScriptFailure(lines, 'E1355: Duplicate function: _Foo')
# Define a private method and a object method with the same name
lines =<< trim END
vim9script
class A
def _Foo()
enddef
def Foo()
enddef
endclass
var a = A.new()
END
v9.CheckScriptFailure(lines, 'E1355: Duplicate function: Foo')
# Define an object method and a private method with the same name
lines =<< trim END
vim9script
class A
def Foo()
enddef
def _Foo()
enddef
endclass
var a = A.new()
END
v9.CheckScriptFailure(lines, 'E1355: Duplicate function: _Foo')
# Call a public method and a private method from a private method
lines =<< trim END
vim9script
class A
def Foo(): number
return 100
enddef
def _Bar(): number
return 200
enddef
def _Baz()
assert_equal(100, this.Foo())
assert_equal(200, this._Bar())
enddef
def T()
this._Baz()
enddef
endclass
var a = A.new()
a.T()
END
v9.CheckScriptSuccess(lines)
# Try calling a private method from another class
lines =<< trim END
vim9script
class A
def _Foo(): number
return 100
enddef
endclass
class B
def Foo(): number
var a = A.new()
a._Foo()
enddef
endclass
var b = B.new()
b.Foo()
END
v9.CheckScriptFailure(lines, 'E1366: Cannot access private method: _Foo()')
# Call a private object method from a child class object method
lines =<< trim END
vim9script
class A
def _Foo(): number
return 1234
enddef
endclass
class B extends A
def Bar()
enddef
endclass
class C extends B
def Baz(): number
return this._Foo()
enddef
endclass
var c = C.new()
assert_equal(1234, c.Baz())
END
v9.CheckScriptSuccess(lines)
# Call a private object method from a child class object
lines =<< trim END
vim9script
class A
def _Foo(): number
return 1234
enddef
endclass
class B extends A
def Bar()
enddef
endclass
class C extends B
def Baz(): number
enddef
endclass
var c = C.new()
assert_equal(1234, c._Foo())
END
v9.CheckScriptFailure(lines, 'E1366: Cannot access private method: _Foo()')
# Using "_" prefix in a method name should fail outside of a class
lines =<< trim END
vim9script
def _Foo(): number
return 1234
enddef
var a = _Foo()
END
v9.CheckScriptFailure(lines, 'E1267: Function name must start with a capital: _Foo(): number')
enddef
" Test for an private class method
def Test_private_class_method()
# Try calling a class private method (at the script level)
var lines =<< trim END
vim9script
class A
static def _Foo(): number
return 1234
enddef
endclass
A._Foo()
END
v9.CheckScriptFailure(lines, 'E1366: Cannot access private method: _Foo()')
# Try calling a class private method (from a def function)
lines =<< trim END
vim9script
class A
static def _Foo(): number
return 1234
enddef
endclass
def T()
A._Foo()
enddef
T()
END
v9.CheckScriptFailure(lines, 'E1366: Cannot access private method: _Foo()')
# Try calling a class private method using an object (at the script level)
lines =<< trim END
vim9script
class A
static def _Foo(): number
return 1234
enddef
endclass
var a = A.new()
a._Foo()
END
v9.CheckScriptFailure(lines, 'E1325: Method not found on class "A": _Foo()')
# Try calling a class private method using an object (from a def function)
lines =<< trim END
vim9script
class A
static def _Foo(): number
return 1234
enddef
endclass
def T()
var a = A.new()
a._Foo()
enddef
T()
END
v9.CheckScriptFailure(lines, 'E1325: Method not found on class "A": _Foo()')
# Use a class private method from an object method
lines =<< trim END
vim9script
class A
static def _Foo(): number
return 1234
enddef
def Bar()
assert_equal(1234, A._Foo())
enddef
endclass
var a = A.new()
a.Bar()
END
v9.CheckScriptSuccess(lines)
# Use a class private method from another class private method
lines =<< trim END
vim9script
class A
static def _Foo1(): number
return 1234
enddef
static def _Foo2()
assert_equal(1234, A._Foo1())
enddef
def Bar()
A._Foo2()
enddef
endclass
var a = A.new()
a.Bar()
END
v9.CheckScriptSuccess(lines)
# Declare a class method and a class private method with the same name
lines =<< trim END
vim9script
class A
static def _Foo()
enddef
static def Foo()
enddef
endclass
var a = A.new()
END
v9.CheckScriptFailure(lines, 'E1355: Duplicate function: Foo')
# Try calling a class private method from another class
lines =<< trim END
vim9script
class A
static def _Foo(): number
return 1234
enddef
endclass
class B
def Foo(): number
return A._Foo()
enddef
endclass
var b = B.new()
assert_equal(1234, b.Foo())
END
v9.CheckScriptFailure(lines, 'E1366: Cannot access private method: _Foo()')
# Call a private class method from a child class object method
lines =<< trim END
vim9script
class A
static def _Foo(): number
return 1234
enddef
endclass
class B extends A
def Bar()
enddef
endclass
class C extends B
def Baz(): number
return A._Foo()
enddef
endclass
var c = C.new()
assert_equal(1234, c.Baz())
END
v9.CheckScriptSuccess(lines)
# Call a private class method from a child class private class method
lines =<< trim END
vim9script
class A
static def _Foo(): number
return 1234
enddef
endclass
class B extends A
def Bar()
enddef
endclass
class C extends B
static def Baz(): number
return A._Foo()
enddef
endclass
assert_equal(1234, C.Baz())
END
v9.CheckScriptSuccess(lines)
# Call a private class method from a child class object
lines =<< trim END
vim9script
class A
static def _Foo(): number
return 1234
enddef
endclass
class B extends A
def Bar()
enddef
endclass
class C extends B
def Baz(): number
enddef
endclass
var c = C.new()
assert_equal(1234, C._Foo())
END
v9.CheckScriptFailure(lines, 'E1366: Cannot access private method: _Foo()')
enddef
" Test for an interface private object_method
def Test_interface_private_object_method()
# Implement an interface private method and use it from a public method
var lines =<< trim END
vim9script
interface Intf
def _Foo(): number
endinterface
class A implements Intf
def _Foo(): number
return 1234
enddef
def Bar(): number
return this._Foo()
enddef
endclass
var a = A.new()
assert_equal(1234, a.Bar())
END
v9.CheckScriptSuccess(lines)
# Call an interface private class method (script context)
lines =<< trim END
vim9script
interface Intf
def _Foo(): number
endinterface
class A implements Intf
def _Foo(): number
return 1234
enddef
endclass
var a = A.new()
assert_equal(1234, a._Foo())
END
v9.CheckScriptFailure(lines, 'E1366: Cannot access private method: _Foo()')
# Call an interface private class method (def context)
lines =<< trim END
vim9script
interface Intf
def _Foo(): number
endinterface
class A implements Intf
def _Foo(): number
return 1234
enddef
endclass
def T()
var a = A.new()
assert_equal(1234, a._Foo())
enddef
T()
END
v9.CheckScriptFailure(lines, 'E1366: Cannot access private method: _Foo()')
# Implement an interface private object method as a private class method
lines =<< trim END
vim9script
interface Intf
def _Foo(): number
endinterface
class A implements Intf
static def _Foo(): number
return 1234
enddef
endclass
END
v9.CheckScriptFailure(lines, 'E1349: Function "_Foo" of interface "Intf" not implemented')
enddef
" Test for an interface private class method
def Test_interface_private_class_method()
# Implement an interface private class method and use it from a public method
var lines =<< trim END
vim9script
interface Intf
static def _Foo(): number
endinterface
class A implements Intf
static def _Foo(): number
return 1234
enddef
def Bar(): number
return A._Foo()
enddef
endclass
var a = A.new()
assert_equal(1234, a.Bar())
END
v9.CheckScriptSuccess(lines)
# Call an interface private class method (script context)
lines =<< trim END
vim9script
interface Intf
static def _Foo(): number
endinterface
class A implements Intf
static def _Foo(): number
return 1234
enddef
endclass
assert_equal(1234, A._Foo())
END
v9.CheckScriptFailure(lines, 'E1366: Cannot access private method: _Foo())')
# Call an interface private class method (def context)
lines =<< trim END
vim9script
interface Intf
static def _Foo(): number
endinterface
class A implements Intf
static def _Foo(): number
return 1234
enddef
endclass
def T()
assert_equal(1234, A._Foo())
enddef
T()
END
v9.CheckScriptFailure(lines, 'E1366: Cannot access private method: _Foo())')
# Implement an interface private class method as a private object method
lines =<< trim END
vim9script
interface Intf
static def _Foo(): number
endinterface
class A implements Intf
def _Foo(): number
return 1234
enddef
endclass
END
v9.CheckScriptFailure(lines, 'E1349: Function "_Foo" of interface "Intf" not implemented')
enddef
" vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker

View File

@@ -4369,10 +4369,14 @@ trans_function_name_ext(
lead += (int)STRLEN(sid_buf);
}
}
// The function name must start with an upper case letter (unless it is a
// Vim9 class new() function or a Vim9 class private method)
else if (!(flags & TFN_INT)
&& (builtin_function(lv.ll_name, len)
|| (vim9script && *lv.ll_name == '_'))
&& !((flags & TFN_IN_CLASS) && STRNCMP(lv.ll_name, "new", 3) == 0))
&& !((flags & TFN_IN_CLASS)
&& (STRNCMP(lv.ll_name, "new", 3) == 0
|| (*lv.ll_name == '_'))))
{
semsg(_(vim9script ? e_function_name_must_start_with_capital_str
: e_function_name_must_start_with_capital_or_s_str),

View File

@@ -699,6 +699,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
1804,
/**/
1803,
/**/

View File

@@ -357,6 +357,13 @@ validate_interface_methods(
if (check_type_maybe(if_fp[if_i]->uf_func_type,
cl_fp[cl_i]->uf_func_type, TRUE, where) != OK)
success = FALSE;
// Ensure the public/private access level is matching.
if (if_fp[if_i]->uf_private != cl_fp[cl_i]->uf_private)
{
semsg(_(e_interface_str_and_class_str_function_access_not_same),
cl_name, if_name);
success = FALSE;
}
break;
}
}
@@ -1150,7 +1157,9 @@ early_ret:
for (int i = 0; i < fgap->ga_len; ++i)
{
char_u *n = ((ufunc_T **)fgap->ga_data)[i]->uf_name;
if (STRCMP(name, n) == 0)
char_u *pstr = *name == '_' ? name + 1 : name;
char_u *qstr = *n == '_' ? n + 1 : n;
if (STRCMP(pstr, qstr) == 0)
{
semsg(_(e_duplicate_function_str), name);
break;
@@ -1162,6 +1171,11 @@ early_ret:
if (is_new)
uf->uf_flags |= FC_NEW;
// If the method name starts with '_', then it a private
// method.
if (*name == '_')
uf->uf_private = TRUE;
((ufunc_T **)fgap->ga_data)[fgap->ga_len] = uf;
++fgap->ga_len;
}
@@ -1523,6 +1537,13 @@ class_object_index(
typval_T argvars[MAX_FUNC_ARGS + 1];
int argcount = 0;
if (fp->uf_private)
{
// Cannot access a private method outside of a class
semsg(_(e_cannot_access_private_method_str), name);
return FAIL;
}
char_u *argp = name_end;
int ret = get_func_arguments(&argp, evalarg, 0,
argvars, &argcount);

View File

@@ -251,6 +251,30 @@ compile_member(int is_slice, int *keeping_dict, cctx_T *cctx)
return OK;
}
/*
* Returns TRUE if the current function is inside the class "cl" or one of the
* parent classes.
*/
static int
inside_class_hierarchy(cctx_T *cctx_arg, class_T *cl)
{
for (cctx_T *cctx = cctx_arg; cctx != NULL; cctx = cctx->ctx_outer)
{
if (cctx->ctx_ufunc != NULL && cctx->ctx_ufunc->uf_class != NULL)
{
class_T *clp = cctx->ctx_ufunc->uf_class;
while (clp != NULL)
{
if (clp == cl)
return TRUE;
clp = clp->class_extends;
}
}
}
return FALSE;
}
/*
* Compile ".member" coming after an object or class.
*/
@@ -348,6 +372,12 @@ compile_class_object_index(cctx_T *cctx, char_u **arg, type_T *type)
return FAIL;
}
if (ufunc->uf_private && !inside_class_hierarchy(cctx, cl))
{
semsg(_(e_cannot_access_private_method_str), name);
return FAIL;
}
// Compile the arguments and call the class function or object method.
// The object method will know that the object is on the stack, just
// before the arguments.