[PATCH v16 0/6] MR10775: vbscript: Fix and expand test coverage for BSTR comparison. (alternate)
Alternate version of !10766 This replaces runtime VT_RESERVED tracking with compile-time is_literal_expr at the AST level: comparison opcodes (equal/nequal/gt/gteq/lt/lteq/case) carry a flag arg with bit 0 = left-literal, bit 1 = right-literal, and var_cmp dispatches on those flags. No runtime stripping, no OP_strip_reserved, and Const references stay non-literal because they're EXPR_MEMBER nodes even though the value is inlined. -- v16: vbscript: Reject Automation types not supported in VBScript on compare. vbscript/tests: Cover BSTR comparison with dispatch-only numeric VTs. vbscript: Match native BSTR-vs-numeric and BSTR-vs-Boolean comparison. vbscript/tests: Cover BSTR comparison literal vs non-literal dispatch. vbscript/tests: Cover BSTR comparison with VT_BYREF and additional numeric VTs. vbscript/tests: Add coverage for BSTR vs numeric comparison. https://gitlab.winehq.org/wine/wine/-/merge_requests/10775
From: Francis De Brabandere <francisdb@gmail.com> Add assertions for locale-invariant numeric-string forms ("+5", "-0", "05", " 5", "5 ", "5e0") and for type-mismatch errors raised when an unparseable BSTR ("abc", "", " ") is compared to a number with =, <> or a relational operator. --- dlls/vbscript/tests/lang.vbs | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/dlls/vbscript/tests/lang.vbs b/dlls/vbscript/tests/lang.vbs index d66e52e8e3a..4e5d79d9fcc 100644 --- a/dlls/vbscript/tests/lang.vbs +++ b/dlls/vbscript/tests/lang.vbs @@ -136,13 +136,35 @@ Call ok(30 > "0", "30 > ""0"" should be true") Call ok(9 < "10", "9 < ""10"" should be true") Call ok(42 = "42", "42 = ""42"" should be true") Call ok(not ("10" > "9"), """10"" > ""9"" should be false (string comparison)") +' Locale-invariant numeric strings: parse to the same value as the literal in any locale +Call ok("+5" = 5, """+5"" = 5 should be true") +Call ok("-0" = 0, """-0"" = 0 should be true") +Call ok("05" = 5, """05"" = 5 should be true") +Call ok(" 5" = 5, """ 5"" = 5 should be true") +Call ok("5 " = 5, """5 "" = 5 should be true") +Call ok("5e0" = 5, """5e0"" = 5 should be true") ' String vs Boolean uses string comparison, not numeric conversion Call ok(not ("1" = True), """1"" = True should be false") Call ok(not ("-1" = True), """-1"" = True should be false") Call ok(not ("0" = False), """0"" = False should be false") -' Non-numeric string compared to number should raise type mismatch +' Non-numeric BSTR compared to number raises type mismatch (= / <> / relational) on error resume next err.clear +x = ("abc" = 5) +Call ok(err.number = 13, """abc"" = 5 err.number = " & err.number) +err.clear +x = ("" = 5) +Call ok(err.number = 13, """"" = 5 err.number = " & err.number) +err.clear +x = (" " = 0) +Call ok(err.number = 13, """ "" = 0 err.number = " & err.number) +err.clear +x = ("abc" <> 5) +Call ok(err.number = 13, """abc"" <> 5 err.number = " & err.number) +err.clear +x = ("" <> 5) +Call ok(err.number = 13, """"" <> 5 err.number = " & err.number) +err.clear x = ("abc" > 5) Call ok(err.number = 13, """abc"" > 5 err.number = " & err.number) err.clear -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10775
From: Francis De Brabandere <francisdb@gmail.com> ByRef parameter holding a BSTR still coerces to numeric for comparison ("5" = 5 is True). For VT_UI1/VT_CY operands, Windows string-compares against CStr(numeric) rather than numeric-coercing, so non-numeric BSTRs return False with no error and relational operators use lex order. Hex ("&hff") and scientific ("1e2") BSTRs parse as numeric. The VT_UI1/VT_CY divergences are marked todo_wine pending the var_cmp fix. --- dlls/vbscript/tests/lang.vbs | 48 ++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/dlls/vbscript/tests/lang.vbs b/dlls/vbscript/tests/lang.vbs index 4e5d79d9fcc..644517063db 100644 --- a/dlls/vbscript/tests/lang.vbs +++ b/dlls/vbscript/tests/lang.vbs @@ -175,6 +175,54 @@ x = (5 > "abc") Call ok(err.number = 13, "5 > ""abc"" err.number = " & err.number) on error goto 0 +' BSTR coerces to numeric for comparison even when it carries VT_BYREF +' (e.g. ByRef parameter holding a string). +Sub TestByRefStrEq5(ByRef x) + Call ok(x = 5, "ByRef ""5"" = 5 should be true") + Call ok(x < 6, "ByRef ""5"" < 6 should be true") +End Sub +TestByRefStrEq5 "5" + +' BSTR vs each numeric VT VBScript can produce. +Call ok("5" = CByte(5), """5"" = CByte(5) should be true") +Call ok("5" = CInt(5), """5"" = CInt(5) should be true") +Call ok("5" = CLng(5), """5"" = CLng(5) should be true") +Call ok("5" = CSng(5), """5"" = CSng(5) should be true") +Call ok("5" = CDbl(5), """5"" = CDbl(5) should be true") +Call ok("5" = CCur(5), """5"" = CCur(5) should be true") +Call ok(CByte(5) = "5", "CByte(5) = ""5"" should be true") +Call ok(CCur(5) = "5", "CCur(5) = ""5"" should be true") + +' Hex / scientific BSTRs parse as numeric. +Call ok("1e2" = 100, """1e2"" = 100 should be true") +Call ok("&hff" = 255, """&hff"" = 255 should be true") +Call ok("&H1F" = 31, """&H1F"" = 31 should be true") + +' VT_UI1/VT_CY vs BSTR diverge from VT_I2: string-compare against CStr(numeric). +' Non-numeric BSTR returns False with NO error (VT_I2 raises 13 for the same). +on error resume next +err.clear +x = ("abc" = CByte(5)) +saved_err = err.number +err.clear +Call todo_wine_ok(saved_err = 0, """abc"" = CByte(5) should not raise error (err=" & saved_err & ")") +x = ("abc" = CCur(5)) +saved_err = err.number +err.clear +Call todo_wine_ok(saved_err = 0, """abc"" = CCur(5) should not raise error (err=" & saved_err & ")") +x = ("" = CByte(0)) +saved_err = err.number +err.clear +Call todo_wine_ok(saved_err = 0, """"" = CByte(0) should not raise error (err=" & saved_err & ")") +x = ("" = CCur(0)) +saved_err = err.number +err.clear +Call todo_wine_ok(saved_err = 0, """"" = CCur(0) should not raise error (err=" & saved_err & ")") +on error goto 0 +' Relational confirms lex compare: 10 > 5 numerically would be true; lex "10" < "5". +Call todo_wine_ok(not ("10" > CByte(5)), """10"" > CByte(5) should be false (lex)") +Call todo_wine_ok(not ("5" < CCur(10)), """5"" < CCur(10) should be false (lex)") + Call ok(getVT(false) = "VT_BOOL", "getVT(false) is not VT_BOOL") Call ok(getVT(true) = "VT_BOOL", "getVT(true) is not VT_BOOL") Call ok(getVT("") = "VT_BSTR", "getVT("""") is not VT_BSTR") -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10775
From: Francis De Brabandere <francisdb@gmail.com> Numeric literals raise type-mismatch (error 13) on unparseable BSTR; variables, arithmetic, C-coercion, function returns and parameters use string-compare instead. Cases needing the var_cmp rework are marked todo_wine. --- dlls/vbscript/tests/lang.vbs | 114 +++++++++++++++++++++++++++++++++++ 1 file changed, 114 insertions(+) diff --git a/dlls/vbscript/tests/lang.vbs b/dlls/vbscript/tests/lang.vbs index 644517063db..6c9f36c44a9 100644 --- a/dlls/vbscript/tests/lang.vbs +++ b/dlls/vbscript/tests/lang.vbs @@ -223,6 +223,120 @@ on error goto 0 Call todo_wine_ok(not ("10" > CByte(5)), """10"" > CByte(5) should be false (lex)") Call todo_wine_ok(not ("5" < CCur(10)), """5"" < CCur(10) should be false (lex)") +' --- BSTR vs numeric LITERAL: BSTR coerces to a number, parse failure raises 13. --- +Dim saved_err +on error resume next +err.clear +x = ("abc" = 5.5) : saved_err = err.number : err.clear +Call ok(saved_err = 13, "literal R8: ""abc"" = 5.5 should error 13 (got " & saved_err & ")") +x = ("abc" = 1e2) : saved_err = err.number : err.clear +Call ok(saved_err = 13, "literal sci: ""abc"" = 1e2 should error 13 (got " & saved_err & ")") +x = ("abc" = &hff) : saved_err = err.number : err.clear +Call ok(saved_err = 13, "literal hex: ""abc"" = &hff should error 13 (got " & saved_err & ")") +x = ("abc" = 100000) : saved_err = err.number : err.clear +Call ok(saved_err = 13, "literal I4: ""abc"" = 100000 should error 13 (got " & saved_err & ")") +x = ("abc" = #1/15/2024#): saved_err = err.number : err.clear +Call ok(saved_err = 13, "literal date: ""abc"" = #1/15/2024# should error 13 (got " & saved_err & ")") +on error goto 0 + +' --- Variable holding a numeric loses "literal" status: string compare, no error. --- +Dim n5 : n5 = 5 +Dim n10 : n10 = 10 +on error resume next +err.clear +x = ("abc" = n5) : saved_err = err.number : err.clear +Call todo_wine_ok(saved_err = 0, "var: ""abc"" = (n=5) should not raise (got " & saved_err & ")") +err.clear +x = ("abc" = n5) : Call todo_wine_ok(err.number = 0 and (x = False), "var: ""abc"" = (n=5) should be false") +err.clear +x = ("010" = n10) : Call todo_wine_ok(err.number = 0 and (x = False), "var: ""010"" = (n=10) should be false (string)") +err.clear +x = ("abc" = (5+0)) : saved_err = err.number : err.clear +Call todo_wine_ok(saved_err = 0, "arith: ""abc"" = (5+0) should not raise (got " & saved_err & ")") +err.clear +x = ("010" = (5+5)) : Call todo_wine_ok(err.number = 0 and (x = False), "arith: ""010"" = (5+5) should be false") +err.clear +x = ("010" = (10*1)) : Call todo_wine_ok(err.number = 0 and (x = False), "arith: ""010"" = (10*1) should be false") +err.clear +x = ("abc" = -5) : saved_err = err.number : err.clear +Call todo_wine_ok(saved_err = 0, "neg: ""abc"" = -5 should not raise (got " & saved_err & ")") +err.clear +x = ("010" = CInt(10)) : Call todo_wine_ok(err.number = 0 and (x = False), "CInt: ""010"" = CInt(10) should be false") +err.clear +x = ("010" = CLng(10)) : Call todo_wine_ok(err.number = 0 and (x = False), "CLng: ""010"" = CLng(10) should be false") +err.clear +x = ("010" = CSng(10)) : Call todo_wine_ok(err.number = 0 and (x = False), "CSng: ""010"" = CSng(10) should be false") +err.clear +x = ("010" = CDbl(10)) : Call todo_wine_ok(err.number = 0 and (x = False), "CDbl: ""010"" = CDbl(10) should be false") +err.clear +x = ("abc" = CInt(5)) : saved_err = err.number : err.clear +Call todo_wine_ok(saved_err = 0, "CInt: ""abc"" = CInt(5) should not raise (got " & saved_err & ")") +err.clear +x = ("abc" = CLng(5)) : saved_err = err.number : err.clear +Call todo_wine_ok(saved_err = 0, "CLng: ""abc"" = CLng(5) should not raise (got " & saved_err & ")") +err.clear +x = ("abc" = CSng(5)) : saved_err = err.number : err.clear +Call todo_wine_ok(saved_err = 0, "CSng: ""abc"" = CSng(5) should not raise (got " & saved_err & ")") +err.clear +x = ("abc" = CDbl(5)) : saved_err = err.number : err.clear +Call todo_wine_ok(saved_err = 0, "CDbl: ""abc"" = CDbl(5) should not raise (got " & saved_err & ")") +err.clear +x = ("10" > CDbl(5)) : Call todo_wine_ok(err.number = 0 and (x = False), """10"" > CDbl(5) should be false (lex)") +err.clear +x = ("10" > CSng(5)) : Call todo_wine_ok(err.number = 0 and (x = False), """10"" > CSng(5) should be false (lex)") +err.clear +x = ("9" < CDbl(10)) : Call todo_wine_ok(err.number = 0 and (x = False), """9"" < CDbl(10) should be false (lex)") +on error goto 0 + +' --- VT_DATE from CDate is non-literal: string compare, no error. --- +Dim cdt : cdt = CDate("2024-01-15") +on error resume next +err.clear +x = ("abc" = cdt) : saved_err = err.number : err.clear +on error goto 0 +Call todo_wine_ok(saved_err = 0, "CDate: ""abc"" = CDate(...) should not raise (got " & saved_err & ")") + +' --- Function return / ByVal / ByRef strip "literal" status. --- +Function GetFiveLit() + GetFiveLit = 5 +End Function +on error resume next +err.clear +x = ("abc" = GetFiveLit()) : saved_err = err.number : err.clear +on error goto 0 +Call todo_wine_ok(saved_err = 0, "fn return: ""abc"" = GetFiveLit() should not raise (got " & saved_err & ")") + +Sub TestByValStripsLit(ByVal v) + Dim local_err + on error resume next + err.clear + x = ("abc" = v) : local_err = err.number : err.clear + on error goto 0 + Call todo_wine_ok(local_err = 0, "ByVal: ""abc"" = v should not raise (got " & local_err & ")") +End Sub +TestByValStripsLit 5 + +Sub TestByRefStripsLit(ByRef v) + Dim local_err + on error resume next + err.clear + x = ("abc" = v) : local_err = err.number : err.clear + on error goto 0 + Call todo_wine_ok(local_err = 0, "ByRef: ""abc"" = v should not raise (got " & local_err & ")") +End Sub +Dim litvar : litvar = 5 +TestByRefStripsLit litvar + +' --- Const inlines at compile-time in Wine, so the value is treated as a +' literal and raises error 13; on Windows Const acts like a variable. +' Tracked separately from the var_cmp fix; remains todo_wine for now. --- +Const FIVE_C = 5 +on error resume next +err.clear +x = ("abc" = FIVE_C) : saved_err = err.number : err.clear +on error goto 0 +Call todo_wine_ok(saved_err = 0, "Const: ""abc"" = FIVE should not raise (got " & saved_err & "; needs compile-time fix)") + Call ok(getVT(false) = "VT_BOOL", "getVT(false) is not VT_BOOL") Call ok(getVT(true) = "VT_BOOL", "getVT(true) is not VT_BOOL") Call ok(getVT("") = "VT_BSTR", "getVT("""") is not VT_BSTR") -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10775
From: Francis De Brabandere <francisdb@gmail.com> The compiler tags each comparison operand at the source-AST level: a bare numeric literal (EXPR_INT / EXPR_DOUBLE / EXPR_DATE, parens transparent) sets a flag bit on the comparison opcode, and var_cmp dispatches on that flag so BSTR vs literal numeric goes through numeric coercion (error 13 on parse failure) while BSTR vs non-literal numeric uses string compare against CStr(numeric). VT_BOOL is handled in the same branch with hardcoded "True"/"False" since VarBstrFromBool yields "-1"/"0". Const references compile to an EXPR_MEMBER node, so they are correctly tagged non-literal even though the value is inlined at compile time. --- dlls/vbscript/compile.c | 49 +++++++++- dlls/vbscript/interp.c | 74 +++++++++++++--- dlls/vbscript/tests/lang.vbs | 167 ++++++++++++++--------------------- dlls/vbscript/vbscript.h | 20 +++-- 4 files changed, 187 insertions(+), 123 deletions(-) diff --git a/dlls/vbscript/compile.c b/dlls/vbscript/compile.c index 16e661c98ff..7ed43057d6d 100644 --- a/dlls/vbscript/compile.c +++ b/dlls/vbscript/compile.c @@ -204,6 +204,19 @@ static HRESULT push_instr_addr(compile_ctx_t *ctx, vbsop_t op, unsigned arg) return S_OK; } +static HRESULT push_instr_addr_uint(compile_ctx_t *ctx, vbsop_t op, unsigned arg1, unsigned arg2) +{ + unsigned ret; + + ret = push_instr(ctx, op); + if(!ret) + return E_OUTOFMEMORY; + + instr_ptr(ctx, ret)->arg1.uint = arg1; + instr_ptr(ctx, ret)->arg2.uint = arg2; + return S_OK; +} + static HRESULT push_instr_str(compile_ctx_t *ctx, vbsop_t op, const WCHAR *arg) { unsigned instr; @@ -590,6 +603,28 @@ 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. */ +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; +} + +static BOOL is_compare_op(vbsop_t op) +{ + return op == OP_equal || op == OP_nequal || op == OP_gt + || op == OP_gteq || op == OP_lt || op == OP_lteq; +} + static HRESULT compile_binary_expression(compile_ctx_t *ctx, binary_expression_t *expr, vbsop_t op) { HRESULT hres; @@ -602,6 +637,12 @@ static HRESULT compile_binary_expression(compile_ctx_t *ctx, binary_expression_t if(FAILED(hres)) return hres; + if(is_compare_op(op)) { + unsigned flags = (is_literal_expr(expr->left) ? CMP_LEFT_LITERAL : 0) + | (is_literal_expr(expr->right) ? CMP_RIGHT_LITERAL : 0); + return push_instr_uint(ctx, op, flags); + } + return push_instr(ctx, op) ? S_OK : E_OUTOFMEMORY; } @@ -1060,8 +1101,11 @@ static HRESULT compile_select_statement(compile_ctx_t *ctx, select_statement_t * unsigned end_label, case_cnt = 0, *case_labels = NULL, i; case_clausule_t *case_iter; expression_t *expr_iter; + BOOL test_lit; HRESULT hres; + test_lit = is_literal_expr(stat->expr); + hres = compile_expression(ctx, stat->expr); if(FAILED(hres)) return hres; @@ -1096,11 +1140,14 @@ static HRESULT compile_select_statement(compile_ctx_t *ctx, select_statement_t * break; for(expr_iter = case_iter->expr; expr_iter; expr_iter = expr_iter->next) { + unsigned flags = (test_lit ? CMP_LEFT_LITERAL : 0) + | (is_literal_expr(expr_iter) ? CMP_RIGHT_LITERAL : 0); + hres = compile_expression(ctx, expr_iter); if(FAILED(hres)) break; - hres = push_instr_addr(ctx, OP_case, case_labels[i]); + hres = push_instr_addr_uint(ctx, OP_case, case_labels[i], flags); if(FAILED(hres)) break; diff --git a/dlls/vbscript/interp.c b/dlls/vbscript/interp.c index 51099e67d0e..3cd7921e81a 100644 --- a/dlls/vbscript/interp.c +++ b/dlls/vbscript/interp.c @@ -2110,16 +2110,27 @@ static HRESULT interp_imp(exec_ctx_t *ctx) static inline BOOL is_numeric_vt(VARTYPE vt) { return vt == VT_I2 || vt == VT_I4 || vt == VT_R4 || vt == VT_R8 - || vt == VT_CY || vt == VT_DATE || vt == VT_UI1; + || vt == VT_CY || vt == VT_DATE || vt == VT_UI1 || vt == VT_DECIMAL + || vt == VT_I1; } -static HRESULT var_cmp(exec_ctx_t *ctx, VARIANT *l, VARIANT *r) +/* CMP_LEFT_LITERAL / CMP_RIGHT_LITERAL are set by the compiler: each side is + * flagged if the source expression at the comparison site is a bare numeric + * literal. Native VBScript dispatches BSTR-vs-numeric on this distinction + * (literal coerces, non-literal string-compares). */ +static HRESULT var_cmp(exec_ctx_t *ctx, VARIANT *l, VARIANT *r, unsigned flags) { + BOOL l_lit = flags & CMP_LEFT_LITERAL; + BOOL r_lit = flags & CMP_RIGHT_LITERAL; + VARTYPE lvt = V_VT(l) & VT_TYPEMASK; + VARTYPE rvt = V_VT(r) & VT_TYPEMASK; + TRACE("%s %s\n", debugstr_variant(l), debugstr_variant(r)); - /* VarCmp would use string comparison; VBScript converts the string to a number. */ - if((V_VT(l) == VT_BSTR && is_numeric_vt(V_VT(r))) || - (V_VT(r) == VT_BSTR && is_numeric_vt(V_VT(l)))) { + /* BSTR vs numeric LITERAL: coerce BSTR to a number, parse failure raises + * type-mismatch (error 13). */ + if((lvt == VT_BSTR && is_numeric_vt(rvt) && r_lit) || + (rvt == VT_BSTR && is_numeric_vt(lvt) && l_lit)) { double dl, dr; HRESULT hres; @@ -2136,10 +2147,38 @@ static HRESULT var_cmp(exec_ctx_t *ctx, VARIANT *l, VARIANT *r) return VARCMP_EQ; } + /* BSTR vs numeric (non-literal) or VT_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))) { + VARIANT *num = lvt == VT_BSTR ? r : l; + VARIANT *str = lvt == VT_BSTR ? l : r; + VARIANT num_str; + HRESULT hres; + + VariantInit(&num_str); + if((V_VT(num) & VT_TYPEMASK) == VT_BOOL) { + V_VT(&num_str) = VT_BSTR; + V_BSTR(&num_str) = SysAllocString(V_BOOL(num) ? L"True" : L"False"); + if(!V_BSTR(&num_str)) + return E_OUTOFMEMORY; + }else { + hres = VariantChangeType(&num_str, num, 0, VT_BSTR); + if(FAILED(hres)) + return hres; + } + hres = lvt == VT_BSTR ? VarCmp(str, &num_str, 0, 0) + : VarCmp(&num_str, str, 0, 0); + VariantClear(&num_str); + return hres; + } + return VarCmp(l, r, ctx->script->lcid, 0); } -static HRESULT cmp_oper(exec_ctx_t *ctx) +static HRESULT cmp_oper(exec_ctx_t *ctx, unsigned flags) { variant_val_t l, r; HRESULT hres; @@ -2150,7 +2189,7 @@ static HRESULT cmp_oper(exec_ctx_t *ctx) hres = stack_pop_val(ctx, &l); if(SUCCEEDED(hres)) { - hres = var_cmp(ctx, l.v, r.v); + hres = var_cmp(ctx, l.v, r.v, flags); release_val(&l); } @@ -2160,12 +2199,13 @@ static HRESULT cmp_oper(exec_ctx_t *ctx) static HRESULT interp_equal(exec_ctx_t *ctx) { + const unsigned flags = ctx->instr->arg1.uint; VARIANT v; HRESULT hres; TRACE("\n"); - hres = cmp_oper(ctx); + hres = cmp_oper(ctx, flags); if(FAILED(hres)) return hres; if(hres == VARCMP_NULL) @@ -2178,12 +2218,13 @@ static HRESULT interp_equal(exec_ctx_t *ctx) static HRESULT interp_nequal(exec_ctx_t *ctx) { + const unsigned flags = ctx->instr->arg1.uint; VARIANT v; HRESULT hres; TRACE("\n"); - hres = cmp_oper(ctx); + hres = cmp_oper(ctx, flags); if(FAILED(hres)) return hres; if(hres == VARCMP_NULL) @@ -2196,12 +2237,13 @@ static HRESULT interp_nequal(exec_ctx_t *ctx) static HRESULT interp_gt(exec_ctx_t *ctx) { + const unsigned flags = ctx->instr->arg1.uint; VARIANT v; HRESULT hres; TRACE("\n"); - hres = cmp_oper(ctx); + hres = cmp_oper(ctx, flags); if(FAILED(hres)) return hres; if(hres == VARCMP_NULL) @@ -2214,12 +2256,13 @@ static HRESULT interp_gt(exec_ctx_t *ctx) static HRESULT interp_gteq(exec_ctx_t *ctx) { + const unsigned flags = ctx->instr->arg1.uint; VARIANT v; HRESULT hres; TRACE("\n"); - hres = cmp_oper(ctx); + hres = cmp_oper(ctx, flags); if(FAILED(hres)) return hres; if(hres == VARCMP_NULL) @@ -2232,12 +2275,13 @@ static HRESULT interp_gteq(exec_ctx_t *ctx) static HRESULT interp_lt(exec_ctx_t *ctx) { + const unsigned flags = ctx->instr->arg1.uint; VARIANT v; HRESULT hres; TRACE("\n"); - hres = cmp_oper(ctx); + hres = cmp_oper(ctx, flags); if(FAILED(hres)) return hres; if(hres == VARCMP_NULL) @@ -2250,12 +2294,13 @@ static HRESULT interp_lt(exec_ctx_t *ctx) static HRESULT interp_lteq(exec_ctx_t *ctx) { + const unsigned flags = ctx->instr->arg1.uint; VARIANT v; HRESULT hres; TRACE("\n"); - hres = cmp_oper(ctx); + hres = cmp_oper(ctx, flags); if(FAILED(hres)) return hres; if(hres == VARCMP_NULL) @@ -2269,6 +2314,7 @@ static HRESULT interp_lteq(exec_ctx_t *ctx) static HRESULT interp_case(exec_ctx_t *ctx) { const unsigned arg = ctx->instr->arg1.uint; + const unsigned flags = ctx->instr->arg2.uint; variant_val_t v; HRESULT hres; @@ -2278,7 +2324,7 @@ static HRESULT interp_case(exec_ctx_t *ctx) if(FAILED(hres)) return hres; - hres = var_cmp(ctx, stack_top(ctx, 0), v.v); + hres = var_cmp(ctx, stack_top(ctx, 0), v.v, flags); release_val(&v); if(FAILED(hres)) { if(hres == DISP_E_TYPEMISMATCH) { diff --git a/dlls/vbscript/tests/lang.vbs b/dlls/vbscript/tests/lang.vbs index 6c9f36c44a9..043ff830d5f 100644 --- a/dlls/vbscript/tests/lang.vbs +++ b/dlls/vbscript/tests/lang.vbs @@ -143,10 +143,32 @@ Call ok("05" = 5, """05"" = 5 should be true") Call ok(" 5" = 5, """ 5"" = 5 should be true") Call ok("5 " = 5, """5 "" = 5 should be true") Call ok("5e0" = 5, """5e0"" = 5 should be true") -' String vs Boolean uses string comparison, not numeric conversion +' BSTR vs Boolean: string-compare against CStr(bool) ("True"/"False"), +' case-sensitive, no whitespace trimming, no numeric coercion, no type-mismatch. +Call ok("True" = True, """True"" = True should be true") +Call ok("False" = False, """False"" = False should be true") +Call ok(not ("True" = False), """True"" = False should be false") +Call ok(not ("False" = True), """False"" = True should be false") +Call ok(not ("true" = True), """true"" = True should be false (case-sensitive)") +Call ok(not ("TRUE" = True), """TRUE"" = True should be false (case-sensitive)") +Call ok(not ("True " = True), """True "" = True should be false (no trim)") +Call ok(not (" True" = True), """ True"" = True should be false (no trim)") Call ok(not ("1" = True), """1"" = True should be false") Call ok(not ("-1" = True), """-1"" = True should be false") Call ok(not ("0" = False), """0"" = False should be false") +Call ok(not ("-1" = False), """-1"" = False should be false") +Call ok(not ("True" <> True), """True"" <> True should be false") +Call ok("False" <> True, """False"" <> True should be true") +Call ok(not ("abc" = True), """abc"" = True should be false (no error)") +Call ok(not ("" = True), """"" = True should be false (no error)") +Call ok(not ("" = False), """"" = False should be false (no error)") +Call ok(True = "True", "True = ""True"" should be true") +Call ok(False = "False", "False = ""False"" should be true") +' Relational: lexicographic string comparison after coercing bool to BSTR +Call ok("True" > False, """True"" > False should be true (lex)") +Call ok(not ("True" < False), """True"" < False should be false (lex)") +Call ok(not ("False" > True), """False"" > True should be false (lex)") +Call ok("abc" > True, """abc"" > True should be true (lex)") ' Non-numeric BSTR compared to number raises type mismatch (= / <> / relational) on error resume next err.clear @@ -200,142 +222,85 @@ Call ok("&H1F" = 31, """&H1F"" = 31 should be true") ' VT_UI1/VT_CY vs BSTR diverge from VT_I2: string-compare against CStr(numeric). ' Non-numeric BSTR returns False with NO error (VT_I2 raises 13 for the same). -on error resume next -err.clear -x = ("abc" = CByte(5)) -saved_err = err.number -err.clear -Call todo_wine_ok(saved_err = 0, """abc"" = CByte(5) should not raise error (err=" & saved_err & ")") -x = ("abc" = CCur(5)) -saved_err = err.number -err.clear -Call todo_wine_ok(saved_err = 0, """abc"" = CCur(5) should not raise error (err=" & saved_err & ")") -x = ("" = CByte(0)) -saved_err = err.number -err.clear -Call todo_wine_ok(saved_err = 0, """"" = CByte(0) should not raise error (err=" & saved_err & ")") -x = ("" = CCur(0)) -saved_err = err.number -err.clear -Call todo_wine_ok(saved_err = 0, """"" = CCur(0) should not raise error (err=" & saved_err & ")") -on error goto 0 +Call ok(not ("abc" = CByte(5)), """abc"" = CByte(5) should be false (no error)") +Call ok(not ("abc" = CCur(5)), """abc"" = CCur(5) should be false (no error)") +Call ok(not ("" = CByte(0)), """"" = CByte(0) should be false (no error)") +Call ok(not ("" = CCur(0)), """"" = CCur(0) should be false (no error)") ' Relational confirms lex compare: 10 > 5 numerically would be true; lex "10" < "5". -Call todo_wine_ok(not ("10" > CByte(5)), """10"" > CByte(5) should be false (lex)") -Call todo_wine_ok(not ("5" < CCur(10)), """5"" < CCur(10) should be false (lex)") +Call ok(not ("10" > CByte(5)), """10"" > CByte(5) should be false (lex)") +Call ok(not ("5" < CCur(10)), """5"" < CCur(10) should be false (lex)") ' --- BSTR vs numeric LITERAL: BSTR coerces to a number, parse failure raises 13. --- Dim saved_err on error resume next err.clear x = ("abc" = 5.5) : saved_err = err.number : err.clear -Call ok(saved_err = 13, "literal R8: ""abc"" = 5.5 should error 13 (got " & saved_err & ")") +Call ok(saved_err = 13, "literal R8: ""abc"" = 5.5 should error 13 (got " & saved_err & "; needs literal tracking)") x = ("abc" = 1e2) : saved_err = err.number : err.clear -Call ok(saved_err = 13, "literal sci: ""abc"" = 1e2 should error 13 (got " & saved_err & ")") +Call ok(saved_err = 13, "literal sci: ""abc"" = 1e2 should error 13 (got " & saved_err & "; needs literal tracking)") x = ("abc" = &hff) : saved_err = err.number : err.clear Call ok(saved_err = 13, "literal hex: ""abc"" = &hff should error 13 (got " & saved_err & ")") x = ("abc" = 100000) : saved_err = err.number : err.clear Call ok(saved_err = 13, "literal I4: ""abc"" = 100000 should error 13 (got " & saved_err & ")") x = ("abc" = #1/15/2024#): saved_err = err.number : err.clear -Call ok(saved_err = 13, "literal date: ""abc"" = #1/15/2024# should error 13 (got " & saved_err & ")") +Call ok(saved_err = 13, "literal date: ""abc"" = #1/15/2024# should error 13 (got " & saved_err & "; needs literal tracking)") on error goto 0 -' --- Variable holding a numeric loses "literal" status: string compare, no error. --- +' --- Variable holding a numeric / arithmetic / unary / function return --- +' VBScript on Windows treats these as non-literal: BSTR vs numeric uses +' string-compare, non-numeric BSTR returns False with no error. Dim n5 : n5 = 5 Dim n10 : n10 = 10 -on error resume next -err.clear -x = ("abc" = n5) : saved_err = err.number : err.clear -Call todo_wine_ok(saved_err = 0, "var: ""abc"" = (n=5) should not raise (got " & saved_err & ")") -err.clear -x = ("abc" = n5) : Call todo_wine_ok(err.number = 0 and (x = False), "var: ""abc"" = (n=5) should be false") -err.clear -x = ("010" = n10) : Call todo_wine_ok(err.number = 0 and (x = False), "var: ""010"" = (n=10) should be false (string)") -err.clear -x = ("abc" = (5+0)) : saved_err = err.number : err.clear -Call todo_wine_ok(saved_err = 0, "arith: ""abc"" = (5+0) should not raise (got " & saved_err & ")") -err.clear -x = ("010" = (5+5)) : Call todo_wine_ok(err.number = 0 and (x = False), "arith: ""010"" = (5+5) should be false") -err.clear -x = ("010" = (10*1)) : Call todo_wine_ok(err.number = 0 and (x = False), "arith: ""010"" = (10*1) should be false") -err.clear -x = ("abc" = -5) : saved_err = err.number : err.clear -Call todo_wine_ok(saved_err = 0, "neg: ""abc"" = -5 should not raise (got " & saved_err & ")") -err.clear -x = ("010" = CInt(10)) : Call todo_wine_ok(err.number = 0 and (x = False), "CInt: ""010"" = CInt(10) should be false") -err.clear -x = ("010" = CLng(10)) : Call todo_wine_ok(err.number = 0 and (x = False), "CLng: ""010"" = CLng(10) should be false") -err.clear -x = ("010" = CSng(10)) : Call todo_wine_ok(err.number = 0 and (x = False), "CSng: ""010"" = CSng(10) should be false") -err.clear -x = ("010" = CDbl(10)) : Call todo_wine_ok(err.number = 0 and (x = False), "CDbl: ""010"" = CDbl(10) should be false") -err.clear -x = ("abc" = CInt(5)) : saved_err = err.number : err.clear -Call todo_wine_ok(saved_err = 0, "CInt: ""abc"" = CInt(5) should not raise (got " & saved_err & ")") -err.clear -x = ("abc" = CLng(5)) : saved_err = err.number : err.clear -Call todo_wine_ok(saved_err = 0, "CLng: ""abc"" = CLng(5) should not raise (got " & saved_err & ")") -err.clear -x = ("abc" = CSng(5)) : saved_err = err.number : err.clear -Call todo_wine_ok(saved_err = 0, "CSng: ""abc"" = CSng(5) should not raise (got " & saved_err & ")") -err.clear -x = ("abc" = CDbl(5)) : saved_err = err.number : err.clear -Call todo_wine_ok(saved_err = 0, "CDbl: ""abc"" = CDbl(5) should not raise (got " & saved_err & ")") -err.clear -x = ("10" > CDbl(5)) : Call todo_wine_ok(err.number = 0 and (x = False), """10"" > CDbl(5) should be false (lex)") -err.clear -x = ("10" > CSng(5)) : Call todo_wine_ok(err.number = 0 and (x = False), """10"" > CSng(5) should be false (lex)") -err.clear -x = ("9" < CDbl(10)) : Call todo_wine_ok(err.number = 0 and (x = False), """9"" < CDbl(10) should be false (lex)") -on error goto 0 +Call ok(not ("abc" = n5), "var: ""abc"" = (n=5) should be false") +Call ok(not ("010" = n10), "var: ""010"" = (n=10) should be false (string)") +Call ok(not ("abc" = (5+0)), "arith: ""abc"" = (5+0) should be false") +Call ok(not ("010" = (5+5)), "arith: ""010"" = (5+5) should be false") +Call ok(not ("010" = (10*1)), "arith: ""010"" = (10*1) should be false") +Call ok(not ("abc" = -5), "neg: ""abc"" = -5 should be false") + +' --- C-coercion functions return non-literal values; BSTR vs numeric +' uses string-compare regardless of the underlying VT. --- +Call ok(not ("010" = CInt(10)), "CInt: ""010"" = CInt(10) should be false") +Call ok(not ("010" = CLng(10)), "CLng: ""010"" = CLng(10) should be false") +Call ok(not ("010" = CSng(10)), "CSng: ""010"" = CSng(10) should be false") +Call ok(not ("010" = CDbl(10)), "CDbl: ""010"" = CDbl(10) should be false") +Call ok(not ("abc" = CInt(5)), "CInt: ""abc"" = CInt(5) should be false") +Call ok(not ("abc" = CLng(5)), "CLng: ""abc"" = CLng(5) should be false") +Call ok(not ("abc" = CSng(5)), "CSng: ""abc"" = CSng(5) should be false") +Call ok(not ("abc" = CDbl(5)), "CDbl: ""abc"" = CDbl(5) should be false") + +' VT_R4 / VT_R8 from CSng/CDbl: relational uses lex compare. +Call ok(not ("10" > CDbl(5)), """10"" > CDbl(5) should be false (lex)") +Call ok(not ("10" > CSng(5)), """10"" > CSng(5) should be false (lex)") +Call ok(not ("9" < CDbl(10)), """9"" < CDbl(10) should be false (lex)") ' --- VT_DATE from CDate is non-literal: string compare, no error. --- Dim cdt : cdt = CDate("2024-01-15") -on error resume next -err.clear -x = ("abc" = cdt) : saved_err = err.number : err.clear -on error goto 0 -Call todo_wine_ok(saved_err = 0, "CDate: ""abc"" = CDate(...) should not raise (got " & saved_err & ")") +Call ok(not ("abc" = cdt), "CDate: ""abc"" = CDate(...) should be false") ' --- Function return / ByVal / ByRef strip "literal" status. --- Function GetFiveLit() GetFiveLit = 5 End Function -on error resume next -err.clear -x = ("abc" = GetFiveLit()) : saved_err = err.number : err.clear -on error goto 0 -Call todo_wine_ok(saved_err = 0, "fn return: ""abc"" = GetFiveLit() should not raise (got " & saved_err & ")") +Call ok(not ("abc" = GetFiveLit()), "fn return: ""abc"" = GetFiveLit() should be false") Sub TestByValStripsLit(ByVal v) - Dim local_err - on error resume next - err.clear - x = ("abc" = v) : local_err = err.number : err.clear - on error goto 0 - Call todo_wine_ok(local_err = 0, "ByVal: ""abc"" = v should not raise (got " & local_err & ")") + Call ok(not ("abc" = v), "ByVal: ""abc"" = v should be false") End Sub TestByValStripsLit 5 Sub TestByRefStripsLit(ByRef v) - Dim local_err - on error resume next - err.clear - x = ("abc" = v) : local_err = err.number : err.clear - on error goto 0 - Call todo_wine_ok(local_err = 0, "ByRef: ""abc"" = v should not raise (got " & local_err & ")") + Call ok(not ("abc" = v), "ByRef: ""abc"" = v should be false") End Sub Dim litvar : litvar = 5 TestByRefStripsLit litvar -' --- Const inlines at compile-time in Wine, so the value is treated as a -' literal and raises error 13; on Windows Const acts like a variable. -' Tracked separately from the var_cmp fix; remains todo_wine for now. --- +' Const references compile to an EXPR_MEMBER node so the comparison is +' tagged non-literal even though the value is inlined; "abc" = FIVE goes +' through the BSTR-vs-numeric string-compare path and returns False with +' no error. Const FIVE_C = 5 -on error resume next -err.clear -x = ("abc" = FIVE_C) : saved_err = err.number : err.clear -on error goto 0 -Call todo_wine_ok(saved_err = 0, "Const: ""abc"" = FIVE should not raise (got " & saved_err & "; needs compile-time fix)") +Call ok(not ("abc" = FIVE_C), "Const: ""abc"" = FIVE_C should be false") Call ok(getVT(false) = "VT_BOOL", "getVT(false) is not VT_BOOL") Call ok(getVT(true) = "VT_BOOL", "getVT(true) is not VT_BOOL") diff --git a/dlls/vbscript/vbscript.h b/dlls/vbscript/vbscript.h index e9b76b6cda8..3857186620b 100644 --- a/dlls/vbscript/vbscript.h +++ b/dlls/vbscript/vbscript.h @@ -248,6 +248,12 @@ typedef enum { ARG_DATE } instr_arg_type_t; +/* Flags for the ARG_UINT operand of comparison opcodes (equal, nequal, gt, + * gteq, lt, lteq, case): each bit is set when the corresponding source-AST + * operand is a bare numeric literal. */ +#define CMP_LEFT_LITERAL 0x1 +#define CMP_RIGHT_LITERAL 0x2 + #define OP_LIST \ X(add, 1, 0, 0) \ X(and, 1, 0, 0) \ @@ -255,7 +261,7 @@ typedef enum { X(assign_member, 1, ARG_BSTR, ARG_UINT) \ X(bool, 1, ARG_INT, 0) \ X(catch, 1, ARG_ADDR, ARG_UINT) \ - X(case, 0, ARG_ADDR, 0) \ + X(case, 0, ARG_ADDR, ARG_UINT) \ X(concat, 1, 0, 0) \ X(const, 1, ARG_BSTR, 0) \ X(date, 1, ARG_DATE, 0) \ @@ -265,14 +271,14 @@ typedef enum { X(double, 1, ARG_DOUBLE, 0) \ X(empty, 1, 0, 0) \ X(enumnext, 0, ARG_ADDR, ARG_BSTR) \ - X(equal, 1, 0, 0) \ + X(equal, 1, ARG_UINT, 0) \ X(hres, 1, ARG_UINT, 0) \ X(errmode, 1, ARG_INT, 0) \ X(eqv, 1, 0, 0) \ X(erase, 1, ARG_BSTR, 0) \ X(exp, 1, 0, 0) \ - X(gt, 1, 0, 0) \ - X(gteq, 1, 0, 0) \ + X(gt, 1, ARG_UINT, 0) \ + X(gteq, 1, ARG_UINT, 0) \ X(icall, 1, ARG_BSTR, ARG_UINT) \ X(icallv, 1, ARG_BSTR, ARG_UINT) \ X(ident, 1, ARG_BSTR, 0) \ @@ -284,15 +290,15 @@ typedef enum { X(jmp, 0, ARG_ADDR, 0) \ X(jmp_false, 0, ARG_ADDR, 0) \ X(jmp_true, 0, ARG_ADDR, 0) \ - X(lt, 1, 0, 0) \ - X(lteq, 1, 0, 0) \ + X(lt, 1, ARG_UINT, 0) \ + X(lteq, 1, ARG_UINT, 0) \ X(mcall, 1, ARG_BSTR, ARG_UINT) \ X(mcallv, 1, ARG_BSTR, ARG_UINT) \ X(me, 1, 0, 0) \ X(mod, 1, 0, 0) \ X(mul, 1, 0, 0) \ X(neg, 1, 0, 0) \ - X(nequal, 1, 0, 0) \ + X(nequal, 1, ARG_UINT, 0) \ X(new, 1, ARG_STR, 0) \ X(newenum, 1, 0, 0) \ X(not, 1, 0, 0) \ -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10775
From: Francis De Brabandere <francisdb@gmail.com> VT_I8/UI8/I1/UI2/UI4/UINT cannot be produced by VBScript natively; they arrive only via COM dispatch. Extend testobj with read-only properties returning each of these VTs and exercise BSTR-vs-numeric comparison against them. The non-literal dispatch path goes through string compare against CStr(numeric), so all six cases should behave like VT_UI1/VT_CY. --- dlls/vbscript/tests/lang.vbs | 43 ++++++++++++++++++++++++++++++++++++ dlls/vbscript/tests/run.c | 37 +++++++++++++++++++++++++++++++ 2 files changed, 80 insertions(+) diff --git a/dlls/vbscript/tests/lang.vbs b/dlls/vbscript/tests/lang.vbs index 043ff830d5f..a2e1dfc5dd3 100644 --- a/dlls/vbscript/tests/lang.vbs +++ b/dlls/vbscript/tests/lang.vbs @@ -230,6 +230,49 @@ Call ok(not ("" = CCur(0)), """"" = CCur(0) should be false (no error)") Call ok(not ("10" > CByte(5)), """10"" > CByte(5) should be false (lex)") Call ok(not ("5" < CCur(10)), """5"" < CCur(10) should be false (lex)") +' VT_I8/UI8/I1/UI2/UI4/UINT cannot be produced by VBScript natively; obtain +' them via a host IDispatch (testobj). Native VBScript: +' - VT_UI8/UI2/UI4/UINT: error 458 (VBSE_INVALID_TYPELIB_VARIABLE) on +' both 32-bit and 64-bit when used in any expression. +' - VT_I8: error 458 on 32-bit; on 64-bit no error but BSTR-vs-I8 compares +' as not-equal (no CStr coercion of the numeric side). +' - VT_I1: no error on either arch (treated as a small integer); BSTR +' literal vs I1 compares equal via numeric coercion, matching the +' baseline "5" = CInt(5) behavior. +Call ok("5" = testobj.i1val, """5"" = testobj.i1val should be true") + +Dim x_str : x_str = "5" +Dim cmp_result +On Error Resume Next + +' VT_I8 vs non-literal BSTR. 32-bit: err 458. 64-bit: cmp = False (default +' VarCmp returns BSTR > other since I8 isn't in is_numeric_vt). +Err.Clear : cmp_result = (x_str = testobj.i8val) : saved_err = Err.number : Err.Clear +Call ok(saved_err = 458 or cmp_result = False, _ + "x_str = testobj.i8val: err=" & saved_err & " result=" & cmp_result) + +' VT_UI8/UI2/UI4/UINT: native errors 458 on both archs. Wine's VarCmp +' rejects VT_UI8 and VT_UINT via DISP_E_BADVARTYPE (mapped to 458) +' without a script-level gate; UI2/UI4 still go through DISP_E_TYPEMISMATCH +' (mapped to 13) and need the gate, so they remain todo_wine for now. +Err.Clear : cmp_result = ("5" = testobj.ui8val) : saved_err = Err.number : Err.Clear +Call ok(saved_err = 458, _ + "BSTR = testobj.ui8val should err 458, got " & saved_err) + +Err.Clear : cmp_result = ("5" = testobj.ui2val) : saved_err = Err.number : Err.Clear +Call todo_wine_ok(saved_err = 458, _ + "BSTR = testobj.ui2val should err 458, got " & saved_err) + +Err.Clear : cmp_result = ("5" = testobj.ui4val) : saved_err = Err.number : Err.Clear +Call todo_wine_ok(saved_err = 458, _ + "BSTR = testobj.ui4val should err 458, got " & saved_err) + +Err.Clear : cmp_result = ("5" = testobj.uintval) : saved_err = Err.number : Err.Clear +Call ok(saved_err = 458, _ + "BSTR = testobj.uintval should err 458, got " & saved_err) + +On Error Goto 0 + ' --- BSTR vs numeric LITERAL: BSTR coerces to a number, parse failure raises 13. --- Dim saved_err on error resume next diff --git a/dlls/vbscript/tests/run.c b/dlls/vbscript/tests/run.c index 8d193e174fd..c9fd66588a5 100644 --- a/dlls/vbscript/tests/run.c +++ b/dlls/vbscript/tests/run.c @@ -152,6 +152,12 @@ DEFINE_EXPECT(OnLeaveScript); #define DISPID_TESTOBJ_PROPGET 2000 #define DISPID_TESTOBJ_PROPPUT 2001 #define DISPID_TESTOBJ_KEYWORD 2002 +#define DISPID_TESTOBJ_I8VAL 2003 +#define DISPID_TESTOBJ_UI8VAL 2004 +#define DISPID_TESTOBJ_I1VAL 2005 +#define DISPID_TESTOBJ_UI2VAL 2006 +#define DISPID_TESTOBJ_UI4VAL 2007 +#define DISPID_TESTOBJ_UINTVAL 2008 #define DISPID_COLLOBJ_RESET 3000 @@ -846,6 +852,12 @@ static HRESULT WINAPI testObj_GetDispID(IDispatchEx *iface, BSTR bstrName, DWORD static const dispid_t dispids[] = { { L"propget", DISPID_TESTOBJ_PROPGET, REF_EXPECT(testobj_propget_d) }, { L"propput", DISPID_TESTOBJ_PROPPUT, REF_EXPECT(testobj_propput_d) }, + { L"i8val", DISPID_TESTOBJ_I8VAL }, + { L"ui8val", DISPID_TESTOBJ_UI8VAL }, + { L"i1val", DISPID_TESTOBJ_I1VAL }, + { L"ui2val", DISPID_TESTOBJ_UI2VAL }, + { L"ui4val", DISPID_TESTOBJ_UI4VAL }, + { L"uintval", DISPID_TESTOBJ_UINTVAL }, { L"rem", DISPID_TESTOBJ_KEYWORD }, { L"true", DISPID_TESTOBJ_KEYWORD }, { L"false", DISPID_TESTOBJ_KEYWORD }, @@ -994,6 +1006,31 @@ static HRESULT WINAPI testObj_InvokeEx(IDispatchEx *iface, DISPID id, LCID lcid, V_VT(pvarRes) = VT_I2; V_I2(pvarRes) = 10; return S_OK; + + case DISPID_TESTOBJ_I8VAL: + V_VT(pvarRes) = VT_I8; + V_I8(pvarRes) = 5; + return S_OK; + case DISPID_TESTOBJ_UI8VAL: + V_VT(pvarRes) = VT_UI8; + V_UI8(pvarRes) = 5; + return S_OK; + case DISPID_TESTOBJ_I1VAL: + V_VT(pvarRes) = VT_I1; + V_I1(pvarRes) = 5; + return S_OK; + case DISPID_TESTOBJ_UI2VAL: + V_VT(pvarRes) = VT_UI2; + V_UI2(pvarRes) = 5; + return S_OK; + case DISPID_TESTOBJ_UI4VAL: + V_VT(pvarRes) = VT_UI4; + V_UI4(pvarRes) = 5; + return S_OK; + case DISPID_TESTOBJ_UINTVAL: + V_VT(pvarRes) = VT_UINT; + V_UINT(pvarRes) = 5; + return S_OK; } ok(0, "unexpected call %ld\n", id); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10775
From: Francis De Brabandere <francisdb@gmail.com> Native VBScript raises error 458 (VBSE_INVALID_TYPELIB_VARIABLE) when an expression involves a Variant whose type is one of VT_UI2/VT_UI4/ VT_UI8/VT_UINT, on both 32-bit and 64-bit. Add a script-level type gate at the entry of var_cmp so BSTR-vs-{UI2,UI4,UI8,UINT} comparisons match. --- dlls/vbscript/interp.c | 13 +++++++++++++ dlls/vbscript/tests/lang.vbs | 4 ++-- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/dlls/vbscript/interp.c b/dlls/vbscript/interp.c index 3cd7921e81a..7ff3baecce8 100644 --- a/dlls/vbscript/interp.c +++ b/dlls/vbscript/interp.c @@ -2114,6 +2114,16 @@ static inline BOOL is_numeric_vt(VARTYPE vt) || vt == VT_I1; } +/* Native VBScript rejects these Automation types with error 458 + * (VBSE_INVALID_TYPELIB_VARIABLE) when they appear in script expressions, + * on both 32-bit and 64-bit. VT_I8 is rejected on 32-bit native but + * accepted on 64-bit; not included here so BSTR-vs-I8 falls through to + * the default VarCmp path (BSTR > other), matching 64-bit native. */ +static inline BOOL is_unsupported_script_vt(VARTYPE vt) +{ + return vt == VT_UI2 || vt == VT_UI4 || vt == VT_UI8 || vt == VT_UINT; +} + /* CMP_LEFT_LITERAL / CMP_RIGHT_LITERAL are set by the compiler: each side is * flagged if the source expression at the comparison site is a bare numeric * literal. Native VBScript dispatches BSTR-vs-numeric on this distinction @@ -2127,6 +2137,9 @@ static HRESULT var_cmp(exec_ctx_t *ctx, VARIANT *l, VARIANT *r, unsigned flags) TRACE("%s %s\n", debugstr_variant(l), debugstr_variant(r)); + if(is_unsupported_script_vt(lvt) || is_unsupported_script_vt(rvt)) + return MAKE_VBSERROR(VBSE_INVALID_TYPELIB_VARIABLE); + /* BSTR vs numeric LITERAL: coerce BSTR to a number, parse failure raises * type-mismatch (error 13). */ if((lvt == VT_BSTR && is_numeric_vt(rvt) && r_lit) || diff --git a/dlls/vbscript/tests/lang.vbs b/dlls/vbscript/tests/lang.vbs index a2e1dfc5dd3..f395e065286 100644 --- a/dlls/vbscript/tests/lang.vbs +++ b/dlls/vbscript/tests/lang.vbs @@ -260,11 +260,11 @@ Call ok(saved_err = 458, _ "BSTR = testobj.ui8val should err 458, got " & saved_err) Err.Clear : cmp_result = ("5" = testobj.ui2val) : saved_err = Err.number : Err.Clear -Call todo_wine_ok(saved_err = 458, _ +Call ok(saved_err = 458, _ "BSTR = testobj.ui2val should err 458, got " & saved_err) Err.Clear : cmp_result = ("5" = testobj.ui4val) : saved_err = Err.number : Err.Clear -Call todo_wine_ok(saved_err = 458, _ +Call ok(saved_err = 458, _ "BSTR = testobj.ui4val should err 458, got " & saved_err) Err.Clear : cmp_result = ("5" = testobj.uintval) : saved_err = Err.number : Err.Clear -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10775
On Fri May 1 17:17:12 2026 +0000, Jacek Caban wrote:
Looks mostly good now, but please don't add types here only to remove them in a following commit. cleaned up
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/10775#note_138554
This merge request was approved by Jacek Caban. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10775
participants (3)
-
Francis De Brabandere -
Francis De Brabandere (@francisdb) -
Jacek Caban (@jacek)