Compare commits

...

7 Commits

Author SHA1 Message Date
Bram Moolenaar
f2a8bafa4b patch 8.2.3440: recover test fails if there is an old swap file
Problem:    Recover test fails if there is an old swap file.
Solution:   Delete old swap files.
2021-09-14 22:58:23 +02:00
Christian Brabandt
78eb9cce91 patch 8.2.3439: deleted lines go to wrong yank register
Problem:    Deleted lines go to wrong yank register.
Solution:   Reset y_append when not calling get_yank_register(). (Christian
            Brabandt, closes #8872)
2021-09-14 18:55:51 +02:00
Yegappan Lakshmanan
5dfe467432 patch 8.2.3438: cannot manipulate blobs
Problem:    Cannot manipulate blobs.
Solution:   Add blob2list() and list2blob(). (Yegappan Lakshmanan,
            closes #8868)
2021-09-14 17:54:30 +02:00
Mike Williams
f5785cf059 patch 8.2.3437: compiler warnings for 32/64 bit usage
Problem:    Compiler warnings for 32/64 bit usage.
Solution:   Add type casts. (Mike Williams, closes #8870)
2021-09-13 22:17:38 +02:00
Bram Moolenaar
a29856fcdc patch 8.2.3436: check for optional bool type has confusing return type
Problem:    Check for optional bool type has confusing return type.
Solution:   Explicitly return OK.
2021-09-13 21:36:27 +02:00
Bram Moolenaar
b1b6f4de2b patch 8.2.3435: Vim9: dict is not passed to dict function
Problem:    Vim9: dict is not passed to dict function.
Solution:   Keep the dict used until a function call.
2021-09-13 18:25:54 +02:00
Bram Moolenaar
28e591dd50 patch 8.2.3434: function prototype for trigger_modechanged() is incomplete
Problem:    Function prototype for trigger_modechanged() is incomplete.
Solution:   Add "void".
2021-09-12 21:00:14 +02:00
25 changed files with 501 additions and 36 deletions

View File

@@ -2469,6 +2469,7 @@ atan2({expr1}, {expr2}) Float arc tangent of {expr1} / {expr2}
balloon_gettext() String current text in the balloon
balloon_show({expr}) none show {expr} inside the balloon
balloon_split({msg}) List split {msg} as used for a balloon
blob2list({blob}) List convert {blob} into a list of numbers
browse({save}, {title}, {initdir}, {default})
String put up a file requester
browsedir({title}, {initdir}) String put up a directory requester
@@ -2721,7 +2722,8 @@ libcallnr({lib}, {func}, {arg}) Number idem, but return a Number
line({expr} [, {winid}]) Number line nr of cursor, last line or mark
line2byte({lnum}) Number byte count of line {lnum}
lispindent({lnum}) Number Lisp indent for line {lnum}
list2str({list} [, {utf8}]) String turn numbers in {list} into a String
list2blob({list}) Blob turn {list} of numbers into a Blob
list2str({list} [, {utf8}]) String turn {list} of numbers into a String
listener_add({callback} [, {buf}])
Number add a callback to listen to changes
listener_flush([{buf}]) none invoke listener callbacks
@@ -3355,6 +3357,17 @@ balloon_split({msg}) *balloon_split()*
< {only available when compiled with the |+balloon_eval_term|
feature}
blob2list({blob}) *blob2list()*
Return a List containing the number value of each byte in Blob
{blob}. Examples: >
blob2list(0z0102.0304) returns [1, 2, 3, 4]
blob2list(0z) returns []
< Returns an empty List on error. |list2blob()| does the
opposite.
Can also be used as a |method|: >
GetBlob()->blob2list()
*browse()*
browse({save}, {title}, {initdir}, {default})
Put up a file requester. This only works when "has("browse")"
@@ -7208,6 +7221,19 @@ lispindent({lnum}) *lispindent()*
Can also be used as a |method|: >
GetLnum()->lispindent()
list2blob({list}) *list2blob()*
Return a Blob concatenating all the number values in {list}.
Examples: >
list2blob([1, 2, 3, 4]) returns 0z01020304
list2blob([]) returns 0z
< Returns an empty Blob on error. If one of the numbers is
negative or more than 255 error *E1239* is given.
|blob2list()| does the opposite.
Can also be used as a |method|: >
GetList()->list2blob()
list2str({list} [, {utf8}]) *list2str()*
Convert each number in {list} to a character string can
concatenate them all. Examples: >

View File

@@ -723,6 +723,10 @@ Floating point computation: *float-functions*
isinf() check for infinity
isnan() check for not a number
Blob manipulation: *blob-functions*
blob2list() get a list of numbers from a blob
list2blob() get a blob from a list of numbers
Other computation: *bitwise-function*
and() bitwise AND
invert() bitwise invert
@@ -1449,6 +1453,8 @@ is a List with arguments.
Function references are most useful in combination with a Dictionary, as is
explained in the next section.
More information about defining your own functions here: |user-functions|.
==============================================================================
*41.8* Lists and Dictionaries

View File

@@ -483,4 +483,65 @@ blob_remove(typval_T *argvars, typval_T *rettv, char_u *arg_errmsg)
}
}
/*
* blob2list() function
*/
void
f_blob2list(typval_T *argvars, typval_T *rettv)
{
blob_T *blob;
list_T *l;
int i;
if (rettv_list_alloc(rettv) == FAIL)
return;
if (check_for_blob_arg(argvars, 0) == FAIL)
return;
blob = argvars->vval.v_blob;
l = rettv->vval.v_list;
for (i = 0; i < blob_len(blob); i++)
list_append_number(l, blob_get(blob, i));
}
/*
* list2blob() function
*/
void
f_list2blob(typval_T *argvars, typval_T *rettv)
{
list_T *l;
listitem_T *li;
blob_T *blob;
if (rettv_blob_alloc(rettv) == FAIL)
return;
blob = rettv->vval.v_blob;
if (check_for_list_arg(argvars, 0) == FAIL)
return;
l = argvars->vval.v_list;
if (l == NULL)
return;
FOR_ALL_LIST_ITEMS(l, li)
{
int error;
varnumber_T n;
error = FALSE;
n = tv_get_number_chk(&li->li_tv, &error);
if (error == TRUE || n < 0 || n > 255)
{
if (!error)
semsg(_(e_invalid_value_for_blob_nr), n);
ga_clear(&blob->bv_ga);
return;
}
ga_append(&blob->bv_ga, n);
}
}
#endif // defined(FEAT_EVAL)

View File

@@ -660,3 +660,7 @@ EXTERN char e_cannot_use_str_itself_it_is_imported_with_star[]
INIT(= N_("E1236: Cannot use %s itself, it is imported with '*'"));
EXTERN char e_no_such_user_defined_command_in_current_buffer_str[]
INIT(= N_("E1237: No such user-defined command in current buffer: %s"));
EXTERN char e_blob_required_for_argument_nr[]
INIT(= N_("E1238: Blob required for argument %d"));
EXTERN char e_invalid_value_for_blob_nr[]
INIT(= N_("E1239: Invalid value for blob: %d"));

View File

@@ -288,6 +288,15 @@ arg_string(type_T *type, argcontext_T *context)
return check_arg_type(&t_string, type, context);
}
/*
* Check "type" is a blob
*/
static int
arg_blob(type_T *type, argcontext_T *context)
{
return check_arg_type(&t_blob, type, context);
}
/*
* Check "type" is a bool or number 0 or 1.
*/
@@ -680,6 +689,7 @@ arg_cursor1(type_T *type, argcontext_T *context)
/*
* Lists of functions that check the argument types of a builtin function.
*/
static argcheck_T arg1_blob[] = {arg_blob};
static argcheck_T arg1_bool[] = {arg_bool};
static argcheck_T arg1_buffer[] = {arg_buffer};
static argcheck_T arg1_buffer_or_dict_any[] = {arg_buffer_or_dict_any};
@@ -1169,6 +1179,8 @@ static funcentry_T global_functions[] =
NULL
#endif
},
{"blob2list", 1, 1, FEARG_1, arg1_blob,
ret_list_number, f_blob2list},
{"browse", 4, 4, 0, arg4_browse,
ret_string, f_browse},
{"browsedir", 2, 2, 0, arg2_string,
@@ -1589,6 +1601,8 @@ static funcentry_T global_functions[] =
ret_number, f_line2byte},
{"lispindent", 1, 1, FEARG_1, arg1_lnum,
ret_number, f_lispindent},
{"list2blob", 1, 1, FEARG_1, arg1_list_number,
ret_blob, f_list2blob},
{"list2str", 1, 2, FEARG_1, arg2_list_number_bool,
ret_string, f_list2str},
{"listener_add", 1, 2, FEARG_2, arg2_any_buffer,

View File

@@ -695,6 +695,8 @@ op_delete(oparg_T *oap)
if (op_yank(oap, TRUE, FALSE) == OK) // yank without message
did_yank = TRUE;
}
else
reset_y_append(); // not appending to unnamed register
/*
* Put deleted text into register 1 and shift number registers if the

View File

@@ -19,4 +19,6 @@ int check_blob_index(long bloblen, varnumber_T n1, int quiet);
int check_blob_range(long bloblen, varnumber_T n1, varnumber_T n2, int quiet);
int blob_set_range(blob_T *dest, long n1, long n2, typval_T *src);
void blob_remove(typval_T *argvars, typval_T *rettv, char_u *arg_errmsg);
void f_blob2list(typval_T *argvars, typval_T *rettv);
void f_list2blob(typval_T *argvars, typval_T *rettv);
/* vim: set ft=c : */

