0
0
mirror of https://github.com/vim/vim.git synced 2025-09-24 03:44:06 -04:00

patch 8.2.2936: Vim9: converting number to bool uses wrong stack offset

Problem:    Vim9: converting number to bool uses wrong stack offset. (Salman
            Halim)
Solution:   Include the offset in the 2BOOL command.
This commit is contained in:
Bram Moolenaar
2021-06-04 21:00:32 +02:00
parent 6c4c404c58
commit 5fa9b24440
6 changed files with 104 additions and 34 deletions

View File

@@ -1650,11 +1650,11 @@ def Test_disassemble_invert_bool()
'\d STORE $0\_s*' .. '\d STORE $0\_s*' ..
'var invert = !flag\_s*' .. 'var invert = !flag\_s*' ..
'\d LOAD $0\_s*' .. '\d LOAD $0\_s*' ..
'\d INVERT (!val)\_s*' .. '\d INVERT -1 (!val)\_s*' ..
'\d STORE $1\_s*' .. '\d STORE $1\_s*' ..
'var res = !!flag\_s*' .. 'var res = !!flag\_s*' ..
'\d LOAD $0\_s*' .. '\d LOAD $0\_s*' ..
'\d 2BOOL (!!val)\_s*' .. '\d 2BOOL -1 (!!val)\_s*' ..
'\d STORE $2\_s*', '\d STORE $2\_s*',
instr) instr)
assert_equal(true, InvertBool()) assert_equal(true, InvertBool())

View File

@@ -2480,6 +2480,25 @@ def Test_expr7_dict_vim9script()
endif endif
enddef enddef
def Test_expr7_call_2bool()
var lines =<< trim END
vim9script
def BrokenCall(nr: number, mode: bool, use: string): void
assert_equal(3, nr)
assert_equal(false, mode)
assert_equal('ab', use)
enddef
def TestBrokenCall(): void
BrokenCall(3, 0, 'ab')
enddef
TestBrokenCall()
END
CheckScriptSuccess(lines)
enddef
let g:oneString = 'one' let g:oneString = 'one'
def Test_expr_member() def Test_expr_member()

View File

@@ -750,6 +750,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 */
/**/
2936,
/**/ /**/
2935, 2935,
/**/ /**/

View File

@@ -148,9 +148,9 @@ typedef enum {
ISN_GETITEM, // push list item, isn_arg.number is the index ISN_GETITEM, // push list item, isn_arg.number is the index
ISN_MEMBER, // dict[member] ISN_MEMBER, // dict[member]
ISN_STRINGMEMBER, // dict.member using isn_arg.string ISN_STRINGMEMBER, // dict.member using isn_arg.string
ISN_2BOOL, // falsy/truthy to bool, invert if isn_arg.number != 0 ISN_2BOOL, // falsy/truthy to bool, uses isn_arg.tobool
ISN_COND2BOOL, // convert value to bool ISN_COND2BOOL, // convert value to bool
ISN_2STRING, // convert value to string at isn_arg.number on stack ISN_2STRING, // convert value to string at isn_arg.tostring on stack
ISN_2STRING_ANY, // like ISN_2STRING but check type ISN_2STRING_ANY, // like ISN_2STRING but check type
ISN_NEGATENR, // apply "-" to number ISN_NEGATENR, // apply "-" to number
@@ -369,6 +369,18 @@ typedef struct {
cexprref_T *cexpr_ref; cexprref_T *cexpr_ref;
} cexpr_T; } cexpr_T;
// arguments to ISN_2STRING and ISN_2STRING_ANY
typedef struct {
int offset;
int tolerant;
} tostring_T;
// arguments to ISN_2BOOL
typedef struct {
int offset;
int invert;
} tobool_T;
/* /*
* Instruction * Instruction
*/ */
@@ -414,6 +426,8 @@ struct isn_S {
subs_T subs; subs_T subs;
cexpr_T cexpr; cexpr_T cexpr;
isn_T *instr; isn_T *instr;
tostring_T tostring;
tobool_T tobool;
} isn_arg; } isn_arg;
}; };

View File

