[PATCH 0/2] MR10909: vbscript: Treat non-literal BSTR as greater than non-literal numeric/bool.
Native VBScript's BSTR-vs-numeric/bool comparison has three paths depending on compile-time literal status of each side: - numeric/bool side is a bare literal: coerce BSTR to a number (error 13 on parse failure) — already handled. - BSTR side is a bare literal, numeric/bool is not: convert numeric to its CStr form and string-compare — was previously taken unconditionally. - neither side is a literal: return BSTR > numeric/bool regardless of values — previously fell into the string-compare path, so matching stringifications like "5" = CInt(5) compared equal instead of unequal. Extend is_literal_expr to also flag EXPR_STRING so the BSTR-literal case is distinguishable from the variable case, and add the third path. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10909
From: Francis De Brabandere <francisdb@gmail.com> --- dlls/vbscript/tests/lang.vbs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/dlls/vbscript/tests/lang.vbs b/dlls/vbscript/tests/lang.vbs index 1b6cdf763e4..4d8af1c810e 100644 --- a/dlls/vbscript/tests/lang.vbs +++ b/dlls/vbscript/tests/lang.vbs @@ -346,6 +346,24 @@ Call ok((Len("ab") < (" " & ctl_nul)), "Len(""ab"") < "" ""&Chr(0) should be tru Call ok((Len("ab") > ""), "Len(""ab"") > """" should be true") +' --- BSTR vs numeric/bool with NEITHER side literal: native treats BSTR +' as greater than the numeric/boolean, regardless of values. --- +Sub testNonLitBstrCmp + Dim s : s = "5" + Dim n : n = CInt(5) + Dim b : b = True + Dim sb : sb = "True" + todo_wine_ok not (s = n), "var ""5"" = var 5 should be false" + todo_wine_ok s > n, "var ""5"" > var 5 should be true (BSTR > num)" + call ok(not (s < n), "var ""5"" < var 5 should be false") + todo_wine_ok not (sb = b), "var ""True"" = var True should be false" + todo_wine_ok sb > b, "var ""True"" > var True should be true (BSTR > bool)" + Dim e : e = "" + call ok(not (e = n), "var """" = var 5 should be false") + todo_wine_ok e > n, "var """" > var 5 should be true (BSTR > num)" +End Sub +Call testNonLitBstrCmp + Dim guard_str : guard_str = "ab" Dim guard_err, guard_r On Error Resume Next -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10909
From: Francis De Brabandere <francisdb@gmail.com> Native VBScript's BSTR-vs-numeric/bool comparison has three paths depending on compile-time literal status of each side: - numeric/bool side is a bare literal: coerce BSTR to a number (error 13 on parse failure) — already handled. - BSTR side is a bare literal, numeric/bool is not: convert numeric to its CStr form and string-compare — was previously taken unconditionally. - neither side is a literal: return BSTR > numeric/bool regardless of values — previously fell into the string-compare path, so matching stringifications like "5" = CInt(5) compared equal instead of unequal. Extend is_literal_expr to also flag EXPR_STRING so the BSTR-literal case is distinguishable from the variable case, and add the third path. --- dlls/vbscript/compile.c | 17 +++++++++-------- dlls/vbscript/interp.c | 35 ++++++++++------------------------- dlls/vbscript/tests/lang.vbs | 10 +++++----- 3 files changed, 24 insertions(+), 38 deletions(-) diff --git a/dlls/vbscript/compile.c b/dlls/vbscript/compile.c index 0b3fbaf97d9..940703762f5 100644 --- a/dlls/vbscript/compile.c +++ b/dlls/vbscript/compile.c @@ -608,20 +608,21 @@ static HRESULT compile_unary_expression(compile_ctx_t *ctx, unary_expression_t * return push_instr(ctx, op) ? S_OK : E_OUTOFMEMORY; } -/* Bare numeric literals at a comparison site take a different code path on - * native VBScript: BSTR vs literal numeric coerces to a number (error 13 on - * parse failure), while BSTR vs anything else (variable, arithmetic result, - * function return, Const, ...) uses string comparison. Detect "bare numeric - * literal" syntactically; parens are transparent, but a Const reference is an - * EXPR_MEMBER and thus correctly treated as non-literal even if its expansion - * is itself an EXPR_INT. */ +/* Bare literals at a comparison site take a different code path on native + * VBScript: BSTR vs literal numeric coerces to a number (error 13 on parse + * failure), literal BSTR vs non-literal numeric/bool uses string comparison, + * and non-literal BSTR vs non-literal numeric/bool treats the BSTR as + * always greater. Detect "bare literal" syntactically; parens are + * transparent, but a Const reference is an EXPR_MEMBER and thus correctly + * treated as non-literal even if its expansion is itself an EXPR_INT. */ static BOOL is_literal_expr(expression_t *expr) { while(expr->type == EXPR_BRACKETS) expr = ((unary_expression_t*)expr)->subexpr; return expr->type == EXPR_INT || expr->type == EXPR_DOUBLE - || expr->type == EXPR_DATE; + || expr->type == EXPR_DATE + || expr->type == EXPR_STRING; } static BOOL is_compare_op(vbsop_t op) diff --git a/dlls/vbscript/interp.c b/dlls/vbscript/interp.c index 219b4fdefda..00510697614 100644 --- a/dlls/vbscript/interp.c +++ b/dlls/vbscript/interp.c @@ -2293,39 +2293,17 @@ static HRESULT var_cmp(exec_ctx_t *ctx, VARIANT *l, VARIANT *r, unsigned flags) return VARCMP_EQ; } - /* BSTR vs numeric (non-literal) or VT_BOOL: coerce the numeric/bool side + /* Literal BSTR vs non-literal numeric/bool: coerce the numeric/bool side * to its CStr form and string-compare. No error on unparseable BSTR; * relational uses binary lex order. VarBstrFromBool yields "-1"/"0" * rather than VBScript's "True"/"False", so bool strings are hardcoded. */ - if((lvt == VT_BSTR && (is_numeric_vt(rvt) || rvt == VT_BOOL)) || - (rvt == VT_BSTR && (is_numeric_vt(lvt) || lvt == VT_BOOL))) { + if((lvt == VT_BSTR && (is_numeric_vt(rvt) || rvt == VT_BOOL) && l_lit) || + (rvt == VT_BSTR && (is_numeric_vt(lvt) || lvt == VT_BOOL) && r_lit)) { VARIANT *num = lvt == VT_BSTR ? r : l; VARIANT *str = lvt == VT_BSTR ? l : r; - BSTR str_bstr = V_BSTR(str); VARIANT num_str; HRESULT hres; - /* Native treats a BSTR as greater than any numeric or boolean, - * regardless of binary lex order, when it is non-empty and either - * all-whitespace or contains any C0 control character (Chr(0).. - * Chr(31)). BSTRs are length-prefixed and may contain embedded - * NUL, so use SysStringLen rather than NUL-terminated scan. */ - if(str_bstr) { - UINT len = SysStringLen(str_bstr); - BOOL has_ctrl = FALSE, all_space = TRUE; - UINT i; - for(i = 0; i < len; i++) { - if(str_bstr[i] < 0x20) { - has_ctrl = TRUE; - break; - } - if(!iswspace(str_bstr[i])) - all_space = FALSE; - } - if(len && (has_ctrl || all_space)) - return lvt == VT_BSTR ? VARCMP_GT : VARCMP_LT; - } - VariantInit(&num_str); if((V_VT(num) & VT_TYPEMASK) == VT_BOOL) { V_VT(&num_str) = VT_BSTR; @@ -2343,6 +2321,13 @@ static HRESULT var_cmp(exec_ctx_t *ctx, VARIANT *l, VARIANT *r, unsigned flags) return hres; } + /* BSTR vs numeric/bool with neither side a literal: native treats the + * BSTR as always greater than the numeric/boolean, regardless of the + * BSTR's content. */ + if((lvt == VT_BSTR && (is_numeric_vt(rvt) || rvt == VT_BOOL)) || + (rvt == VT_BSTR && (is_numeric_vt(lvt) || lvt == VT_BOOL))) + return lvt == VT_BSTR ? VARCMP_GT : VARCMP_LT; + return VarCmp(l, r, ctx->script->lcid, 0); } diff --git a/dlls/vbscript/tests/lang.vbs b/dlls/vbscript/tests/lang.vbs index 4d8af1c810e..d34516422e9 100644 --- a/dlls/vbscript/tests/lang.vbs +++ b/dlls/vbscript/tests/lang.vbs @@ -353,14 +353,14 @@ Sub testNonLitBstrCmp Dim n : n = CInt(5) Dim b : b = True Dim sb : sb = "True" - todo_wine_ok not (s = n), "var ""5"" = var 5 should be false" - todo_wine_ok s > n, "var ""5"" > var 5 should be true (BSTR > num)" + call ok(not (s = n), "var ""5"" = var 5 should be false") + call ok(s > n, "var ""5"" > var 5 should be true (BSTR > num)") call ok(not (s < n), "var ""5"" < var 5 should be false") - todo_wine_ok not (sb = b), "var ""True"" = var True should be false" - todo_wine_ok sb > b, "var ""True"" > var True should be true (BSTR > bool)" + call ok(not (sb = b), "var ""True"" = var True should be false") + call ok(sb > b, "var ""True"" > var True should be true (BSTR > bool)") Dim e : e = "" call ok(not (e = n), "var """" = var 5 should be false") - todo_wine_ok e > n, "var """" > var 5 should be true (BSTR > num)" + call ok(e > n, "var """" > var 5 should be true (BSTR > num)") End Sub Call testNonLitBstrCmp -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10909
This merge request was approved by Jacek Caban. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10909
participants (3)
-
Francis De Brabandere -
Francis De Brabandere (@francisdb) -
Jacek Caban (@jacek)