View File

@@ -47,5 +47,5 @@ int goto_im(void);
char_u *get_isolated_shell_name(void);
int path_is_url(char_u *p);
int path_with_url(char_u *fname);
void trigger_modechanged();
void trigger_modechanged(void);
/* vim: set ft=c : */

View File

@@ -5,6 +5,7 @@ yankreg_T *get_y_current(void);
yankreg_T *get_y_previous(void);
void set_y_current(yankreg_T *yreg);
void set_y_previous(yankreg_T *yreg);
void reset_y_append(void);
int get_expr_register(void);
void set_expr_line(char_u *new_line, exarg_T *eap);
char_u *get_expr_line(void);

View File

@@ -17,6 +17,7 @@ int check_for_opt_number_arg(typval_T *args, int idx);
int check_for_float_or_nr_arg(typval_T *args, int idx);
int check_for_bool_arg(typval_T *args, int idx);
int check_for_opt_bool_arg(typval_T *args, int idx);
int check_for_blob_arg(typval_T *args, int idx);
int check_for_list_arg(typval_T *args, int idx);
int check_for_opt_list_arg(typval_T *args, int idx);
int check_for_dict_arg(typval_T *args, int idx);

View File

@@ -74,6 +74,13 @@ set_y_previous(yankreg_T *yreg)
y_previous = yreg;
}
void
reset_y_append(void)
{
y_append = FALSE;
}
#if defined(FEAT_EVAL) || defined(PROTO)
/*
* Keep the last expression line here, for repeating.

View File

@@ -4855,7 +4855,7 @@ set_chars_option(win_T *wp, char_u **varp)
{
lcs_chars.tab1 = NUL;
lcs_chars.tab3 = NUL;
if (multispace_len)
if (multispace_len > 0)
{
lcs_chars.multispace = ALLOC_MULT(int, multispace_len + 1);
lcs_chars.multispace[multispace_len] = NUL;
@@ -4904,7 +4904,7 @@ set_chars_option(win_T *wp, char_u **varp)
if (*s == ',' || *s == NUL)
{
if (round)
if (round > 0)
{
if (tab[i].cp == &lcs_chars.tab2)
{
@@ -4924,7 +4924,7 @@ set_chars_option(win_T *wp, char_u **varp)
if (i == entries)
{
len = STRLEN("multispace");
len = (int)STRLEN("multispace");
if ((varp == &p_lcs || varp == &wp->w_p_lcs)
&& STRNCMP(p, "multispace", len) == 0
&& p[len] == ':'
@@ -4951,6 +4951,7 @@ set_chars_option(win_T *wp, char_u **varp)
else
{
int multispace_pos = 0;
while (*s != NUL && *s != ',')
{
c1 = mb_ptr2char_adv(&s);

View File

@@ -638,4 +638,43 @@ func Test_blob_sort()
call CheckLegacyAndVim9Failure(['call sort([11, 0z11], "N")'], 'E974:')
endfunc
" Tests for the blob2list() function
func Test_blob2list()
call assert_fails('let v = blob2list(10)', 'E1238: Blob required for argument 1')
eval 0zFFFF->blob2list()->assert_equal([255, 255])
let tests = [[0z0102, [1, 2]],
\ [0z00, [0]],
\ [0z, []],
\ [0z00000000, [0, 0, 0, 0]],
\ [0zAABB.CCDD, [170, 187, 204, 221]]]
for t in tests
call assert_equal(t[0]->blob2list(), t[1])
endfor
exe 'let v = 0z' .. repeat('000102030405060708090A0B0C0D0E0F', 64)
call assert_equal(1024, blob2list(v)->len())
call assert_equal([4, 8, 15], [v[100], v[1000], v[1023]])
call assert_equal([], blob2list(test_null_blob()))
endfunc
" Tests for the list2blob() function
func Test_list2blob()
call assert_fails('let b = list2blob(0z10)', 'E1211: List required for argument 1')
let tests = [[[1, 2], 0z0102],
\ [[0], 0z00],
\ [[], 0z],
\ [[0, 0, 0, 0], 0z00000000],
\ [[170, 187, 204, 221], 0zAABB.CCDD],
\ ]
for t in tests
call assert_equal(t[0]->list2blob(), t[1])
endfor
call assert_fails('let b = list2blob([1, []])', 'E745:')
call assert_fails('let b = list2blob([-1])', 'E1239:')
call assert_fails('let b = list2blob([256])', 'E1239:')
let b = range(16)->repeat(64)->list2blob()
call assert_equal(1024, b->len())
call assert_equal([4, 8, 15], [b[100], b[1000], b[1023]])
call assert_equal(0z, list2blob(test_null_list()))
endfunc
" vim: shiftwidth=2 sts=2 expandtab

View File

@@ -170,6 +170,12 @@ func Test_recover_empty_swap_file()
call assert_match('Unable to read block 0 from .Xfile1.swp', msg)
call assert_equal('Xfile1', @%)
bw!
" make sure there are no old swap files laying around
for f in glob('.sw?', 0, 1)
call delete(f)
endfor
" :recover from an empty buffer
call assert_fails('recover', 'E305:')
call delete('.Xfile1.swp')

View File

@@ -728,4 +728,56 @@ func Test_record_in_insert_mode()
bwipe!
endfunc
" Make sure that y_append is correctly reset
" and the previous register is working as expected
func Test_register_y_append_reset()
new
call setline(1, ['1',
\ '2 ----------------------------------------------------',
\ '3',
\ '4',
\ '5 ----------------------------------------------------',
\ '6',
\ '7',
\ '8 ----------------------------------------------------',
\ '9',
\ '10 aaaaaaa 4.',
\ '11 Game Dbl-Figures Leaders:',
\ '12 Player Pts FG% 3P% FT% RB AS BL ST TO PF EFF',
\ '13 bbbbbbbbb 12 (50 /0 /67 )/ 7/ 3/ 0/ 2/ 3/ 4/+15',
\ '14 cccccc 12 (57 /67 /100)/ 2/ 1/ 1/ 0/ 1/ 3/+12',
\ '15 ddddddd 10 (63 /0 /0 )/ 1/ 3/ 0/ 3/ 5/ 3/ +9',
\ '16 4 5-15 0-3 2-2 5-12 1-1 3-4 33.3 0.0 100 41.7 100 75 12 14',
\ '17 F 23-55 2-10 9-11 23-52 3-13 26-29 41.8 20 81.8 44.2 23.1 89.7 57 75',
\ '18 4 3 6 3 2 3 3 4 3 3 7 3 1 4 6 -1 -1 +2 -1 -2',
\ '19 F 13 19 5 10 4 17 22 9 14 32 13 4 20 17 -1 -13 -4 -3 -3 +5'])
11
exe "norm! \"a5dd"
norm! j
exe "norm! \"bY"
norm! 2j
exe "norm! \"BY"
norm! 4k
norm! 5dd
norm! 3k
" The next put should put the content of the unnamed register, not of
" register b!
norm! p
call assert_equal(['1',
\ '2 ----------------------------------------------------',
\ '3',
\ '4',
\ '5 ----------------------------------------------------',
\ '6',
\ '10 aaaaaaa 4.',
\ '16 4 5-15 0-3 2-2 5-12 1-1 3-4 33.3 0.0 100 41.7 100 75 12 14',
\ '17 F 23-55 2-10 9-11 23-52 3-13 26-29 41.8 20 81.8 44.2 23.1 89.7 57 75',
\ '18 4 3 6 3 2 3 3 4 3 3 7 3 1 4 6 -1 -1 +2 -1 -2',
\ '19 F 13 19 5 10 4 17 22 9 14 32 13 4 20 17 -1 -13 -4 -3 -3 +5',
\ '7',
\ '8 ----------------------------------------------------',
\ '9'], getline(1,'$'))
bwipe!
endfunc
" vim: shiftwidth=2 sts=2 expandtab

View File

@@ -287,6 +287,10 @@ def Test_balloon_split()
assert_fails('balloon_split(true)', 'E1174:')
enddef
def Test_blob2list()
CheckDefAndScriptFailure2(['blob2list(10)'], 'E1013: Argument 1: type mismatch, expected blob but got number', 'E1238: Blob required for argument 1')
enddef
def Test_browse()
CheckFeature browse
@@ -572,6 +576,7 @@ def Test_char2nr()
assert_equal(97, char2nr('a', 0))
assert_equal(97, char2nr('a', true))
assert_equal(97, char2nr('a', false))
char2nr('')->assert_equal(0)
enddef
def Test_charclass()
@@ -786,6 +791,8 @@ def Test_escape()
CheckDefAndScriptFailure2(['escape(true, false)'], 'E1013: Argument 1: type mismatch, expected string but got bool', 'E1174: String required for argument 1')
CheckDefAndScriptFailure2(['escape("a", 10)'], 'E1013: Argument 2: type mismatch, expected string but got number', 'E1174: String required for argument 2')
assert_equal('a\:b', escape("a:b", ":"))
escape('abc', '')->assert_equal('abc')
escape('', ':')->assert_equal('')
enddef
def Test_eval()
@@ -1921,6 +1928,11 @@ def Test_lispindent()
assert_equal(0, lispindent(1))
enddef
def Test_list2blob()
CheckDefAndScriptFailure2(['list2blob(10)'], 'E1013: Argument 1: type mismatch, expected list<number> but got number', 'E1211: List required for argument 1')
CheckDefFailure(['list2blob([0z10, 0z02])'], 'E1013: Argument 1: type mismatch, expected list<number> but got list<blob>')
enddef
def Test_list2str_str2list_utf8()
var s = "\u3042\u3044"
var l = [0x3042, 0x3044]

View File

@@ -412,7 +412,8 @@ def Test_disassemble_store_index()
'\d PUSHNR 0\_s*' ..
'\d LOAD $0\_s*' ..
'\d MEMBER dd\_s*' ..
'\d STOREINDEX any\_s*' ..
'\d\+ USEDICT\_s*' ..
'\d\+ STOREINDEX any\_s*' ..
'\d\+ RETURN void',
res)
enddef
@@ -1625,11 +1626,13 @@ def Test_disassemble_dict_member()
'var res = d.item\_s*' ..
'\d\+ LOAD $0\_s*' ..
'\d\+ MEMBER item\_s*' ..
'\d\+ USEDICT\_s*' ..
'\d\+ STORE $1\_s*' ..
'res = d\["item"\]\_s*' ..
'\d\+ LOAD $0\_s*' ..
'\d\+ PUSHS "item"\_s*' ..
'\d\+ MEMBER\_s*' ..
'\d\+ USEDICT\_s*' ..
'\d\+ STORE $1\_s*',
instr)
assert_equal(1, DictMember())
@@ -2302,6 +2305,35 @@ def Test_debug_elseif()
res)
enddef
func Legacy() dict
echo 'legacy'
endfunc
def s:UseMember()
var d = {func: Legacy}
var v = d.func()
enddef
def Test_disassemble_dict_stack()
var res = execute('disass s:UseMember')
assert_match('<SNR>\d*_UseMember\_s*' ..
'var d = {func: Legacy}\_s*' ..
'\d PUSHS "func"\_s*' ..
'\d PUSHFUNC "Legacy"\_s*' ..
'\d NEWDICT size 1\_s*' ..
'\d STORE $0\_s*' ..
'var v = d.func()\_s*' ..
'\d LOAD $0\_s*' ..
'\d MEMBER func\_s*' ..
'\d PCALL top (argc 0)\_s*' ..
'\d PCALL end\_s*' ..
'\d CLEARDICT\_s*' ..
'\d\+ STORE $1\_s*' ..
'\d\+ RETURN void*',
res)
enddef
def s:EchoMessages()
echohl ErrorMsg | echom v:exception | echohl NONE
enddef
@@ -2363,4 +2395,5 @@ def Test_disassemble_after_reload()
enddef
" vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker

View File

@@ -2557,6 +2557,37 @@ def Test_legacy_errors()
endfor
enddef
def Test_call_legacy_with_dict()
var lines =<< trim END
vim9script
func Legacy() dict
let g:result = self.value
endfunc
def TestDirect()
var d = {value: 'yes', func: Legacy}
d.func()
enddef
TestDirect()
assert_equal('yes', g:result)
unlet g:result
def TestIndirect()
var d = {value: 'foo', func: Legacy}
var Fi = d.func
Fi()
enddef
TestIndirect()
assert_equal('foo', g:result)
unlet g:result
var d = {value: 'bar', func: Legacy}
d.func()
assert_equal('bar', g:result)
unlet g:result
END
CheckScriptSuccess(lines)
enddef
def DoFilterThis(a: string): list<string>
# closure nested inside another closure using argument
var Filter = (l) => filter(l, (_, v) => stridx(v, a) == 0)

View File

@@ -459,13 +459,32 @@ check_for_bool_arg(typval_T *args, int idx)
}
/*
* Check for an optional bool argument at 'idx'
* Check for an optional bool argument at 'idx'.
* Return FAIL if the type is wrong.
*/
int
check_for_opt_bool_arg(typval_T *args, int idx)
{
return (args[idx].v_type == VAR_UNKNOWN
|| check_for_bool_arg(args, idx) != FAIL);
if (args[idx].v_type == VAR_UNKNOWN)
return OK;
return check_for_bool_arg(args, idx);
}
/*
* Give an error and return FAIL unless "args[idx]" is a blob.
*/
int
check_for_blob_arg(typval_T *args, int idx)
{
if (args[idx].v_type != VAR_BLOB)
{
if (idx >= 0)
semsg(_(e_blob_required_for_argument_nr), idx + 1);
else
emsg(_(e_blob_required));
return FAIL;
}
return OK;
}
/*

View File

@@ -755,6 +755,20 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
3440,
/**/
3439,
/**/
3438,
/**/
3437,
/**/
3436,
/**/
3435,
/**/
3434,
/**/
3433,
/**/

