forked from aniani/vim
patch 9.0.1982: vim9: clean up from v9.0.1955
Problem: vim9: clean up from v9.0.1955 Solution: Fix a few remaining issues, improve error message - Use `cl_exec`, the executing class, to check permissions in `get_lval()`. - Handle lockvar of script variable from class. - Add 'in class "Xxx"' to e_cannot_access_private_variable_str. closes: #13222 Signed-off-by: Christian Brabandt <cb@256bit.org> Co-authored-by: Ernie Rael <errael@raelity.com>
This commit is contained in:
committed by
Christian Brabandt
parent
0583491277
commit
64885645e7
@@ -3411,7 +3411,7 @@ EXTERN char e_public_must_be_followed_by_this_or_static[]
|
||||
EXTERN char e_public_variable_name_cannot_start_with_underscore_str[]
|
||||
INIT(= N_("E1332: Public variable name cannot start with underscore: %s"));
|
||||
EXTERN char e_cannot_access_private_variable_str[]
|
||||
INIT(= N_("E1333: Cannot access private variable: %s"));
|
||||
INIT(= N_("E1333: Cannot access private variable \"%s\" in class \"%s\""));
|
||||
// E1334 unused
|
||||
EXTERN char e_variable_is_not_writable_str[]
|
||||
INIT(= N_("E1335: Variable \"%s\" in class \"%s\" is not writable"));
|
||||
|
||||
149
src/eval.c
149
src/eval.c
@@ -985,6 +985,52 @@ eval_foldexpr(win_T *wp, int *cp)
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef LOG_LOCKVAR
|
||||
typedef struct flag_string_S
|
||||
{
|
||||
int flag;
|
||||
char *str;
|
||||
} flag_string_T;
|
||||
|
||||
static char *
|
||||
flags_tostring(int flags, flag_string_T *_fstring, char *buf, size_t n)
|
||||
{
|
||||
char *p = buf;
|
||||
*p = NUL;
|
||||
for (flag_string_T *fstring = _fstring; fstring->flag; ++fstring)
|
||||
{
|
||||
if ((fstring->flag & flags) != 0)
|
||||
{
|
||||
size_t len = STRLEN(fstring->str);
|
||||
if (n > p - buf + len + 7)
|
||||
{
|
||||
STRCAT(p, fstring->str);
|
||||
p += len;
|
||||
STRCAT(p, " ");
|
||||
++p;
|
||||
}
|
||||
else
|
||||
{
|
||||
STRCAT(buf, "...");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
flag_string_T glv_flag_strings[] = {
|
||||
{ GLV_QUIET, "QUIET" },
|
||||
{ GLV_NO_AUTOLOAD, "NO_AUTOLOAD" },
|
||||
{ GLV_READ_ONLY, "READ_ONLY" },
|
||||
{ GLV_NO_DECL, "NO_DECL" },
|
||||
{ GLV_COMPILING, "COMPILING" },
|
||||
{ GLV_ASSIGN_WITH_OP, "ASSIGN_WITH_OP" },
|
||||
{ GLV_PREFER_FUNC, "PREFER_FUNC" },
|
||||
{ 0, NULL }
|
||||
};
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Fill in "lp" using "root". This is used in a special case when
|
||||
* "get_lval()" parses a bare word when "lval_root" is not NULL.
|
||||
@@ -1004,30 +1050,30 @@ eval_foldexpr(win_T *wp, int *cp)
|
||||
* execute_instructions: ISN_LOCKUNLOCK - sets lval_root from stack.
|
||||
*/
|
||||
static void
|
||||
get_lval_root(lval_T *lp, typval_T *root, int is_arg)
|
||||
get_lval_root(lval_T *lp, lval_root_T *lr)
|
||||
{
|
||||
#ifdef LOG_LOCKVAR
|
||||
ch_log(NULL, "LKVAR: get_lvalroot(): name %s", lp->ll_name);
|
||||
ch_log(NULL, "LKVAR: get_lval_root(): name %s", lp->ll_name);
|
||||
#endif
|
||||
if (!is_arg && root->v_type == VAR_CLASS)
|
||||
if (!lr->lr_is_arg && lr->lr_tv->v_type == VAR_CLASS)
|
||||
{
|
||||
if (root->vval.v_class != NULL)
|
||||
if (lr->lr_tv->vval.v_class != NULL)
|
||||
{
|
||||
// Special special case. Look for a bare class variable reference.
|
||||
class_T *cl = root->vval.v_class;
|
||||
class_T *cl = lr->lr_tv->vval.v_class;
|
||||
int m_idx;
|
||||
ocmember_T *m = class_member_lookup(cl, lp->ll_name,
|
||||
lp->ll_name_end - lp->ll_name, &m_idx);
|
||||
if (m != NULL)
|
||||
{
|
||||
// Assuming "inside class" since bare reference.
|
||||
lp->ll_class = root->vval.v_class;
|
||||
lp->ll_class = lr->lr_tv->vval.v_class;
|
||||
lp->ll_oi = m_idx;
|
||||
lp->ll_valtype = m->ocm_type;
|
||||
lp->ll_tv = &lp->ll_class->class_members_tv[m_idx];
|
||||
#ifdef LOG_LOCKVAR
|
||||
ch_log(NULL, "LKVAR: get_lvalroot() class member: name %s",
|
||||
lp->ll_name);
|
||||
ch_log(NULL, "LKVAR: ... class member %s.%s",
|
||||
lp->ll_class->class_name, lp->ll_name);
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
@@ -1035,12 +1081,54 @@ get_lval_root(lval_T *lp, typval_T *root, int is_arg)
|
||||
}
|
||||
|
||||
#ifdef LOG_LOCKVAR
|
||||
ch_log(NULL, "LKVAR: get_lvalroot() any type");
|
||||
ch_log(NULL, "LKVAR: ... type: %s", vartype_name(lr->lr_tv->v_type));
|
||||
#endif
|
||||
lp->ll_tv = root;
|
||||
lp->ll_tv = lr->lr_tv;
|
||||
lp->ll_is_root = TRUE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if the class has permission to access the member.
|
||||
* Returns OK or FAIL.
|
||||
*/
|
||||
static int
|
||||
get_lval_check_access(
|
||||
class_T *cl_exec, // executing class, NULL if :def or script level
|
||||
class_T *cl, // class which contains the member
|
||||
ocmember_T *om, // member being accessed
|
||||
char_u *p, // char after member name
|
||||
int flags) // GLV flags to check if writing to lval
|
||||
{
|
||||
#ifdef LOG_LOCKVAR
|
||||
ch_log(NULL, "LKVAR: get_lval_check_access(), cl_exec %p, cl %p, %c",
|
||||
(void*)cl_exec, (void*)cl, *p);
|
||||
#endif
|
||||
if (cl_exec == NULL || cl_exec != cl)
|
||||
{
|
||||
switch (om->ocm_access)
|
||||
{
|
||||
case VIM_ACCESS_PRIVATE:
|
||||
semsg(_(e_cannot_access_private_variable_str),
|
||||
om->ocm_name, cl->class_name);
|
||||
return FAIL;
|
||||
case VIM_ACCESS_READ:
|
||||
// If [idx] or .key following, read only OK.
|
||||
if (*p == '[' || *p == '.')
|
||||
break;
|
||||
if ((flags & GLV_READ_ONLY) == 0)
|
||||
{
|
||||
semsg(_(e_variable_is_not_writable_str),
|
||||
om->ocm_name, cl->class_name);
|
||||
return FAIL;
|
||||
}
|
||||
break;
|
||||
case VIM_ACCESS_ALL:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get an lval: variable, Dict item or List item that can be assigned a value
|
||||
* to: "name", "na{me}", "name[expr]", "name[expr:expr]", "name[expr][expr]",
|
||||
@@ -1083,10 +1171,19 @@ get_lval(
|
||||
int quiet = flags & GLV_QUIET;
|
||||
int writing = 0;
|
||||
int vim9script = in_vim9script();
|
||||
class_T *cl_exec = NULL; // class that is executing, or NULL.
|
||||
|
||||
#ifdef LOG_LOCKVAR
|
||||
ch_log(NULL, "LKVAR: get_lval(): name %s, lval_root %p",
|
||||
name, (void*)lval_root);
|
||||
if (lval_root == NULL)
|
||||
ch_log(NULL,
|
||||
"LKVAR: get_lval(): name %s, lval_root (nil)", name);
|
||||
else
|
||||
ch_log(NULL,
|
||||
"LKVAR: get_lval(): name %s, lr_tv %p lr_is_arg %d",
|
||||
name, (void*)lval_root->lr_tv, lval_root->lr_is_arg);
|
||||
char buf[80];
|
||||
ch_log(NULL, "LKVAR: ...: GLV flags %s",
|
||||
flags_tostring(flags, glv_flag_strings, buf, sizeof(buf)));
|
||||
#endif
|
||||
|
||||
// Clear everything in "lp".
|
||||
@@ -1221,15 +1318,16 @@ get_lval(
|
||||
if ((*p != '[' && *p != '.'))
|
||||
{
|
||||
if (lval_root != NULL)
|
||||
get_lval_root(lp, lval_root, lval_root_is_arg);
|
||||
get_lval_root(lp, lval_root);
|
||||
return p;
|
||||
}
|
||||
|
||||
if (vim9script && lval_root != NULL)
|
||||
{
|
||||
// using local variable
|
||||
lp->ll_tv = lval_root;
|
||||
lp->ll_tv = lval_root->lr_tv;
|
||||
v = NULL;
|
||||
cl_exec = lval_root->lr_cl_exec;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1643,26 +1741,9 @@ get_lval(
|
||||
lp->ll_oi = m_idx;
|
||||
if (om != NULL)
|
||||
{
|
||||
switch (om->ocm_access)
|
||||
{
|
||||
case VIM_ACCESS_PRIVATE:
|
||||
semsg(_(e_cannot_access_private_variable_str),
|
||||
om->ocm_name);
|
||||
return NULL;
|
||||
case VIM_ACCESS_READ:
|
||||
// If [idx] or .key following, read only OK.
|
||||
if (*p == '[' || *p == '.')
|
||||
break;
|
||||
if ((flags & GLV_READ_ONLY) == 0)
|
||||
{
|
||||
semsg(_(e_variable_is_not_writable_str),
|
||||
om->ocm_name, cl->class_name);
|
||||
return NULL;
|
||||
}
|
||||
break;
|
||||
case VIM_ACCESS_ALL:
|
||||
break;
|
||||
}
|
||||
if (get_lval_check_access(cl_exec, cl, om,
|
||||
p, flags) == FAIL)
|
||||
return NULL;
|
||||
|
||||
lp->ll_valtype = om->ocm_type;
|
||||
|
||||
|
||||
@@ -2278,30 +2278,6 @@ do_lock_var(
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef LOG_LOCKVAR
|
||||
static char *
|
||||
vartype_tostring(vartype_T vartype)
|
||||
{
|
||||
return
|
||||
vartype == VAR_BOOL ? "v_number"
|
||||
: vartype == VAR_SPECIAL ? "v_number"
|
||||
: vartype == VAR_NUMBER ? "v_number"
|
||||
: vartype == VAR_FLOAT ? "v_float"
|
||||
: vartype == VAR_STRING ? "v_string"
|
||||
: vartype == VAR_BLOB ? "v_blob"
|
||||
: vartype == VAR_FUNC ? "v_string"
|
||||
: vartype == VAR_PARTIAL ? "v_partial"
|
||||
: vartype == VAR_LIST ? "v_list"
|
||||
: vartype == VAR_DICT ? "v_dict"
|
||||
: vartype == VAR_JOB ? "v_job"
|
||||
: vartype == VAR_CHANNEL ? "v_channel"
|
||||
: vartype == VAR_INSTR ? "v_instr"
|
||||
: vartype == VAR_CLASS ? "v_class"
|
||||
: vartype == VAR_OBJECT ? "v_object"
|
||||
: "";
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Lock or unlock an item. "deep" is nr of levels to go.
|
||||
* When "check_refcount" is TRUE do not lock a list or dict with a reference
|
||||
@@ -2319,7 +2295,7 @@ item_lock(typval_T *tv, int deep, int lock, int check_refcount)
|
||||
int todo;
|
||||
|
||||
#ifdef LOG_LOCKVAR
|
||||
ch_log(NULL, "LKVAR: item_lock(): type %s", vartype_tostring(tv->v_type));
|
||||
ch_log(NULL, "LKVAR: item_lock(): type %s", vartype_name(tv->v_type));
|
||||
#endif
|
||||
|
||||
if (recurse >= DICT_MAXNEST)
|
||||
|
||||
@@ -1953,8 +1953,7 @@ EXTERN int timer_busy INIT(= 0); // when timer is inside vgetc() then > 0
|
||||
#ifdef FEAT_EVAL
|
||||
EXTERN int input_busy INIT(= 0); // when inside get_user_input() then > 0
|
||||
|
||||
EXTERN typval_T *lval_root INIT(= NULL);
|
||||
EXTERN int lval_root_is_arg INIT(= 0);
|
||||
EXTERN lval_root_T *lval_root INIT(= NULL);
|
||||
#endif
|
||||
|
||||
#ifdef FEAT_BEVAL_TERM
|
||||
|
||||
@@ -4595,6 +4595,16 @@ typedef struct lval_S
|
||||
// ignore the rest.
|
||||
} lval_T;
|
||||
|
||||
/**
|
||||
* This may be used to specify the base type that get_lval() uses when
|
||||
* following a chain, for example a[idx1][idx2].
|
||||
*/
|
||||
typedef struct lval_root_S {
|
||||
typval_T *lr_tv;
|
||||
class_T *lr_cl_exec; // executing class for access checking
|
||||
int lr_is_arg;
|
||||
} lval_root_T;
|
||||
|
||||
// Structure used to save the current state. Used when executing Normal mode
|
||||
// commands while in any other mode.
|
||||
typedef struct {
|
||||
|
||||
@@ -626,7 +626,7 @@ def Test_member_any_used_as_object()
|
||||
var outer_obj = Outer.new(inner_obj)
|
||||
F(outer_obj)
|
||||
END
|
||||
v9.CheckSourceFailure(lines, 'E1333: Cannot access private variable: _value', 1)
|
||||
v9.CheckSourceFailure(lines, 'E1333: Cannot access private variable "_value" in class "Inner"', 1)
|
||||
|
||||
# Try modifying a non-existing variable using an "any" object
|
||||
lines =<< trim END
|
||||
@@ -1063,9 +1063,9 @@ def Test_instance_variable_access()
|
||||
assert_equal(1, trip.GetOne())
|
||||
assert_equal(2, trip.two)
|
||||
assert_equal(3, trip.three)
|
||||
assert_fails('echo trip._one', 'E1333: Cannot access private variable: _one')
|
||||
assert_fails('echo trip._one', 'E1333: Cannot access private variable "_one" in class "Triple"')
|
||||
|
||||
assert_fails('trip._one = 11', 'E1333: Cannot access private variable: _one')
|
||||
assert_fails('trip._one = 11', 'E1333: Cannot access private variable "_one" in class "Triple"')
|
||||
assert_fails('trip.two = 22', 'E1335: Variable "two" in class "Triple" is not writable')
|
||||
trip.three = 33
|
||||
assert_equal(33, trip.three)
|
||||
@@ -1315,7 +1315,7 @@ def Test_class_variable_access()
|
||||
var b = B.new()
|
||||
b.Foo()
|
||||
END
|
||||
v9.CheckSourceFailure(lines, 'E1333: Cannot access private variable: _priv_class_var', 1)
|
||||
v9.CheckSourceFailure(lines, 'E1333: Cannot access private variable "_priv_class_var" in class "A"', 1)
|
||||
|
||||
# A private class variable cannot be modified from a child class
|
||||
lines =<< trim END
|
||||
@@ -1333,7 +1333,7 @@ def Test_class_variable_access()
|
||||
var b = B.new()
|
||||
b.Foo()
|
||||
END
|
||||
v9.CheckSourceFailure(lines, 'E1333: Cannot access private variable: _priv_class_var', 1)
|
||||
v9.CheckSourceFailure(lines, 'E1333: Cannot access private variable "_priv_class_var" in class "A"', 1)
|
||||
|
||||
# Access from child class extending a class and from script context
|
||||
lines =<< trim END
|
||||
@@ -1514,8 +1514,8 @@ def Test_class_member()
|
||||
assert_fails('TextPos.counter = 5', 'E1335: Variable "counter" in class "TextPos" is not writable')
|
||||
assert_fails('TextPos.counter += 5', 'E1335: Variable "counter" in class "TextPos" is not writable')
|
||||
|
||||
assert_fails('echo TextPos._secret', 'E1333: Cannot access private variable: _secret')
|
||||
assert_fails('TextPos._secret = 8', 'E1333: Cannot access private variable: _secret')
|
||||
assert_fails('echo TextPos._secret', 'E1333: Cannot access private variable "_secret" in class "TextPos"')
|
||||
assert_fails('TextPos._secret = 8', 'E1333: Cannot access private variable "_secret" in class "TextPos"')
|
||||
|
||||
assert_equal(42, TextPos.anybody)
|
||||
TextPos.anybody = 12
|
||||
@@ -3522,9 +3522,6 @@ def Test_lockvar_object_variable()
|
||||
# method arg, static method arg.
|
||||
# Also different depths
|
||||
|
||||
# TODO: handle inside_class in vim9class
|
||||
# lockvar of a read-only currently fails even if inside
|
||||
|
||||
#
|
||||
# lockvar of read-only object variable
|
||||
#
|
||||
@@ -3542,8 +3539,7 @@ def Test_lockvar_object_variable()
|
||||
var o = C.new(3)
|
||||
o.Lock()
|
||||
END
|
||||
# TODO: wrong error
|
||||
v9.CheckSourceFailure(lines, 'E1335: Variable "val1" in class "C" is not writable')
|
||||
v9.CheckSourceFailure(lines, 'E1391: Cannot (un)lock variable "this.val1" in class "C"')
|
||||
|
||||
# read-only lockvar from scriptlevel
|
||||
lines =<< trim END
|
||||
@@ -3602,8 +3598,7 @@ def Test_lockvar_object_variable()
|
||||
var o = C.new(3)
|
||||
o.Lock(C.new(5))
|
||||
END
|
||||
# TODO: wrong error, tricky since type "any"
|
||||
v9.CheckSourceFailure(lines, 'E1335: Variable "val5" in class "C" is not writable')
|
||||
v9.CheckSourceFailure(lines, 'E1391: Cannot (un)lock variable "o_any.val5" in class "C"')
|
||||
|
||||
# read-only lockvar from class method arg
|
||||
lines =<< trim END
|
||||
@@ -3618,8 +3613,7 @@ def Test_lockvar_object_variable()
|
||||
var o = C.new(3)
|
||||
C.Lock(o)
|
||||
END
|
||||
# TODO: wrong error, tricky since type "any"
|
||||
v9.CheckSourceFailure(lines, 'E1335: Variable "val6" in class "C" is not writable')
|
||||
v9.CheckSourceFailure(lines, 'E1391: Cannot (un)lock variable "o_any.val6" in class "C"')
|
||||
|
||||
#
|
||||
# lockvar of public object variable
|
||||
@@ -3987,6 +3981,71 @@ def Test_lockvar_general()
|
||||
assert_equal([ [9], [2], [8] ], o.val)
|
||||
END
|
||||
v9.CheckSourceSuccess(lines)
|
||||
|
||||
# lock a script level variable from an object method
|
||||
lines =<< trim END
|
||||
vim9script
|
||||
|
||||
class C
|
||||
def Lock()
|
||||
lockvar l
|
||||
enddef
|
||||
endclass
|
||||
|
||||
var l = [1]
|
||||
C.new().Lock()
|
||||
l[0] = 11
|
||||
END
|
||||
v9.CheckSourceFailure(lines, 'E741: Value is locked: l[0] = 11', 11)
|
||||
|
||||
# lock a list element referenced by a private object variable
|
||||
# in an object fetched via a script level list
|
||||
lines =<< trim END
|
||||
vim9script
|
||||
|
||||
class C
|
||||
this._v1: list<list<number>>
|
||||
def Lock()
|
||||
lockvar lc[0]._v1[1]
|
||||
enddef
|
||||
endclass
|
||||
|
||||
var l = [[1], [2], [3]]
|
||||
var o = C.new(l)
|
||||
var lc: list<C> = [ o ]
|
||||
|
||||
o.Lock()
|
||||
l[0] = [22]
|
||||
l[1] = [33]
|
||||
END
|
||||
v9.CheckSourceFailure(lines, 'E741: Value is locked: l[1] = [33]', 16)
|
||||
|
||||
# similar to the previous test, except the locking code is executing
|
||||
# in a class that does not own the private variable.
|
||||
# Note that the locking code is in a class has a private variable of
|
||||
# the same name.
|
||||
lines =<< trim END
|
||||
vim9script
|
||||
|
||||
class C2
|
||||
this._v1: list<list<number>>
|
||||
def Lock(obj: any)
|
||||
lockvar lc[0]._v1[1]
|
||||
enddef
|
||||
endclass
|
||||
|
||||
class C
|
||||
this._v1: list<list<number>>
|
||||
endclass
|
||||
|
||||
var l = [[1], [2], [3]]
|
||||
var o = C.new(l)
|
||||
var lc: list<C> = [ o ]
|
||||
|
||||
var o2 = C2.new()
|
||||
o2.Lock(o)
|
||||
END
|
||||
v9.CheckSourceFailure(lines, 'E1333: Cannot access private variable "_v1" in class "C"')
|
||||
enddef
|
||||
|
||||
" Test for a private object method
|
||||
@@ -4518,7 +4577,7 @@ def Test_static_inheritence()
|
||||
assert_equal(102, ob.AccessObject())
|
||||
assert_equal(103, oc.AccessObject())
|
||||
|
||||
assert_fails('echo oc.AccessPrivateStaticThroughClassName()', 'E1333: Cannot access private variable: _svar')
|
||||
assert_fails('echo oc.AccessPrivateStaticThroughClassName()', 'E1333: Cannot access private variable "_svar" in class "A"')
|
||||
|
||||
# verify object properly resolves to correct static
|
||||
assert_equal(1, oa.AccessStaticThroughObject())
|
||||
@@ -4699,7 +4758,7 @@ def Test_private_member_access_outside_class()
|
||||
enddef
|
||||
T()
|
||||
END
|
||||
v9.CheckSourceFailure(lines, 'E1333: Cannot access private variable: _val', 2)
|
||||
v9.CheckSourceFailure(lines, 'E1333: Cannot access private variable "_val" in class "A"', 2)
|
||||
|
||||
# access a non-existing private object member variable
|
||||
lines =<< trim END
|
||||
@@ -4754,7 +4813,7 @@ def Test_private_member_access_outside_class()
|
||||
enddef
|
||||
T()
|
||||
END
|
||||
v9.CheckSourceFailure(lines, 'E1333: Cannot access private variable: _val', 1)
|
||||
v9.CheckSourceFailure(lines, 'E1333: Cannot access private variable "_val" in class "A"', 1)
|
||||
|
||||
# private static class variable
|
||||
lines =<< trim END
|
||||
@@ -4767,7 +4826,7 @@ def Test_private_member_access_outside_class()
|
||||
enddef
|
||||
T()
|
||||
END
|
||||
v9.CheckSourceFailure(lines, 'E1333: Cannot access private variable: _val', 1)
|
||||
v9.CheckSourceFailure(lines, 'E1333: Cannot access private variable "_val" in class "A"', 1)
|
||||
enddef
|
||||
|
||||
" Test for changing the member access of an interface in a implementation class
|
||||
@@ -4828,7 +4887,7 @@ def Test_modify_class_member_from_def_function()
|
||||
A.var3 = {c: 3, d: 4}
|
||||
assert_equal([3, 4], A.var2)
|
||||
assert_equal({c: 3, d: 4}, A.var3)
|
||||
assert_fails('echo A._priv_var4', 'E1333: Cannot access private variable: _priv_var4')
|
||||
assert_fails('echo A._priv_var4', 'E1333: Cannot access private variable "_priv_var4" in class "A"')
|
||||
enddef
|
||||
T()
|
||||
END
|
||||
|
||||
@@ -704,6 +704,8 @@ static char *(features[]) =
|
||||
|
||||
static int included_patches[] =
|
||||
{ /* Add new patch number below this line */
|
||||
/**/
|
||||
1982,
|
||||
/**/
|
||||
1981,
|
||||
/**/
|
||||
|
||||
@@ -508,8 +508,9 @@ typedef struct {
|
||||
|
||||
// arguments to ISN_LOCKUNLOCK
|
||||
typedef struct {
|
||||
char_u *string; // for exec_command
|
||||
int is_arg; // is lval_root a function arg
|
||||
char_u *lu_string; // for exec_command
|
||||
class_T *lu_cl_exec; // executing, null if not class/obj method
|
||||
int lu_is_arg; // is lval_root a function arg
|
||||
} lockunlock_T;
|
||||
|
||||
/*
|
||||
|
||||
@@ -2172,7 +2172,8 @@ get_member_tv(
|
||||
|
||||
if (*name == '_')
|
||||
{
|
||||
semsg(_(e_cannot_access_private_variable_str), m->ocm_name);
|
||||
semsg(_(e_cannot_access_private_variable_str), m->ocm_name,
|
||||
cl->class_name);
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
@@ -2331,7 +2332,8 @@ class_object_index(
|
||||
|
||||
if (*name == '_')
|
||||
{
|
||||
semsg(_(e_cannot_access_private_variable_str), m->ocm_name);
|
||||
semsg(_(e_cannot_access_private_variable_str), m->ocm_name,
|
||||
cl->class_name);
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
|
||||
@@ -212,11 +212,20 @@ compile_lock_unlock(
|
||||
if (p[1] != ':')
|
||||
{
|
||||
char_u *end = find_name_end(p, NULL, NULL, FNE_CHECK_START);
|
||||
// If name is is locally accessible, except for local var,
|
||||
|
||||
// The most important point is that something like
|
||||
// name[idx].member... needs to be resolved at runtime, get_lval(),
|
||||
// starting from the root "name".
|
||||
|
||||
// These checks are reminiscent of the variable_exists function.
|
||||
// But most of the matches require special handling.
|
||||
|
||||
// If bare name is is locally accessible, except for local var,
|
||||
// then put it on the stack to use with ISN_LOCKUNLOCK.
|
||||
// This could be v.memb, v[idx_key]; bare class variable,
|
||||
// function arg. The local variable on the stack, will be passed
|
||||
// to ex_lockvar() indirectly.
|
||||
// function arg. The item on the stack, will be passed
|
||||
// to ex_lockvar() indirectly and be used as the root for get_lval.
|
||||
// A bare script variable name needs no special handling.
|
||||
|
||||
char_u *name = NULL;
|
||||
int len = end - p;
|
||||
@@ -233,7 +242,7 @@ compile_lock_unlock(
|
||||
// Push the local on the stack, could be "this".
|
||||
name = p;
|
||||
#ifdef LOG_LOCKVAR
|
||||
ch_log(NULL, "LKVAR: compile... lookup_local: name %s", name);
|
||||
ch_log(NULL, "LKVAR: ... lookup_local: name %s", name);
|
||||
#endif
|
||||
}
|
||||
if (name == NULL)
|
||||
@@ -247,7 +256,7 @@ compile_lock_unlock(
|
||||
name = cl->class_name;
|
||||
len = STRLEN(name);
|
||||
#ifdef LOG_LOCKVAR
|
||||
ch_log(NULL, "LKVAR: compile... cctx_class_member: name %s",
|
||||
ch_log(NULL, "LKVAR: ... cctx_class_member: name %s",
|
||||
name);
|
||||
#endif
|
||||
}
|
||||
@@ -255,23 +264,33 @@ compile_lock_unlock(
|
||||
}
|
||||
if (name == NULL)
|
||||
{
|
||||
int idx;
|
||||
type_T *type;
|
||||
// Can lockvar any function arg.
|
||||
// TODO: test arg[idx]/arg.member
|
||||
if (arg_exists(p, len, &idx, &type, NULL, cctx) == OK)
|
||||
if (arg_exists(p, len, NULL, NULL, NULL, cctx) == OK)
|
||||
{
|
||||
name = p;
|
||||
is_arg = TRUE;
|
||||
#ifdef LOG_LOCKVAR
|
||||
ch_log(NULL, "LKVAR: compile... arg_exists: name %s", name);
|
||||
ch_log(NULL, "LKVAR: ... arg_exists: name %s", name);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
if (name == NULL)
|
||||
{
|
||||
// No special handling for a bare script variable; but
|
||||
// if followed by '[' or '.', it's a root for get_lval().
|
||||
if (script_var_exists(p, len, cctx, NULL) == OK
|
||||
&& (*end == '.' || *end == '['))
|
||||
{
|
||||
name = p;
|
||||
#ifdef LOG_LOCKVAR
|
||||
ch_log(NULL, "LKVAR: ... script_var_exists: name %s", name);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
if (name != NULL)
|
||||
{
|
||||
#ifdef LOG_LOCKVAR
|
||||
ch_log(NULL, "LKVAR: compile... INS_LOCKUNLOCK %s", name);
|
||||
ch_log(NULL, "LKVAR: ... INS_LOCKUNLOCK %s", name);
|
||||
#endif
|
||||
if (compile_load(&name, name + len, cctx, FALSE, FALSE) == FAIL)
|
||||
return FAIL;
|
||||
@@ -292,7 +311,7 @@ compile_lock_unlock(
|
||||
else
|
||||
vim_snprintf((char *)buf, len, "%s %d %s", cmd, deep, p);
|
||||
#ifdef LOG_LOCKVAR
|
||||
ch_log(NULL, "LKVAR: compile... buf %s", buf);
|
||||
ch_log(NULL, "LKVAR: ... buf %s", buf);
|
||||
#endif
|
||||
if (isn == ISN_LOCKUNLOCK)
|
||||
ret = generate_LOCKUNLOCK(cctx, buf, is_arg);
|
||||
|
||||
@@ -2180,7 +2180,8 @@ execute_storeindex(isn_T *iptr, ectx_T *ectx)
|
||||
{
|
||||
if (*member == '_')
|
||||
{
|
||||
semsg(_(e_cannot_access_private_variable_str), m->ocm_name);
|
||||
semsg(_(e_cannot_access_private_variable_str),
|
||||
m->ocm_name, cl->class_name);
|
||||
status = FAIL;
|
||||
}
|
||||
|
||||
@@ -4178,9 +4179,7 @@ exec_instructions(ectx_T *ectx)
|
||||
|
||||
case ISN_LOCKUNLOCK:
|
||||
{
|
||||
// TODO: could put lval_root info in struct
|
||||
typval_T *lval_root_save = lval_root;
|
||||
int lval_root_is_arg_save = lval_root_is_arg;
|
||||
lval_root_T *lval_root_save = lval_root;
|
||||
int res;
|
||||
#ifdef LOG_LOCKVAR
|
||||
ch_log(NULL, "LKVAR: execute INS_LOCKUNLOCK isn_arg %s",
|
||||
@@ -4190,12 +4189,14 @@ exec_instructions(ectx_T *ectx)
|
||||
// Stack has the local variable, argument the whole :lock
|
||||
// or :unlock command, like ISN_EXEC.
|
||||
--ectx->ec_stack.ga_len;
|
||||
lval_root = STACK_TV_BOT(0);
|
||||
lval_root_is_arg = iptr->isn_arg.lockunlock.is_arg;
|
||||
res = exec_command(iptr, iptr->isn_arg.lockunlock.string);
|
||||
clear_tv(lval_root);
|
||||
lval_root_T root = { STACK_TV_BOT(0),
|
||||
iptr->isn_arg.lockunlock.lu_cl_exec,
|
||||
iptr->isn_arg.lockunlock.lu_is_arg };
|
||||
lval_root = &root;
|
||||
res = exec_command(iptr,
|
||||
iptr->isn_arg.lockunlock.lu_string);
|
||||
clear_tv(root.lr_tv);
|
||||
lval_root = lval_root_save;
|
||||
lval_root_is_arg = lval_root_is_arg_save;
|
||||
if (res == FAIL)
|
||||
goto on_error;
|
||||
}
|
||||
|
||||
@@ -407,7 +407,8 @@ compile_class_object_index(cctx_T *cctx, char_u **arg, type_T *type)
|
||||
{
|
||||
if (*name == '_' && !inside_class(cctx, cl))
|
||||
{
|
||||
semsg(_(e_cannot_access_private_variable_str), m->ocm_name);
|
||||
semsg(_(e_cannot_access_private_variable_str), m->ocm_name,
|
||||
cl->class_name);
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
@@ -442,7 +443,8 @@ compile_class_object_index(cctx_T *cctx, char_u **arg, type_T *type)
|
||||
// it is defined.
|
||||
if (*name == '_' && cctx->ctx_ufunc->uf_class != cl)
|
||||
{
|
||||
semsg(_(e_cannot_access_private_variable_str), m->ocm_name);
|
||||
semsg(_(e_cannot_access_private_variable_str), m->ocm_name,
|
||||
cl->class_name);
|
||||
return FAIL;
|
||||
}
|
||||
|
||||
|
||||
@@ -2170,7 +2170,11 @@ generate_PUT(cctx_T *cctx, int regname, linenr_T lnum)
|
||||
}
|
||||
|
||||
/*
|
||||
* Generate an EXEC instruction that takes a string argument.
|
||||
* Generate a LOCKUNLOCK instruction.The root item, where the indexing starts
|
||||
* to find the variable, is on the stack. The instr takes
|
||||
* - the string to parse, "root.b[idx1][idx2].d.val", to find the variable
|
||||
* - the class, if any, in which the string executes.
|
||||
* - if the root item is a function argument
|
||||
* A copy is made of "line".
|
||||
*/
|
||||
int
|
||||
@@ -2181,8 +2185,12 @@ generate_LOCKUNLOCK(cctx_T *cctx, char_u *line, int is_arg)
|
||||
RETURN_OK_IF_SKIP(cctx);
|
||||
if ((isn = generate_instr(cctx, ISN_LOCKUNLOCK)) == NULL)
|
||||
return FAIL;
|
||||
isn->isn_arg.lockunlock.string = vim_strsave(line);
|
||||
isn->isn_arg.lockunlock.is_arg = is_arg;
|
||||
class_T *cl = cctx->ctx_ufunc != NULL ? cctx->ctx_ufunc->uf_class : NULL;
|
||||
isn->isn_arg.lockunlock.lu_string = vim_strsave(line);
|
||||
isn->isn_arg.lockunlock.lu_cl_exec = cl;
|
||||
if (cl != NULL)
|
||||
++cl->class_refcount;
|
||||
isn->isn_arg.lockunlock.lu_is_arg = is_arg;
|
||||
return OK;
|
||||
}
|
||||
|
||||
@@ -2490,7 +2498,6 @@ delete_instr(isn_T *isn)
|
||||
case ISN_LOADOPT:
|
||||
case ISN_LOADT:
|
||||
case ISN_LOADW:
|
||||
case ISN_LOCKUNLOCK:
|
||||
case ISN_PUSHEXC:
|
||||
case ISN_PUSHFUNC:
|
||||
case ISN_PUSHS:
|
||||
@@ -2505,6 +2512,11 @@ delete_instr(isn_T *isn)
|
||||
vim_free(isn->isn_arg.string);
|
||||
break;
|
||||
|
||||
case ISN_LOCKUNLOCK:
|
||||
class_unref(isn->isn_arg.lockunlock.lu_cl_exec);
|
||||
vim_free(isn->isn_arg.lockunlock.lu_string);
|
||||
break;
|
||||
|
||||
case ISN_SUBSTITUTE:
|
||||
{
|
||||
int idx;
|
||||
|
||||
Reference in New Issue
Block a user