From: Francis De Brabandere <francisdb@gmail.com> Native VarXor already converts a BSTR operand to VT_I4 when it parses as a number, falling back to VT_BOOL otherwise; VarAnd, VarOr and VarImp do not. That left expressions like ""1"" And ""2"" producing Boolean[True] in Wine instead of native's Long[0]. Apply the same coercion in interp_and / interp_or / interp_imp before calling oleaut32 so the bitwise result type matches. --- dlls/vbscript/interp.c | 43 +++++++++++++++++++++++++++++++++--- dlls/vbscript/tests/lang.vbs | 18 +++++++-------- 2 files changed, 49 insertions(+), 12 deletions(-) diff --git a/dlls/vbscript/interp.c b/dlls/vbscript/interp.c index 219b4fdefda..b0dda54a584 100644 --- a/dlls/vbscript/interp.c +++ b/dlls/vbscript/interp.c @@ -2078,6 +2078,31 @@ static inline void coerce_empty_to_i2(variant_val_t *val) val->owned = TRUE; } +/* Native And/Or/Imp coerce a BSTR operand to a number when it parses as one, + * falling back to a Boolean conversion otherwise. VarXor already does this + * internally; VarAnd/VarOr/VarImp do not, so VBScript needs to coerce first. */ +static HRESULT coerce_str_to_num_or_bool(variant_val_t *val) +{ + DOUBLE d; + HRESULT hres; + VARTYPE target; + + if(V_VT(val->v) != VT_BSTR) + return S_OK; + + target = SUCCEEDED(VarR8FromStr(V_BSTR(val->v), LOCALE_USER_DEFAULT, 0, &d)) ? VT_I4 : VT_BOOL; + VariantInit(&val->store); + hres = VariantChangeType(&val->store, val->v, VARIANT_LOCALBOOL, target); + if(FAILED(hres)) + return hres; + + if(val->owned) + VariantClear(val->v); + val->v = &val->store; + val->owned = TRUE; + return S_OK; +} + static HRESULT interp_not(exec_ctx_t *ctx) { variant_val_t val; @@ -2115,7 +2140,11 @@ static HRESULT interp_and(exec_ctx_t *ctx) if(SUCCEEDED(hres)) { coerce_empty_to_i4(&l); coerce_empty_to_i4(&r); - hres = VarAnd(l.v, r.v, &v); + hres = coerce_str_to_num_or_bool(&l); + if(SUCCEEDED(hres)) + hres = coerce_str_to_num_or_bool(&r); + if(SUCCEEDED(hres)) + hres = VarAnd(l.v, r.v, &v); release_val(&l); } release_val(&r); @@ -2141,7 +2170,11 @@ static HRESULT interp_or(exec_ctx_t *ctx) if(SUCCEEDED(hres)) { coerce_empty_to_i4(&l); coerce_empty_to_i4(&r); - hres = VarOr(l.v, r.v, &v); + hres = coerce_str_to_num_or_bool(&l); + if(SUCCEEDED(hres)) + hres = coerce_str_to_num_or_bool(&r); + if(SUCCEEDED(hres)) + hres = VarOr(l.v, r.v, &v); release_val(&l); } release_val(&r); @@ -2229,7 +2262,11 @@ static HRESULT interp_imp(exec_ctx_t *ctx) } else { coerce_empty_to_i4(&l); coerce_empty_to_i4(&r); - hres = VarImp(l.v, r.v, &v); + hres = coerce_str_to_num_or_bool(&l); + if(SUCCEEDED(hres)) + hres = coerce_str_to_num_or_bool(&r); + if(SUCCEEDED(hres)) + hres = VarImp(l.v, r.v, &v); } release_val(&l); } diff --git a/dlls/vbscript/tests/lang.vbs b/dlls/vbscript/tests/lang.vbs index be80ca2b51a..699f59820c3 100644 --- a/dlls/vbscript/tests/lang.vbs +++ b/dlls/vbscript/tests/lang.vbs @@ -537,19 +537,19 @@ Call ok(getVT(Not Empty) = "VT_I4", "getVT(Not Empty) = " & getVT(Not E ' Logical/bitwise ops with BSTR operands coerce the string to a number when ' parseable (matching VarXor), giving a Long result instead of treating the ' string as a Boolean. -todo_wine_ok 0 = ("1" And "2"), """1"" And ""2"" is not 0" -todo_wine_ok 1 = ("5" And "3"), """5"" And ""3"" is not 1" -todo_wine_ok getVT("1" And "2") = "VT_I4", "getVT(""1"" And ""2"") = " & getVT("1" And "2") -todo_wine_ok 3 = ("1" Or "2"), """1"" Or ""2"" is not 3" -todo_wine_ok 7 = ("5" Or "3"), """5"" Or ""3"" is not 7" -todo_wine_ok getVT("1" Or "2") = "VT_I4", "getVT(""1"" Or ""2"") = " & getVT("1" Or "2") -todo_wine_ok -2 = ("1" Imp "2"), """1"" Imp ""2"" is not -2" -todo_wine_ok getVT("1" Imp "2") = "VT_I4", "getVT(""1"" Imp ""2"") = " & getVT("1" Imp "2") +Call ok(("1" And "2") = 0, """1"" And ""2"" is not 0") +Call ok(("5" And "3") = 1, """5"" And ""3"" is not 1") +Call ok(getVT("1" And "2") = "VT_I4", "getVT(""1"" And ""2"") = " & getVT("1" And "2")) +Call ok(("1" Or "2") = 3, """1"" Or ""2"" is not 3") +Call ok(("5" Or "3") = 7, """5"" Or ""3"" is not 7") +Call ok(getVT("1" Or "2") = "VT_I4", "getVT(""1"" Or ""2"") = " & getVT("1" Or "2")) +Call ok(("1" Imp "2") = -2, """1"" Imp ""2"" is not -2") +Call ok(getVT("1" Imp "2") = "VT_I4", "getVT(""1"" Imp ""2"") = " & getVT("1" Imp "2")) ' Mixed BSTR + numeric stays Long. Call ok((5 And "3") = 1, "5 And ""3"" is not 1") Call ok(("5" And 3) = 1, """5"" And 3 is not 1") -todo_wine_ok getVT(5 And "3") = "VT_I4", "getVT(5 And ""3"") = " & getVT(5 And "3") +Call ok(getVT(5 And "3") = "VT_I4", "getVT(5 And ""3"") = " & getVT(5 And "3")) ' BSTR with non-numeric content falls back to Boolean conversion, which ' fails with type mismatch for arbitrary text. -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10901