@@ -577,9 +577,10 @@ generate_instr_type(cctx_T *cctx, isntype_T isn_type, type_T *type)
/* /*
* If type at "offset" isn't already VAR_STRING then generate ISN_2STRING. * If type at "offset" isn't already VAR_STRING then generate ISN_2STRING.
* But only for simple types. * But only for simple types.
* When "tolerant" is TRUE convert most types to string, e.g. a List.
*/ */
static int static int
may_generate_2STRING(int offset, cctx_T *cctx) may_generate_2STRING(int offset, int tolerant, cctx_T *cctx)
{ {
isn_T *isn; isn_T *isn;
isntype_T isntype = ISN_2STRING; isntype_T isntype = ISN_2STRING;
@@ -606,12 +607,20 @@ may_generate_2STRING(int offset, cctx_T *cctx)
isntype = ISN_2STRING_ANY; isntype = ISN_2STRING_ANY;
break; break;
// conversion possible when tolerant
case VAR_LIST:
if (tolerant)
{
isntype = ISN_2STRING_ANY;
break;
}
// FALLTHROUGH
// conversion not possible // conversion not possible
case VAR_VOID: case VAR_VOID:
case VAR_BLOB: case VAR_BLOB:
case VAR_FUNC: case VAR_FUNC:
case VAR_PARTIAL: case VAR_PARTIAL:
case VAR_LIST:
case VAR_DICT: case VAR_DICT:
case VAR_JOB: case VAR_JOB:
case VAR_CHANNEL: case VAR_CHANNEL:
@@ -623,7 +632,8 @@ may_generate_2STRING(int offset, cctx_T *cctx)
*type = &t_string; *type = &t_string;
if ((isn = generate_instr(cctx, isntype)) == NULL) if ((isn = generate_instr(cctx, isntype)) == NULL)
return FAIL; return FAIL;
isn->isn_arg.number = offset; isn->isn_arg.tostring.offset = offset;
isn->isn_arg.tostring.tolerant = tolerant;
return OK; return OK;
} }
@@ -886,9 +896,10 @@ generate_COMPARE(cctx_T *cctx, exprtype_T exprtype, int ic)
/* /*
* Generate an ISN_2BOOL instruction. * Generate an ISN_2BOOL instruction.
* "offset" is the offset in the type stack.
*/ */
static int static int
generate_2BOOL(cctx_T *cctx, int invert) generate_2BOOL(cctx_T *cctx, int invert, int offset)
{ {
isn_T *isn; isn_T *isn;
garray_T *stack = &cctx->ctx_type_stack; garray_T *stack = &cctx->ctx_type_stack;
@@ -896,10 +907,11 @@ generate_2BOOL(cctx_T *cctx, int invert)
RETURN_OK_IF_SKIP(cctx); RETURN_OK_IF_SKIP(cctx);
if ((isn = generate_instr(cctx, ISN_2BOOL)) == NULL) if ((isn = generate_instr(cctx, ISN_2BOOL)) == NULL)
return FAIL; return FAIL;
isn->isn_arg.number = invert; isn->isn_arg.tobool.invert = invert;
isn->isn_arg.tobool.offset = offset;
// type becomes bool // type becomes bool
((type_T **)stack->ga_data)[stack->ga_len - 1] = &t_bool; ((type_T **)stack->ga_data)[stack->ga_len + offset] = &t_bool;
return OK; return OK;
} }
@@ -1008,7 +1020,7 @@ need_type(
{ {
// Using "0", "1" or the result of an expression with "&&" or "||" as a // Using "0", "1" or the result of an expression with "&&" or "||" as a
// boolean is OK but requires a conversion. // boolean is OK but requires a conversion.
generate_2BOOL(cctx, FALSE); generate_2BOOL(cctx, FALSE, offset);
return OK; return OK;
} }
@@ -2782,7 +2794,7 @@ compile_member(int is_slice, cctx_T *cctx)
return FAIL; return FAIL;
*typep = &t_any; *typep = &t_any;
} }
if (may_generate_2STRING(-1, cctx) == FAIL) if (may_generate_2STRING(-1, FALSE, cctx) == FAIL)
return FAIL; return FAIL;
if (generate_instr_drop(cctx, ISN_MEMBER, 1) == FAIL) if (generate_instr_drop(cctx, ISN_MEMBER, 1) == FAIL)
return FAIL; return FAIL;
@@ -3625,7 +3637,7 @@ compile_dict(char_u **arg, cctx_T *cctx, ppconst_T *ppconst)
} }
if (isn->isn_type == ISN_PUSHS) if (isn->isn_type == ISN_PUSHS)
key = isn->isn_arg.string; key = isn->isn_arg.string;
else if (may_generate_2STRING(-1, cctx) == FAIL) else if (may_generate_2STRING(-1, FALSE, cctx) == FAIL)
return FAIL; return FAIL;
*arg = skipwhite(*arg); *arg = skipwhite(*arg);
if (**arg != ']') if (**arg != ']')
@@ -4026,7 +4038,7 @@ compile_leader(cctx_T *cctx, int numeric_only, char_u *start, char_u **end)
invert = !invert; invert = !invert;
--p; --p;
} }
if (generate_2BOOL(cctx, invert) == FAIL) if (generate_2BOOL(cctx, invert, -1) == FAIL)
return FAIL; return FAIL;
} }
} }
@@ -4849,8 +4861,8 @@ compile_expr5(char_u **arg, cctx_T *cctx, ppconst_T *ppconst)
ppconst->pp_is_const = FALSE; ppconst->pp_is_const = FALSE;
if (*op == '.') if (*op == '.')
{ {
if (may_generate_2STRING(-2, cctx) == FAIL if (may_generate_2STRING(-2, FALSE, cctx) == FAIL
|| may_generate_2STRING(-1, cctx) == FAIL) || may_generate_2STRING(-1, FALSE, cctx) == FAIL)
return FAIL; return FAIL;
generate_instr_drop(cctx, ISN_CONCAT, 1); generate_instr_drop(cctx, ISN_CONCAT, 1);
} }
@@ -6420,7 +6432,8 @@ compile_assign_unlet(
emsg(e_cannot_use_range_with_dictionary); emsg(e_cannot_use_range_with_dictionary);
return FAIL; return FAIL;
} }
if (dest_type == VAR_DICT && may_generate_2STRING(-1, cctx) == FAIL) if (dest_type == VAR_DICT
&& may_generate_2STRING(-1, FALSE, cctx) == FAIL)
return FAIL; return FAIL;
if (dest_type == VAR_LIST || dest_type == VAR_BLOB) if (dest_type == VAR_LIST || dest_type == VAR_BLOB)
{ {
@@ -8383,7 +8396,7 @@ compile_throw(char_u *arg, cctx_T *cctx UNUSED)
return NULL; return NULL;
if (cctx->ctx_skip == SKIP_YES) if (cctx->ctx_skip == SKIP_YES)
return p; return p;
if (may_generate_2STRING(-1, cctx) == FAIL) if (may_generate_2STRING(-1, FALSE, cctx) == FAIL)
return NULL; return NULL;
if (generate_instr_drop(cctx, ISN_THROW, 1) == NULL) if (generate_instr_drop(cctx, ISN_THROW, 1) == NULL)
return NULL; return NULL;
@@ -8618,7 +8631,7 @@ compile_exec(char_u *line, exarg_T *eap, cctx_T *cctx)
p += 2; p += 2;
if (compile_expr0(&p, cctx) == FAIL) if (compile_expr0(&p, cctx) == FAIL)
return NULL; return NULL;
may_generate_2STRING(-1, cctx); may_generate_2STRING(-1, TRUE, cctx);
++count; ++count;
p = skipwhite(p); p = skipwhite(p);
if (*p != '`') if (*p != '`')

View File

@@ -980,7 +980,7 @@ store_var(char_u *name, typval_T *tv)
* Return FAIL if not allowed. * Return FAIL if not allowed.
*/ */
static int static int
do_2string(typval_T *tv, int is_2string_any) do_2string(typval_T *tv, int is_2string_any, int tolerant)
{ {
if (tv->v_type != VAR_STRING) if (tv->v_type != VAR_STRING)
{ {
@@ -995,6 +995,22 @@ do_2string(typval_T *tv, int is_2string_any)
case VAR_NUMBER: case VAR_NUMBER:
case VAR_FLOAT: case VAR_FLOAT:
case VAR_BLOB: break; case VAR_BLOB: break;
case VAR_LIST:
if (tolerant)
{
char_u *p;
str = typval2string(tv, TRUE);
clear_tv(tv);
tv->v_type = VAR_STRING;
tv->vval.v_string = str;
// TODO: escaping
while ((p = vim_strchr(str, '\n')) != NULL)
*p = ' ';
return OK;
}
// FALLTHROUGH
default: to_string_error(tv->v_type); default: to_string_error(tv->v_type);
return FAIL; return FAIL;
} }
@@ -2055,7 +2071,7 @@ exec_instructions(ectx_T *ectx)
{ {
dest_type = tv_dest->v_type; dest_type = tv_dest->v_type;
if (dest_type == VAR_DICT) if (dest_type == VAR_DICT)
status = do_2string(tv_idx, TRUE); status = do_2string(tv_idx, TRUE, FALSE);
else if (dest_type == VAR_LIST else if (dest_type == VAR_LIST
&& tv_idx->v_type != VAR_NUMBER) && tv_idx->v_type != VAR_NUMBER)
{ {
@@ -3770,15 +3786,16 @@ exec_instructions(ectx_T *ectx)
int n; int n;
int error = FALSE; int error = FALSE;
tv = STACK_TV_BOT(-1);
if (iptr->isn_type == ISN_2BOOL) if (iptr->isn_type == ISN_2BOOL)
{ {
tv = STACK_TV_BOT(iptr->isn_arg.tobool.offset);
n = tv2bool(tv); n = tv2bool(tv);
if (iptr->isn_arg.number) // invert if (iptr->isn_arg.tobool.invert)
n = !n; n = !n;
} }
else else
{ {
tv = STACK_TV_BOT(-1);
SOURCING_LNUM = iptr->isn_lnum; SOURCING_LNUM = iptr->isn_lnum;
n = tv_get_bool_chk(tv, &error); n = tv_get_bool_chk(tv, &error);
if (error) if (error)
@@ -3793,8 +3810,9 @@ exec_instructions(ectx_T *ectx)
case ISN_2STRING: case ISN_2STRING:
case ISN_2STRING_ANY: case ISN_2STRING_ANY:
SOURCING_LNUM = iptr->isn_lnum; SOURCING_LNUM = iptr->isn_lnum;
if (do_2string(STACK_TV_BOT(iptr->isn_arg.number), if (do_2string(STACK_TV_BOT(iptr->isn_arg.tostring.offset),
iptr->isn_type == ISN_2STRING_ANY) == FAIL) iptr->isn_type == ISN_2STRING_ANY,
iptr->isn_arg.tostring.tolerant) == FAIL)
goto on_error; goto on_error;
break; break;
@@ -5122,26 +5140,30 @@ list_instructions(char *pfx, isn_T *instr, int instr_count, ufunc_T *ufunc)
break; break;
} }
case ISN_COND2BOOL: smsg("%s%4d COND2BOOL", pfx, current); break; case ISN_COND2BOOL: smsg("%s%4d COND2BOOL", pfx, current); break;
case ISN_2BOOL: if (iptr->isn_arg.number) case ISN_2BOOL: if (iptr->isn_arg.tobool.invert)
smsg("%s%4d INVERT (!val)", pfx, current); smsg("%s%4d INVERT %d (!val)", pfx, current,
iptr->isn_arg.tobool.offset);
else else
smsg("%s%4d 2BOOL (!!val)", pfx, current); smsg("%s%4d 2BOOL %d (!!val)", pfx, current,
iptr->isn_arg.tobool.offset);
break; break;
case ISN_2STRING: smsg("%s%4d 2STRING stack[%lld]", pfx, current, case ISN_2STRING: smsg("%s%4d 2STRING stack[%lld]", pfx, current,
(varnumber_T)(iptr->isn_arg.number)); (varnumber_T)(iptr->isn_arg.tostring.offset));
break; break;
case ISN_2STRING_ANY: smsg("%s%4d 2STRING_ANY stack[%lld]", pfx, current, case ISN_2STRING_ANY: smsg("%s%4d 2STRING_ANY stack[%lld]",
(varnumber_T)(iptr->isn_arg.number)); pfx, current,
(varnumber_T)(iptr->isn_arg.tostring.offset));
break; break;
case ISN_RANGE: smsg("%s%4d RANGE %s", pfx, current, iptr->isn_arg.string); case ISN_RANGE: smsg("%s%4d RANGE %s", pfx, current,
iptr->isn_arg.string);
break; break;
case ISN_PUT: case ISN_PUT:
if (iptr->isn_arg.put.put_lnum == LNUM_VARIABLE_RANGE_ABOVE) if (iptr->isn_arg.put.put_lnum == LNUM_VARIABLE_RANGE_ABOVE)
smsg("%s%4d PUT %c above range", smsg("%s%4d PUT %c above range",
pfx, current, iptr->isn_arg.put.put_regname); pfx, current, iptr->isn_arg.put.put_regname);
else if (iptr->isn_arg.put.put_lnum == LNUM_VARIABLE_RANGE) else if (iptr->isn_arg.put.put_lnum == LNUM_VARIABLE_RANGE)
smsg("%s%4d PUT %c range", smsg("%s%4d PUT %c range",
pfx, current, iptr->isn_arg.put.put_regname); pfx, current, iptr->isn_arg.put.put_regname);
else else
smsg("%s%4d PUT %c %ld", pfx, current, smsg("%s%4d PUT %c %ld", pfx, current,
iptr->isn_arg.put.put_regname, iptr->isn_arg.put.put_regname,