[PATCH 0/1] MR10673: Draft: oleaut32: Fix Null handling in VarAnd.
Handle both operand orderings symmetrically and always write the result payload; the previous code left V_BOOL/V_I4 uninitialized on every fall-through path. Add lang.vbs coverage in both orderings. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10673
From: Francis De Brabandere <francisdb@gmail.com> Handle both operand orderings symmetrically and always write the result payload; the previous code left V_BOOL/V_I4 uninitialized on every fall-through path. Add lang.vbs coverage in both orderings. --- dlls/oleaut32/variant.c | 124 ++++++++++++++++++++++++----------- dlls/vbscript/tests/lang.vbs | 64 ++++++++++++++++++ 2 files changed, 150 insertions(+), 38 deletions(-) diff --git a/dlls/oleaut32/variant.c b/dlls/oleaut32/variant.c index 48851ba05ec..4695ca66bb0 100644 --- a/dlls/oleaut32/variant.c +++ b/dlls/oleaut32/variant.c @@ -3048,52 +3048,100 @@ HRESULT WINAPI VarAnd(LPVARIANT left, LPVARIANT right, LPVARIANT result) if (leftvt == VT_NULL || rightvt == VT_NULL) { - /* - * Special cases for when left variant is VT_NULL - * (VT_NULL & 0 = VT_NULL, VT_NULL & value = value) - */ + /* VBScript three-valued logic for `And` with Null: + * zero And Null = zero (typed by the resvt computed above) + * nonzero And Null = Null + * Null And Null = Null + * + * Both orderings must produce the same result. The previous version + * only inspected the right operand when the left was Null, and never + * wrote the result payload — so the V_BOOL/V_I4 slot was uninitialized + * for every case that fell through. */ + VARIANT *other; + VARTYPE othervt; + BOOL nonzero = FALSE; + + if (leftvt == VT_NULL && rightvt == VT_NULL) + { + V_VT(result) = VT_NULL; + goto VarAnd_Exit; + } + if (leftvt == VT_NULL) + { + other = right; + othervt = rightvt; + } + else + { + other = left; + othervt = leftvt; + } + + switch (othervt) + { + case VT_EMPTY: break; + case VT_I1: nonzero = V_I1(other) != 0; break; + case VT_UI1: nonzero = V_UI1(other) != 0; break; + case VT_I2: nonzero = V_I2(other) != 0; break; + case VT_UI2: nonzero = V_UI2(other) != 0; break; + case VT_I4: nonzero = V_I4(other) != 0; break; + case VT_UI4: nonzero = V_UI4(other) != 0; break; + case VT_I8: nonzero = V_I8(other) != 0; break; + case VT_UI8: nonzero = V_UI8(other) != 0; break; + case VT_INT: nonzero = V_INT(other) != 0; break; + case VT_UINT: nonzero = V_UINT(other) != 0; break; + case VT_BOOL: nonzero = V_BOOL(other) != VARIANT_FALSE; break; + case VT_R4: nonzero = V_R4(other) != 0.0f; break; + case VT_R8: nonzero = V_R8(other) != 0.0; break; + case VT_DATE: nonzero = V_DATE(other) != 0.0; break; + case VT_CY: nonzero = V_CY(other).int64 != 0; break; + case VT_DECIMAL: nonzero = V_DECIMAL(other).Hi32 || V_DECIMAL(other).Lo64; break; + case VT_BSTR: { VARIANT_BOOL b; - switch(rightvt) - { - case VT_I1: if (V_I1(right)) resvt = VT_NULL; break; - case VT_UI1: if (V_UI1(right)) resvt = VT_NULL; break; - case VT_I2: if (V_I2(right)) resvt = VT_NULL; break; - case VT_UI2: if (V_UI2(right)) resvt = VT_NULL; break; - case VT_I4: if (V_I4(right)) resvt = VT_NULL; break; - case VT_UI4: if (V_UI4(right)) resvt = VT_NULL; break; - case VT_I8: if (V_I8(right)) resvt = VT_NULL; break; - case VT_UI8: if (V_UI8(right)) resvt = VT_NULL; break; - case VT_INT: if (V_INT(right)) resvt = VT_NULL; break; - case VT_UINT: if (V_UINT(right)) resvt = VT_NULL; break; - case VT_BOOL: if (V_BOOL(right)) resvt = VT_NULL; break; - case VT_R4: if (V_R4(right)) resvt = VT_NULL; break; - case VT_R8: if (V_R8(right)) resvt = VT_NULL; break; - case VT_CY: - if(V_CY(right).int64) - resvt = VT_NULL; - break; - case VT_DECIMAL: - if (V_DECIMAL(right).Hi32 || V_DECIMAL(right).Lo64) - resvt = VT_NULL; - break; - case VT_BSTR: - hres = VarBoolFromStr(V_BSTR(right), + hres = VarBoolFromStr(V_BSTR(other), LOCALE_USER_DEFAULT, VAR_LOCALBOOL, &b); - if (FAILED(hres)) - return hres; - else if (b) - V_VT(result) = VT_NULL; - else - { - V_VT(result) = VT_BOOL; - V_BOOL(result) = b; - } + if (FAILED(hres)) goto VarAnd_Exit; + if (b) + V_VT(result) = VT_NULL; + else + { + V_VT(result) = VT_BOOL; + V_BOOL(result) = VARIANT_FALSE; } + goto VarAnd_Exit; + } + default: + V_VT(result) = VT_NULL; + goto VarAnd_Exit; + } + + if (nonzero) + { + V_VT(result) = VT_NULL; + goto VarAnd_Exit; } + V_VT(result) = resvt; + switch (resvt) + { + case VT_BOOL: V_BOOL(result) = VARIANT_FALSE; break; + case VT_I1: V_I1(result) = 0; break; + case VT_UI1: V_UI1(result) = 0; break; + case VT_I2: V_I2(result) = 0; break; + case VT_UI2: V_UI2(result) = 0; break; + case VT_I4: V_I4(result) = 0; break; + case VT_UI4: V_UI4(result) = 0; break; + case VT_I8: V_I8(result) = 0; break; + case VT_UI8: V_UI8(result) = 0; break; + case VT_INT: V_INT(result) = 0; break; + case VT_UINT: V_UINT(result) = 0; break; + default: + V_VT(result) = VT_NULL; + break; + } goto VarAnd_Exit; } diff --git a/dlls/vbscript/tests/lang.vbs b/dlls/vbscript/tests/lang.vbs index 777d7bfc5a9..06920a93bad 100644 --- a/dlls/vbscript/tests/lang.vbs +++ b/dlls/vbscript/tests/lang.vbs @@ -196,6 +196,70 @@ call ok(false imp false, "false does not imp false?") call ok(not (true imp false), "true imp false?") call ok(false imp null, "false imp null is false?") +' VBScript three-valued logic for `And` with Null. The result type and value +' must come from the non-Null operand for the zero case, and be Null for any +' non-zero value. Both operand orderings must produce the same result. +Call ok((False And Null) = False, "False And Null is not False") +Call ok(getVT(False And Null) = "VT_BOOL", "getVT(False And Null) = " & getVT(False And Null)) +Call ok(isNull(True And Null), "True And Null is not Null") +Call ok(getVT(True And Null) = "VT_NULL", "getVT(True And Null) = " & getVT(True And Null)) +Call ok((Null And False) = False, "Null And False is not False") +Call ok(getVT(Null And False) = "VT_BOOL", "getVT(Null And False) = " & getVT(Null And False)) +Call ok(isNull(Null And True), "Null And True is not Null") +Call ok(getVT(Null And True) = "VT_NULL", "getVT(Null And True) = " & getVT(Null And True)) +Call ok(isNull(Null And Null), "Null And Null is not Null") +Call ok(getVT(Null And Null) = "VT_NULL", "getVT(Null And Null) = " & getVT(Null And Null)) + +' Integer types: zero-side preserves its own type tag, non-zero collapses to Null. +Call ok((CInt(0) And Null) = 0, "CInt(0) And Null is not 0") +Call ok(getVT(CInt(0) And Null) = "VT_I2", "getVT(CInt(0) And Null) = " & getVT(CInt(0) And Null)) +Call ok((Null And CInt(0)) = 0, "Null And CInt(0) is not 0") +Call ok(getVT(Null And CInt(0)) = "VT_I2", "getVT(Null And CInt(0)) = " & getVT(Null And CInt(0))) +Call ok(isNull(CInt(1) And Null), "CInt(1) And Null is not Null") +Call ok(isNull(Null And CInt(1)), "Null And CInt(1) is not Null") +Call ok(isNull(CInt(5) And Null), "CInt(5) And Null is not Null") +Call ok(isNull(Null And CInt(5)), "Null And CInt(5) is not Null") + +Call ok((CLng(0) And Null) = 0, "CLng(0) And Null is not 0") +Call ok(getVT(CLng(0) And Null) = "VT_I4", "getVT(CLng(0) And Null) = " & getVT(CLng(0) And Null)) +Call ok((Null And CLng(0)) = 0, "Null And CLng(0) is not 0") +Call ok(getVT(Null And CLng(0)) = "VT_I4", "getVT(Null And CLng(0)) = " & getVT(Null And CLng(0))) +Call ok(isNull(CLng(1) And Null), "CLng(1) And Null is not Null") +Call ok(isNull(Null And CLng(1)), "Null And CLng(1) is not Null") +Call ok(isNull(CLng(123456) And Null), "CLng(123456) And Null is not Null") +Call ok(isNull(Null And CLng(123456)), "Null And CLng(123456) is not Null") + +Call ok((CByte(0) And Null) = 0, "CByte(0) And Null is not 0") +Call ok(getVT(CByte(0) And Null) = "VT_UI1", "getVT(CByte(0) And Null) = " & getVT(CByte(0) And Null)) +Call ok((Null And CByte(0)) = 0, "Null And CByte(0) is not 0") +Call ok(getVT(Null And CByte(0)) = "VT_UI1", "getVT(Null And CByte(0)) = " & getVT(Null And CByte(0))) +Call ok(isNull(CByte(1) And Null), "CByte(1) And Null is not Null") +Call ok(isNull(Null And CByte(255)), "Null And CByte(255) is not Null") +Call ok(isNull(CByte(255) And Null), "CByte(255) And Null is not Null") + +' Float and currency types collapse to VT_I4 0 for the zero case (NOT to their +' source type), and to Null otherwise. Verified against Windows cscript. +Call ok((CSng(0.0) And Null) = 0, "CSng(0.0) And Null is not 0") +Call ok(getVT(CSng(0.0) And Null) = "VT_I4", "getVT(CSng(0.0) And Null) = " & getVT(CSng(0.0) And Null)) +Call ok((Null And CSng(0.0)) = 0, "Null And CSng(0.0) is not 0") +Call ok(getVT(Null And CSng(0.0)) = "VT_I4", "getVT(Null And CSng(0.0)) = " & getVT(Null And CSng(0.0))) +Call ok(isNull(CSng(2.5) And Null), "CSng(2.5) And Null is not Null") +Call ok(isNull(Null And CSng(2.5)), "Null And CSng(2.5) is not Null") + +Call ok((CDbl(0.0) And Null) = 0, "CDbl(0.0) And Null is not 0") +Call ok(getVT(CDbl(0.0) And Null) = "VT_I4", "getVT(CDbl(0.0) And Null) = " & getVT(CDbl(0.0) And Null)) +Call ok((Null And CDbl(0.0)) = 0, "Null And CDbl(0.0) is not 0") +Call ok(getVT(Null And CDbl(0.0)) = "VT_I4", "getVT(Null And CDbl(0.0)) = " & getVT(Null And CDbl(0.0))) +Call ok(isNull(CDbl(3.14) And Null), "CDbl(3.14) And Null is not Null") +Call ok(isNull(Null And CDbl(3.14)), "Null And CDbl(3.14) is not Null") + +Call ok((CCur(0) And Null) = 0, "CCur(0) And Null is not 0") +Call ok(getVT(CCur(0) And Null) = "VT_I4", "getVT(CCur(0) And Null) = " & getVT(CCur(0) And Null)) +Call ok((Null And CCur(0)) = 0, "Null And CCur(0) is not 0") +Call ok(getVT(Null And CCur(0)) = "VT_I4", "getVT(Null And CCur(0)) = " & getVT(Null And CCur(0))) +Call ok(isNull(CCur(12.34) And Null), "CCur(12.34) And Null is not Null") +Call ok(isNull(Null And CCur(12.34)), "Null And CCur(12.34) is not Null") + Call ok(2 >= 1, "! 2 >= 1") Call ok(2 >= 2, "! 2 >= 2") Call ok(2 => 1, "! 2 => 1") -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10673
participants (2)
-
Francis De Brabandere -
Francis De Brabandere (@francisdb)