View File

@@ -162,6 +162,9 @@ typedef enum {
ISN_CHECKLEN, // check list length is isn_arg.checklen.cl_min_len
ISN_SETTYPE, // set dict type to isn_arg.type.ct_type
ISN_CLEARDICT, // clear dict saved by ISN_MEMBER/ISN_STRINGMEMBER
ISN_USEDICT, // use or clear dict saved by ISN_MEMBER/ISN_STRINGMEMBER
ISN_PUT, // ":put", uses isn_arg.put
ISN_CMDMOD, // set cmdmod

View File

@@ -2878,9 +2878,10 @@ clear_ppconst(ppconst_T *ppconst)
/*
* Compile getting a member from a list/dict/string/blob. Stack has the
* indexable value and the index or the two indexes of a slice.
* "keeping_dict" is used for dict[func](arg) to pass dict to func.
*/
static int
compile_member(int is_slice, cctx_T *cctx)
compile_member(int is_slice, int *keeping_dict, cctx_T *cctx)
{
type_T **typep;
garray_T *stack = &cctx->ctx_type_stack;
@@ -2935,6 +2936,8 @@ compile_member(int is_slice, cctx_T *cctx)
return FAIL;
if (generate_instr_drop(cctx, ISN_MEMBER, 1) == FAIL)
return FAIL;
if (keeping_dict != NULL)
*keeping_dict = TRUE;
}
else if (vartype == VAR_STRING)
{
@@ -4314,6 +4317,7 @@ compile_subscript(
ppconst_T *ppconst)
{
char_u *name_start = *end_leader;
int keeping_dict = FALSE;
for (;;)
{
@@ -4360,6 +4364,12 @@ compile_subscript(
return FAIL;
if (generate_PCALL(cctx, argcount, name_start, type, TRUE) == FAIL)
return FAIL;
if (keeping_dict)
{
keeping_dict = FALSE;
if (generate_instr(cctx, ISN_CLEARDICT) == NULL)
return FAIL;
}
}
else if (*p == '-' && p[1] == '>')
{
@@ -4470,6 +4480,12 @@ compile_subscript(
if (compile_call(arg, p - *arg, cctx, ppconst, 1) == FAIL)
return FAIL;
}
if (keeping_dict)
{
keeping_dict = FALSE;
if (generate_instr(cctx, ISN_CLEARDICT) == NULL)
return FAIL;
}
}
else if (**arg == '[')
{
@@ -4537,7 +4553,13 @@ compile_subscript(
}
*arg = *arg + 1;
if (compile_member(is_slice, cctx) == FAIL)
if (keeping_dict)
{
keeping_dict = FALSE;
if (generate_instr(cctx, ISN_CLEARDICT) == NULL)
return FAIL;
}
if (compile_member(is_slice, &keeping_dict, cctx) == FAIL)
return FAIL;
}
else if (*p == '.' && p[1] != '.')
@@ -4562,18 +4584,21 @@ compile_subscript(
semsg(_(e_syntax_error_at_str), *arg);
return FAIL;
}
if (keeping_dict && generate_instr(cctx, ISN_CLEARDICT) == NULL)
return FAIL;
if (generate_STRINGMEMBER(cctx, *arg, p - *arg) == FAIL)
return FAIL;
keeping_dict = TRUE;
*arg = p;
}
else
break;
}
// TODO - see handle_subscript():
// Turn "dict.Func" into a partial for "Func" bound to "dict".
// Don't do this when "Func" is already a partial that was bound
// explicitly (pt_auto is FALSE).
// This needs to be done at runtime to be able to check the type.
if (keeping_dict && generate_instr(cctx, ISN_USEDICT) == NULL)
return FAIL;
return OK;
}
@@ -6661,7 +6686,7 @@ compile_load_lhs_with_index(lhs_T *lhs, char_u *var_start, cctx_T *cctx)
}
// Get the member.
if (compile_member(FALSE, cctx) == FAIL)
if (compile_member(FALSE, NULL, cctx) == FAIL)
return FAIL;
}
return OK;
@@ -10406,6 +10431,7 @@ delete_instr(isn_T *isn)
case ISN_CEXPR_AUCMD:
case ISN_CHECKLEN:
case ISN_CHECKNR:
case ISN_CLEARDICT:
case ISN_CMDMOD_REV:
case ISN_COMPAREANY:
case ISN_COMPAREBLOB:
@@ -10482,6 +10508,7 @@ delete_instr(isn_T *isn)
case ISN_UNLETINDEX:
case ISN_UNLETRANGE:
case ISN_UNPACK:
case ISN_USEDICT:
// nothing allocated
break;
}

