mirror of
https://github.com/vim/vim.git
synced 2025-08-26 20:03:41 -04:00
patch 9.1.0850: Vim9: cannot access nested object inside objects
Problem: Vim9: cannot access nested object inside objects (lifepillar, 91khr, mawkish) Solution: Add support for accessing an object member using a "any" variable type (Yegappan Lakshmanan) fixes: #11822 fixes: #12024 fixes: #12196 fixes: #12198 closes: #16029 Signed-off-by: Yegappan Lakshmanan <yegappan@yahoo.com> Signed-off-by: Christian Brabandt <cb@256bit.org>
This commit is contained in:
parent
622f6f5b9a
commit
56d45f1b66
@ -9282,6 +9282,7 @@ o_CTRL-V motion.txt /*o_CTRL-V*
|
|||||||
o_V motion.txt /*o_V*
|
o_V motion.txt /*o_V*
|
||||||
o_object-select motion.txt /*o_object-select*
|
o_object-select motion.txt /*o_object-select*
|
||||||
o_v motion.txt /*o_v*
|
o_v motion.txt /*o_v*
|
||||||
|
obj-var-type-any vim9class.txt /*obj-var-type-any*
|
||||||
object vim9class.txt /*object*
|
object vim9class.txt /*object*
|
||||||
object-const-variable vim9class.txt /*object-const-variable*
|
object-const-variable vim9class.txt /*object-const-variable*
|
||||||
object-empty() vim9class.txt /*object-empty()*
|
object-empty() vim9class.txt /*object-empty()*
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
*vim9class.txt* For Vim version 9.1. Last change: 2024 Apr 13
|
*vim9class.txt* For Vim version 9.1. Last change: 2024 Nov 11
|
||||||
|
|
||||||
|
|
||||||
VIM REFERENCE MANUAL by Bram Moolenaar
|
VIM REFERENCE MANUAL by Bram Moolenaar
|
||||||
@ -873,6 +873,33 @@ Note that the method name must start with "new". If there is no method called
|
|||||||
"new()" then the default constructor is added, even though there are other
|
"new()" then the default constructor is added, even though there are other
|
||||||
constructor methods.
|
constructor methods.
|
||||||
|
|
||||||
|
Using variable type "any" for an Object~
|
||||||
|
*obj-var-type-any*
|
||||||
|
You can use a variable declared with type "any" to hold an object. e.g.
|
||||||
|
>
|
||||||
|
vim9script
|
||||||
|
class A
|
||||||
|
var n = 10
|
||||||
|
def Get(): number
|
||||||
|
return this.n
|
||||||
|
enddef
|
||||||
|
endclass
|
||||||
|
|
||||||
|
def Fn(o: any)
|
||||||
|
echo o.n
|
||||||
|
echo o.Get()
|
||||||
|
enddef
|
||||||
|
|
||||||
|
var a = A.new()
|
||||||
|
Fn(a)
|
||||||
|
<
|
||||||
|
In this example, Vim cannot determine the type of the parameter "o" for
|
||||||
|
function Fn() at compile time. It can be either a |Dict| or an |Object|
|
||||||
|
value. Therefore, at runtime, when the type is known, the object member
|
||||||
|
variable and method are looked up. This process is not efficient, so it is
|
||||||
|
recommended to use a more specific type whenever possible for better
|
||||||
|
efficiency.
|
||||||
|
|
||||||
Compiling methods in a Class ~
|
Compiling methods in a Class ~
|
||||||
*class-compile*
|
*class-compile*
|
||||||
The |:defcompile| command can be used to compile all the class and object
|
The |:defcompile| command can be used to compile all the class and object
|
||||||
|
@ -9,6 +9,8 @@ type_T *oc_member_type_by_idx(class_T *cl, int is_object, int member_idx);
|
|||||||
void ex_enum(exarg_T *eap);
|
void ex_enum(exarg_T *eap);
|
||||||
void typealias_unref(typealias_T *ta);
|
void typealias_unref(typealias_T *ta);
|
||||||
void ex_type(exarg_T *eap);
|
void ex_type(exarg_T *eap);
|
||||||
|
int get_member_tv(class_T *cl, int is_object, char_u *name, size_t namelen, class_T *current_class, typval_T *rettv);
|
||||||
|
int obj_method_to_partial_tv(object_T *obj, ufunc_T *obj_method, typval_T *rettv);
|
||||||
int class_object_index(char_u **arg, typval_T *rettv, evalarg_T *evalarg, int verbose);
|
int class_object_index(char_u **arg, typval_T *rettv, evalarg_T *evalarg, int verbose);
|
||||||
ufunc_T *find_class_func(char_u **arg);
|
ufunc_T *find_class_func(char_u **arg);
|
||||||
int class_member_idx(class_T *cl, char_u *name, size_t namelen);
|
int class_member_idx(class_T *cl, char_u *name, size_t namelen);
|
||||||
|
@ -784,7 +784,7 @@ def Test_member_any_used_as_object()
|
|||||||
vim9script
|
vim9script
|
||||||
|
|
||||||
class Inner
|
class Inner
|
||||||
var value: number = 0
|
public var value: number = 0
|
||||||
endclass
|
endclass
|
||||||
|
|
||||||
class Outer
|
class Outer
|
||||||
@ -11213,4 +11213,339 @@ def Test_class_cast()
|
|||||||
v9.CheckScriptSuccess(lines)
|
v9.CheckScriptSuccess(lines)
|
||||||
enddef
|
enddef
|
||||||
|
|
||||||
|
" Test for using a variable of type "any" with an object
|
||||||
|
def Test_any_obj_var_type()
|
||||||
|
var lines =<< trim END
|
||||||
|
vim9script
|
||||||
|
class A
|
||||||
|
var name: string = "foobar"
|
||||||
|
def Foo(): string
|
||||||
|
return "func foo"
|
||||||
|
enddef
|
||||||
|
endclass
|
||||||
|
|
||||||
|
def CheckVals(x: any)
|
||||||
|
assert_equal("foobar", x.name)
|
||||||
|
assert_equal("func foo", x.Foo())
|
||||||
|
enddef
|
||||||
|
|
||||||
|
var a = A.new()
|
||||||
|
CheckVals(a)
|
||||||
|
END
|
||||||
|
v9.CheckScriptSuccess(lines)
|
||||||
|
|
||||||
|
# Try to set a non-existing variable
|
||||||
|
lines =<< trim END
|
||||||
|
vim9script
|
||||||
|
class A
|
||||||
|
var name: string = "foobar"
|
||||||
|
endclass
|
||||||
|
|
||||||
|
def SetNonExistingVar(x: any)
|
||||||
|
x.bar = [1, 2, 3]
|
||||||
|
enddef
|
||||||
|
|
||||||
|
var a = A.new()
|
||||||
|
SetNonExistingVar(a)
|
||||||
|
END
|
||||||
|
v9.CheckScriptFailure(lines, 'E1326: Variable "bar" not found in object "A"', 1)
|
||||||
|
|
||||||
|
# Try to read a non-existing variable
|
||||||
|
lines =<< trim END
|
||||||
|
vim9script
|
||||||
|
class A
|
||||||
|
var name: string = "foobar"
|
||||||
|
endclass
|
||||||
|
|
||||||
|
def GetNonExistingVar(x: any)
|
||||||
|
var i: dict<any> = x.bar
|
||||||
|
enddef
|
||||||
|
|
||||||
|
var a = A.new()
|
||||||
|
GetNonExistingVar(a)
|
||||||
|
END
|
||||||
|
v9.CheckScriptFailure(lines, 'E1326: Variable "bar" not found in object "A"', 1)
|
||||||
|
|
||||||
|
# Try to invoke a non-existing method
|
||||||
|
lines =<< trim END
|
||||||
|
vim9script
|
||||||
|
class A
|
||||||
|
def Foo(): number
|
||||||
|
return 10
|
||||||
|
enddef
|
||||||
|
endclass
|
||||||
|
|
||||||
|
def CallNonExistingMethod(x: any)
|
||||||
|
var i: number = x.Bar()
|
||||||
|
enddef
|
||||||
|
|
||||||
|
var a = A.new()
|
||||||
|
CallNonExistingMethod(a)
|
||||||
|
END
|
||||||
|
v9.CheckScriptFailure(lines, 'E1326: Variable "Bar" not found in object "A"', 1)
|
||||||
|
|
||||||
|
# Use an object which is a Dict value
|
||||||
|
lines =<< trim END
|
||||||
|
vim9script
|
||||||
|
class Foo
|
||||||
|
def Bar(): number
|
||||||
|
return 369
|
||||||
|
enddef
|
||||||
|
endclass
|
||||||
|
|
||||||
|
def GetValue(FooDict: dict<any>): number
|
||||||
|
var n: number = 0
|
||||||
|
for foo in values(FooDict)
|
||||||
|
n += foo.Bar()
|
||||||
|
endfor
|
||||||
|
return n
|
||||||
|
enddef
|
||||||
|
|
||||||
|
var d = {'x': Foo.new()}
|
||||||
|
assert_equal(369, GetValue(d))
|
||||||
|
END
|
||||||
|
v9.CheckScriptSuccess(lines)
|
||||||
|
|
||||||
|
# Nested data. Object containg a Dict containing another Object.
|
||||||
|
lines =<< trim END
|
||||||
|
vim9script
|
||||||
|
class Context
|
||||||
|
public var state: dict<any> = {}
|
||||||
|
endclass
|
||||||
|
|
||||||
|
class Metadata
|
||||||
|
public var value = 0
|
||||||
|
endclass
|
||||||
|
|
||||||
|
var ctx = Context.new()
|
||||||
|
ctx.state["meta"] = Metadata.new(2468)
|
||||||
|
|
||||||
|
const foo = ctx.state.meta.value
|
||||||
|
|
||||||
|
def F(): number
|
||||||
|
const bar = ctx.state.meta.value
|
||||||
|
return bar
|
||||||
|
enddef
|
||||||
|
|
||||||
|
assert_equal(2468, F())
|
||||||
|
END
|
||||||
|
v9.CheckScriptSuccess(lines)
|
||||||
|
|
||||||
|
# Accessing an object from a method inside the class using any type
|
||||||
|
lines =<< trim END
|
||||||
|
vim9script
|
||||||
|
class C
|
||||||
|
def _G(): string
|
||||||
|
return '_G'
|
||||||
|
enddef
|
||||||
|
static def S(o_any: any): string
|
||||||
|
return o_any._G()
|
||||||
|
enddef
|
||||||
|
endclass
|
||||||
|
|
||||||
|
var o1 = C.new()
|
||||||
|
assert_equal('_G', C.S(o1))
|
||||||
|
END
|
||||||
|
v9.CheckScriptSuccess(lines)
|
||||||
|
|
||||||
|
# Modifying an object private variable from a method in another class using
|
||||||
|
# any type
|
||||||
|
lines =<< trim END
|
||||||
|
vim9script
|
||||||
|
|
||||||
|
class A
|
||||||
|
var num = 10
|
||||||
|
endclass
|
||||||
|
|
||||||
|
class B
|
||||||
|
def SetVal(x: any)
|
||||||
|
x.num = 20
|
||||||
|
enddef
|
||||||
|
endclass
|
||||||
|
|
||||||
|
var a = A.new()
|
||||||
|
var b = B.new()
|
||||||
|
b.SetVal(a)
|
||||||
|
END
|
||||||
|
v9.CheckScriptFailure(lines, 'E1335: Variable "num" in class "A" is not writable', 1)
|
||||||
|
|
||||||
|
# Accessing a object protected variable from a method in another class using
|
||||||
|
# any type
|
||||||
|
lines =<< trim END
|
||||||
|
vim9script
|
||||||
|
|
||||||
|
class A
|
||||||
|
var _num = 10
|
||||||
|
endclass
|
||||||
|
|
||||||
|
class B
|
||||||
|
def GetVal(x: any): number
|
||||||
|
return x._num
|
||||||
|
enddef
|
||||||
|
endclass
|
||||||
|
|
||||||
|
var a = A.new()
|
||||||
|
var b = B.new()
|
||||||
|
var i = b.GetVal(a)
|
||||||
|
END
|
||||||
|
v9.CheckScriptFailure(lines, 'E1333: Cannot access protected variable "_num" in class "A"', 1)
|
||||||
|
|
||||||
|
# Accessing an object returned from an imported function and class
|
||||||
|
lines =<< trim END
|
||||||
|
vim9script
|
||||||
|
export class Foo
|
||||||
|
public var name: string
|
||||||
|
endclass
|
||||||
|
|
||||||
|
export def ReturnFooObject(): Foo
|
||||||
|
var r = Foo.new('star')
|
||||||
|
return r
|
||||||
|
enddef
|
||||||
|
END
|
||||||
|
writefile(lines, 'Xanyvar1.vim', 'D')
|
||||||
|
|
||||||
|
lines =<< trim END
|
||||||
|
vim9script
|
||||||
|
|
||||||
|
import './Xanyvar1.vim'
|
||||||
|
|
||||||
|
def GetName(): string
|
||||||
|
var whatever = Xanyvar1.ReturnFooObject()
|
||||||
|
return whatever.name
|
||||||
|
enddef
|
||||||
|
|
||||||
|
assert_equal('star', GetName())
|
||||||
|
END
|
||||||
|
v9.CheckScriptSuccess(lines)
|
||||||
|
|
||||||
|
# Try to modify a private object variable using a variable of type "any"
|
||||||
|
lines =<< trim END
|
||||||
|
vim9script
|
||||||
|
|
||||||
|
class Foo
|
||||||
|
var n: number = 10
|
||||||
|
endclass
|
||||||
|
def Fn(x: any)
|
||||||
|
x.n = 20
|
||||||
|
enddef
|
||||||
|
var a = Foo.new()
|
||||||
|
Fn(a)
|
||||||
|
END
|
||||||
|
v9.CheckScriptFailure(lines, 'E1335: Variable "n" in class "Foo" is not writable', 1)
|
||||||
|
|
||||||
|
# Try to read a protected object variable using a variable of type "any"
|
||||||
|
lines =<< trim END
|
||||||
|
vim9script
|
||||||
|
|
||||||
|
class Foo
|
||||||
|
var _n: number = 10
|
||||||
|
endclass
|
||||||
|
def Fn(x: any): number
|
||||||
|
return x._n
|
||||||
|
enddef
|
||||||
|
|
||||||
|
var a = Foo.new()
|
||||||
|
Fn(a)
|
||||||
|
END
|
||||||
|
v9.CheckScriptFailure(lines, 'E1333: Cannot access protected variable "_n" in class "Foo"', 1)
|
||||||
|
|
||||||
|
# Read a protected object variable using a variable of type "any" in an object
|
||||||
|
# method
|
||||||
|
lines =<< trim END
|
||||||
|
vim9script
|
||||||
|
|
||||||
|
class Foo
|
||||||
|
var _n: number = 10
|
||||||
|
def Fn(x: any): number
|
||||||
|
return x._n
|
||||||
|
enddef
|
||||||
|
endclass
|
||||||
|
|
||||||
|
var a = Foo.new()
|
||||||
|
assert_equal(10, a.Fn(a))
|
||||||
|
END
|
||||||
|
v9.CheckScriptSuccess(lines)
|
||||||
|
|
||||||
|
# Try to call a protected object method using a "any" type variable
|
||||||
|
lines =<< trim END
|
||||||
|
vim9script
|
||||||
|
|
||||||
|
class Foo
|
||||||
|
def _GetVal(): number
|
||||||
|
return 234
|
||||||
|
enddef
|
||||||
|
endclass
|
||||||
|
def Fn(x: any): number
|
||||||
|
return x._GetVal()
|
||||||
|
enddef
|
||||||
|
|
||||||
|
var a = Foo.new()
|
||||||
|
Fn(a)
|
||||||
|
END
|
||||||
|
v9.CheckScriptFailure(lines, 'E1366: Cannot access protected method: _GetVal', 1)
|
||||||
|
|
||||||
|
# Call a protected object method using a "any" type variable from another
|
||||||
|
# object method
|
||||||
|
lines =<< trim END
|
||||||
|
vim9script
|
||||||
|
|
||||||
|
class Foo
|
||||||
|
def _GetVal(): number
|
||||||
|
return 234
|
||||||
|
enddef
|
||||||
|
def FooVal(x: any): number
|
||||||
|
return x._GetVal()
|
||||||
|
enddef
|
||||||
|
endclass
|
||||||
|
|
||||||
|
var a = Foo.new()
|
||||||
|
assert_equal(234, a.FooVal(a))
|
||||||
|
END
|
||||||
|
v9.CheckScriptSuccess(lines)
|
||||||
|
|
||||||
|
# Method chaining
|
||||||
|
lines =<< trim END
|
||||||
|
vim9script
|
||||||
|
|
||||||
|
export class T
|
||||||
|
var id: number = 268
|
||||||
|
def F(): any
|
||||||
|
return this
|
||||||
|
enddef
|
||||||
|
endclass
|
||||||
|
|
||||||
|
def H()
|
||||||
|
var a = T.new().F().F()
|
||||||
|
assert_equal(268, a.id)
|
||||||
|
enddef
|
||||||
|
H()
|
||||||
|
|
||||||
|
var b: T = T.new().F().F()
|
||||||
|
assert_equal(268, b.id)
|
||||||
|
END
|
||||||
|
v9.CheckScriptSuccess(lines)
|
||||||
|
|
||||||
|
# Using a null object to access a member variable
|
||||||
|
lines =<< trim END
|
||||||
|
vim9script
|
||||||
|
def Fn(x: any): number
|
||||||
|
return x.num
|
||||||
|
enddef
|
||||||
|
|
||||||
|
Fn(null_object)
|
||||||
|
END
|
||||||
|
v9.CheckScriptFailure(lines, 'E1360: Using a null object', 1)
|
||||||
|
|
||||||
|
# Using a null object to invoke a method
|
||||||
|
lines =<< trim END
|
||||||
|
vim9script
|
||||||
|
def Fn(x: any)
|
||||||
|
x.Foo()
|
||||||
|
enddef
|
||||||
|
|
||||||
|
Fn(null_object)
|
||||||
|
END
|
||||||
|
v9.CheckScriptFailure(lines, 'E1360: Using a null object', 1)
|
||||||
|
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 */
|
||||||
|
/**/
|
||||||
|
850,
|
||||||
/**/
|
/**/
|
||||||
849,
|
849,
|
||||||
/**/
|
/**/
|
||||||
|
@ -2777,12 +2777,13 @@ done:
|
|||||||
* "rettv". If "is_object" is TRUE, then the object member variable table is
|
* "rettv". If "is_object" is TRUE, then the object member variable table is
|
||||||
* searched. Otherwise the class member variable table is searched.
|
* searched. Otherwise the class member variable table is searched.
|
||||||
*/
|
*/
|
||||||
static int
|
int
|
||||||
get_member_tv(
|
get_member_tv(
|
||||||
class_T *cl,
|
class_T *cl,
|
||||||
int is_object,
|
int is_object,
|
||||||
char_u *name,
|
char_u *name,
|
||||||
size_t namelen,
|
size_t namelen,
|
||||||
|
class_T *current_class,
|
||||||
typval_T *rettv)
|
typval_T *rettv)
|
||||||
{
|
{
|
||||||
ocmember_T *m;
|
ocmember_T *m;
|
||||||
@ -2793,7 +2794,8 @@ get_member_tv(
|
|||||||
if (m == NULL)
|
if (m == NULL)
|
||||||
return FAIL;
|
return FAIL;
|
||||||
|
|
||||||
if (*name == '_')
|
if (*name == '_' && (current_class == NULL ||
|
||||||
|
!class_instance_of(current_class, cl)))
|
||||||
{
|
{
|
||||||
emsg_var_cl_define(e_cannot_access_protected_variable_str,
|
emsg_var_cl_define(e_cannot_access_protected_variable_str,
|
||||||
m->ocm_name, 0, cl);
|
m->ocm_name, 0, cl);
|
||||||
@ -2873,7 +2875,7 @@ call_oc_method(
|
|||||||
|
|
||||||
if (ocm == NULL && *fp->uf_name == '_')
|
if (ocm == NULL && *fp->uf_name == '_')
|
||||||
{
|
{
|
||||||
// Cannot access a private method outside of a class
|
// Cannot access a protected method outside of a class
|
||||||
semsg(_(e_cannot_access_protected_method_str), fp->uf_name);
|
semsg(_(e_cannot_access_protected_method_str), fp->uf_name);
|
||||||
return FAIL;
|
return FAIL;
|
||||||
}
|
}
|
||||||
@ -2916,6 +2918,33 @@ call_oc_method(
|
|||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Create a partial typval for "obj.obj_method" and store it in "rettv".
|
||||||
|
* Returns OK on success and FAIL on memory allocation failure.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
obj_method_to_partial_tv(object_T *obj, ufunc_T *obj_method, typval_T *rettv)
|
||||||
|
{
|
||||||
|
partial_T *pt = ALLOC_CLEAR_ONE(partial_T);
|
||||||
|
if (pt == NULL)
|
||||||
|
return FAIL;
|
||||||
|
|
||||||
|
pt->pt_refcount = 1;
|
||||||
|
if (obj != NULL)
|
||||||
|
{
|
||||||
|
pt->pt_obj = obj;
|
||||||
|
++pt->pt_obj->obj_refcount;
|
||||||
|
}
|
||||||
|
pt->pt_auto = TRUE;
|
||||||
|
pt->pt_func = obj_method;
|
||||||
|
func_ptr_ref(pt->pt_func);
|
||||||
|
|
||||||
|
rettv->v_type = VAR_PARTIAL;
|
||||||
|
rettv->vval.v_partial = pt;
|
||||||
|
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Evaluate what comes after a class:
|
* Evaluate what comes after a class:
|
||||||
* - class member: SomeClass.varname
|
* - class member: SomeClass.varname
|
||||||
@ -2978,7 +3007,7 @@ class_object_index(
|
|||||||
// Search in the object member variable table and the class member
|
// Search in the object member variable table and the class member
|
||||||
// variable table.
|
// variable table.
|
||||||
int is_object = rettv->v_type == VAR_OBJECT;
|
int is_object = rettv->v_type == VAR_OBJECT;
|
||||||
if (get_member_tv(cl, is_object, name, len, rettv) == OK)
|
if (get_member_tv(cl, is_object, name, len, NULL, rettv) == OK)
|
||||||
{
|
{
|
||||||
*arg = name_end;
|
*arg = name_end;
|
||||||
return OK;
|
return OK;
|
||||||
@ -2989,28 +3018,17 @@ class_object_index(
|
|||||||
ufunc_T *fp = method_lookup(cl, rettv->v_type, name, len, &fidx);
|
ufunc_T *fp = method_lookup(cl, rettv->v_type, name, len, &fidx);
|
||||||
if (fp != NULL)
|
if (fp != NULL)
|
||||||
{
|
{
|
||||||
// Private methods are not accessible outside the class
|
// Protected methods are not accessible outside the class
|
||||||
if (*name == '_')
|
if (*name == '_')
|
||||||
{
|
{
|
||||||
semsg(_(e_cannot_access_protected_method_str), fp->uf_name);
|
semsg(_(e_cannot_access_protected_method_str), fp->uf_name);
|
||||||
return FAIL;
|
return FAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
partial_T *pt = ALLOC_CLEAR_ONE(partial_T);
|
if (obj_method_to_partial_tv(is_object ? rettv->vval.v_object :
|
||||||
if (pt == NULL)
|
NULL, fp, rettv) == FAIL)
|
||||||
return FAIL;
|
return FAIL;
|
||||||
|
|
||||||
pt->pt_refcount = 1;
|
|
||||||
if (is_object)
|
|
||||||
{
|
|
||||||
pt->pt_obj = rettv->vval.v_object;
|
|
||||||
++pt->pt_obj->obj_refcount;
|
|
||||||
}
|
|
||||||
pt->pt_auto = TRUE;
|
|
||||||
pt->pt_func = fp;
|
|
||||||
func_ptr_ref(pt->pt_func);
|
|
||||||
rettv->v_type = VAR_PARTIAL;
|
|
||||||
rettv->vval.v_partial = pt;
|
|
||||||
*arg = name_end;
|
*arg = name_end;
|
||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
|
@ -2268,14 +2268,22 @@ execute_storeindex(isn_T *iptr, ectx_T *ectx)
|
|||||||
ocmember_T *m = object_member_lookup(cl, member, 0, &m_idx);
|
ocmember_T *m = object_member_lookup(cl, member, 0, &m_idx);
|
||||||
if (m != NULL)
|
if (m != NULL)
|
||||||
{
|
{
|
||||||
if (*member == '_')
|
// Get the current function
|
||||||
|
ufunc_T *ufunc = (((dfunc_T *)def_functions.ga_data)
|
||||||
|
+ ectx->ec_dfunc_idx)->df_ufunc;
|
||||||
|
|
||||||
|
// Check whether the member variable is writeable
|
||||||
|
if ((m->ocm_access != VIM_ACCESS_ALL) &&
|
||||||
|
(ufunc->uf_class == NULL ||
|
||||||
|
!class_instance_of(ufunc->uf_class, cl)))
|
||||||
{
|
{
|
||||||
emsg_var_cl_define(
|
char *msg = (m->ocm_access == VIM_ACCESS_PRIVATE)
|
||||||
e_cannot_access_protected_variable_str,
|
? e_cannot_access_protected_variable_str
|
||||||
m->ocm_name, 0, cl);
|
: e_variable_is_not_writable_str;
|
||||||
|
emsg_var_cl_define(msg, m->ocm_name, 0, cl);
|
||||||
status = FAIL;
|
status = FAIL;
|
||||||
}
|
}
|
||||||
|
else
|
||||||
lidx = m_idx;
|
lidx = m_idx;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -3119,6 +3127,73 @@ object_required_error(typval_T *tv)
|
|||||||
clear_type_list(&type_list);
|
clear_type_list(&type_list);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Accessing the member of an object stored in a variable of type "any".
|
||||||
|
* Returns OK if the member variable is present.
|
||||||
|
* Returns FAIL if the variable is not found.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
any_var_get_obj_member(class_T *current_class, isn_T *iptr, typval_T *tv)
|
||||||
|
{
|
||||||
|
object_T *obj = tv->vval.v_object;
|
||||||
|
typval_T mtv;
|
||||||
|
|
||||||
|
if (obj == NULL)
|
||||||
|
{
|
||||||
|
SOURCING_LNUM = iptr->isn_lnum;
|
||||||
|
emsg(_(e_using_null_object));
|
||||||
|
return FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get_member_tv() needs the object information in the typval argument.
|
||||||
|
// So set the object information.
|
||||||
|
copy_tv(tv, &mtv);
|
||||||
|
|
||||||
|
// 'name' can either be a object variable or a object method
|
||||||
|
int namelen = STRLEN(iptr->isn_arg.string);
|
||||||
|
int save_did_emsg = did_emsg;
|
||||||
|
|
||||||
|
if (get_member_tv(obj->obj_class, TRUE, iptr->isn_arg.string, namelen,
|
||||||
|
current_class, &mtv) == OK)
|
||||||
|
{
|
||||||
|
copy_tv(&mtv, tv);
|
||||||
|
clear_tv(&mtv);
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (did_emsg != save_did_emsg)
|
||||||
|
return FAIL;
|
||||||
|
|
||||||
|
// could be a member function
|
||||||
|
ufunc_T *obj_method;
|
||||||
|
int obj_method_idx;
|
||||||
|
|
||||||
|
obj_method = method_lookup(obj->obj_class, VAR_OBJECT,
|
||||||
|
iptr->isn_arg.string, namelen,
|
||||||
|
&obj_method_idx);
|
||||||
|
if (obj_method == NULL)
|
||||||
|
{
|
||||||
|
SOURCING_LNUM = iptr->isn_lnum;
|
||||||
|
semsg(_(e_variable_not_found_on_object_str_str), iptr->isn_arg.string,
|
||||||
|
obj->obj_class->class_name);
|
||||||
|
return FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Protected methods are not accessible outside the class
|
||||||
|
if (*obj_method->uf_name == '_'
|
||||||
|
&& !class_instance_of(current_class, obj->obj_class))
|
||||||
|
{
|
||||||
|
semsg(_(e_cannot_access_protected_method_str), obj_method->uf_name);
|
||||||
|
return FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a partial for the member function
|
||||||
|
if (obj_method_to_partial_tv(obj, obj_method, tv) == FAIL)
|
||||||
|
return FAIL;
|
||||||
|
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Execute instructions in execution context "ectx".
|
* Execute instructions in execution context "ectx".
|
||||||
* Return OK or FAIL;
|
* Return OK or FAIL;
|
||||||
@ -5482,6 +5557,7 @@ exec_instructions(ectx_T *ectx)
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
// dict member with string key (dict['member'])
|
||||||
case ISN_MEMBER:
|
case ISN_MEMBER:
|
||||||
{
|
{
|
||||||
dict_T *dict;
|
dict_T *dict;
|
||||||
@ -5526,13 +5602,28 @@ exec_instructions(ectx_T *ectx)
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// dict member with string key
|
// dict member with string key (dict.member)
|
||||||
|
// or can be an object
|
||||||
case ISN_STRINGMEMBER:
|
case ISN_STRINGMEMBER:
|
||||||
{
|
{
|
||||||
dict_T *dict;
|
dict_T *dict;
|
||||||
dictitem_T *di;
|
dictitem_T *di;
|
||||||
|
|
||||||
tv = STACK_TV_BOT(-1);
|
tv = STACK_TV_BOT(-1);
|
||||||
|
|
||||||
|
if (tv->v_type == VAR_OBJECT)
|
||||||
|
{
|
||||||
|
if (dict_stack_save(tv) == FAIL)
|
||||||
|
goto on_fatal_error;
|
||||||
|
|
||||||
|
ufunc_T *ufunc = (((dfunc_T *)def_functions.ga_data)
|
||||||
|
+ ectx->ec_dfunc_idx)->df_ufunc;
|
||||||
|
// Class object (not a Dict)
|
||||||
|
if (any_var_get_obj_member(ufunc->uf_class, iptr, tv) == FAIL)
|
||||||
|
goto on_error;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
if (tv->v_type != VAR_DICT || tv->vval.v_dict == NULL)
|
if (tv->v_type != VAR_DICT || tv->vval.v_dict == NULL)
|
||||||
{
|
{
|
||||||
SOURCING_LNUM = iptr->isn_lnum;
|
SOURCING_LNUM = iptr->isn_lnum;
|
||||||
@ -5549,13 +5640,14 @@ exec_instructions(ectx_T *ectx)
|
|||||||
iptr->isn_arg.string);
|
iptr->isn_arg.string);
|
||||||
goto on_error;
|
goto on_error;
|
||||||
}
|
}
|
||||||
// Put the dict used on the dict stack, it might be used by
|
// Put the dict used on the dict stack, it might be
|
||||||
// a dict function later.
|
// used by a dict function later.
|
||||||
if (dict_stack_save(tv) == FAIL)
|
if (dict_stack_save(tv) == FAIL)
|
||||||
goto on_fatal_error;
|
goto on_fatal_error;
|
||||||
|
|
||||||
copy_tv(&di->di_tv, tv);
|
copy_tv(&di->di_tv, tv);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ISN_GET_OBJ_MEMBER:
|
case ISN_GET_OBJ_MEMBER:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user