View File

@@ -165,6 +165,75 @@ update_has_breakpoint(ufunc_T *ufunc)
}
}
static garray_T dict_stack = GA_EMPTY;
/*
* Put a value on the dict stack. This consumes "tv".
*/
static int
dict_stack_save(typval_T *tv)
{
if (dict_stack.ga_growsize == 0)
ga_init2(&dict_stack, (int)sizeof(typval_T), 10);
if (ga_grow(&dict_stack, 1) == FAIL)
return FAIL;
((typval_T *)dict_stack.ga_data)[dict_stack.ga_len] = *tv;
++dict_stack.ga_len;
return OK;
}
/*
* Get the typval at top of the dict stack.
*/
static typval_T *
dict_stack_get_tv(void)
{
if (dict_stack.ga_len == 0)
return NULL;
return ((typval_T *)dict_stack.ga_data) + dict_stack.ga_len - 1;
}
/*
* Get the dict at top of the dict stack.
*/
static dict_T *
dict_stack_get_dict(void)
{
typval_T *tv;
if (dict_stack.ga_len == 0)
return NULL;
tv = ((typval_T *)dict_stack.ga_data) + dict_stack.ga_len - 1;
if (tv->v_type == VAR_DICT)
return tv->vval.v_dict;
return NULL;
}
/*
* Drop an item from the dict stack.
*/
static void
dict_stack_drop(void)
{
if (dict_stack.ga_len == 0)
{
iemsg("Dict stack underflow");
return;
}
--dict_stack.ga_len;
clear_tv(((typval_T *)dict_stack.ga_data) + dict_stack.ga_len);
}
/*
* Drop items from the dict stack until the length is equal to "len".
*/
static void
dict_stack_clear(int len)
{
while (dict_stack.ga_len > len)
dict_stack_drop();
}
/*
* Call compiled function "cdf_idx" from compiled code.
* This adds a stack frame and sets the instruction pointer to the start of the
@@ -765,7 +834,8 @@ call_ufunc(
partial_T *pt,
int argcount,
ectx_T *ectx,
isn_T *iptr)
isn_T *iptr,
dict_T *selfdict)
{
typval_T argvars[MAX_FUNC_ARGS];
funcexe_T funcexe;
@@ -807,11 +877,12 @@ call_ufunc(
return FAIL;
CLEAR_FIELD(funcexe);
funcexe.evaluate = TRUE;
funcexe.selfdict = selfdict != NULL ? selfdict : dict_stack_get_dict();
// Call the user function. Result goes in last position on the stack.
// TODO: add selfdict if there is one
error = call_user_func_check(ufunc, argcount, argvars,
STACK_TV_BOT(-1), &funcexe, NULL);
STACK_TV_BOT(-1), &funcexe, funcexe.selfdict);
// Clear the arguments.
for (idx = 0; idx < argcount; ++idx)
@@ -864,7 +935,8 @@ call_by_name(
char_u *name,
int argcount,
ectx_T *ectx,
isn_T *iptr)
isn_T *iptr,
dict_T *selfdict)
{
ufunc_T *ufunc;
@@ -916,7 +988,7 @@ call_by_name(
}
}
return call_ufunc(ufunc, NULL, argcount, ectx, iptr);
return call_ufunc(ufunc, NULL, argcount, ectx, iptr, selfdict);
}
return FAIL;
@@ -932,6 +1004,7 @@ call_partial(
char_u *name = NULL;
int called_emsg_before = called_emsg;
int res = FAIL;
dict_T *selfdict = NULL;
if (tv->v_type == VAR_PARTIAL)
{
@@ -953,9 +1026,10 @@ call_partial(
for (i = 0; i < pt->pt_argc; ++i)
copy_tv(&pt->pt_argv[i], STACK_TV_BOT(-argcount + i));
}
selfdict = pt->pt_dict;
if (pt->pt_func != NULL)
return call_ufunc(pt->pt_func, pt, argcount, ectx, NULL);
return call_ufunc(pt->pt_func, pt, argcount, ectx, NULL, selfdict);
name = pt->pt_name;
}
@@ -973,7 +1047,7 @@ call_partial(
if (error != FCERR_NONE)
res = FAIL;
else
res = call_by_name(fname, argcount, ectx, NULL);
res = call_by_name(fname, argcount, ectx, NULL, selfdict);
vim_free(tofree);
}
@@ -1325,7 +1399,7 @@ call_eval_func(
int called_emsg_before = called_emsg;
int res;
res = call_by_name(name, argcount, ectx, iptr);
res = call_by_name(name, argcount, ectx, iptr, NULL);
if (res == FAIL && called_emsg == called_emsg_before)
{
dictitem_T *v;
@@ -1570,6 +1644,7 @@ exec_instructions(ectx_T *ectx)
{
int ret = FAIL;
int save_trylevel_at_start = ectx->ec_trylevel_at_start;
int dict_stack_len_at_start = dict_stack.ga_len;
// Start execution at the first instruction.
ectx->ec_iidx = 0;
@@ -4022,7 +4097,6 @@ exec_instructions(ectx_T *ectx)
dict_T *dict;
char_u *key;
dictitem_T *di;
typval_T temp_tv;
// dict member: dict is at stack-2, key at stack-1
tv = STACK_TV_BOT(-2);
@@ -4041,23 +4115,24 @@ exec_instructions(ectx_T *ectx)
semsg(_(e_dictkey), key);
// If :silent! is used we will continue, make sure the
// stack contents makes sense.
// stack contents makes sense and the dict stack is
// updated.
clear_tv(tv);
--ectx->ec_stack.ga_len;
tv = STACK_TV_BOT(-1);
clear_tv(tv);
(void) dict_stack_save(tv);
tv->v_type = VAR_NUMBER;
tv->vval.v_number = 0;
goto on_fatal_error;
}
clear_tv(tv);
--ectx->ec_stack.ga_len;
// Clear the dict only after getting the item, to avoid
// that it makes the item invalid.
// Put the dict used on the dict stack, it might be used by
// a dict function later.
tv = STACK_TV_BOT(-1);
temp_tv = *tv;
if (dict_stack_save(tv) == FAIL)
goto on_fatal_error;
copy_tv(&di->di_tv, tv);
clear_tv(&temp_tv);
}
break;
@@ -4066,7 +4141,6 @@ exec_instructions(ectx_T *ectx)
{
dict_T *dict;
dictitem_T *di;
typval_T temp_tv;
tv = STACK_TV_BOT(-1);
if (tv->v_type != VAR_DICT || tv->vval.v_dict == NULL)
@@ -4084,11 +4158,37 @@ exec_instructions(ectx_T *ectx)
semsg(_(e_dictkey), iptr->isn_arg.string);
goto on_error;
}
// Clear the dict after getting the item, to avoid that it
// make the item invalid.
temp_tv = *tv;
// Put the dict used on the dict stack, it might be used by
// a dict function later.
if (dict_stack_save(tv) == FAIL)
goto on_fatal_error;
copy_tv(&di->di_tv, tv);
clear_tv(&temp_tv);
}
break;
case ISN_CLEARDICT:
dict_stack_drop();
break;
case ISN_USEDICT:
{
typval_T *dict_tv = dict_stack_get_tv();
// Turn "dict.Func" into a partial for "Func" bound to
// "dict". Don't do this when "Func" is already a partial
// that was bound explicitly (pt_auto is FALSE).
tv = STACK_TV_BOT(-1);
if (dict_tv != NULL
&& dict_tv->v_type == VAR_DICT
&& dict_tv->vval.v_dict != NULL
&& (tv->v_type == VAR_FUNC
|| (tv->v_type == VAR_PARTIAL
&& (tv->vval.v_partial->pt_auto
|| tv->vval.v_partial->pt_dict == NULL))))
dict_tv->vval.v_dict =
make_partial(dict_tv->vval.v_dict, tv);
dict_stack_drop();
}
break;
@@ -4478,6 +4578,7 @@ on_fatal_error:
done:
ret = OK;
theend:
dict_stack_clear(dict_stack_len_at_start);
ectx->ec_trylevel_at_start = save_trylevel_at_start;
return ret;
}
@@ -5568,6 +5669,9 @@ list_instructions(char *pfx, isn_T *instr, int instr_count, ufunc_T *ufunc)
case ISN_MEMBER: smsg("%s%4d MEMBER", pfx, current); break;
case ISN_STRINGMEMBER: smsg("%s%4d MEMBER %s", pfx, current,
iptr->isn_arg.string); break;
case ISN_CLEARDICT: smsg("%s%4d CLEARDICT", pfx, current); break;
case ISN_USEDICT: smsg("%s%4d USEDICT", pfx, current); break;
case ISN_NEGATENR: smsg("%s%4d NEGATENR", pfx, current); break;
case ISN_CHECKNR: smsg("%s%4d CHECKNR", pfx, current); break;

View File

@@ -31,7 +31,7 @@ static long xdl_get_rec(xdfile_t *xdf, long ri, char const **rec) {
static int xdl_emit_record(xdfile_t *xdf, long ri, char const *pre, xdemitcb_t *ecb) {
long size, psize = strlen(pre);
long size, psize = (long)strlen(pre);
char const *rec;
size = xdl_get_rec(xdf, ri, &rec);

View File

@@ -47,7 +47,7 @@ int xdl_emit_diffrec(char const *rec, long size, char const *pre, long psize,
mb[1].size = size;
if (size > 0 && rec[size - 1] != '\n') {
mb[2].ptr = (char *) "\n\\ No newline at end of file\n";
mb[2].size = strlen(mb[2].ptr);
mb[2].size = (long)strlen(mb[2].ptr);
i++;
}
if (ecb->out_line(ecb->priv, mb, i) < 0) {