Hi Alistair,
On 12.05.2017 05:56, Alistair Leslie-Hughes wrote:
Fixes https://bugs.winehq.org/show_bug.cgi?id=27106
v6 - Simplify the code when the Variant cannot be coerced. v7 - Fix copy/paste bug in return value. v8 - Fixed failed test.
Signed-off-by: Alistair Leslie-Hughes leslie_alistair@hotmail.com
dlls/oleaut32/tests/vartest.c | 217 ++++++++++++++++++++++++++++++++++++++++++ dlls/oleaut32/variant.c | 52 +++------- 2 files changed, 229 insertions(+), 40 deletions(-)
diff --git a/dlls/oleaut32/tests/vartest.c b/dlls/oleaut32/tests/vartest.c index 0e86ffb..f4224dc 100644 --- a/dlls/oleaut32/tests/vartest.c +++ b/dlls/oleaut32/tests/vartest.c @@ -97,6 +97,150 @@ static BOOL has_i8; #define R8_MAX DBL_MAX #define R8_MIN DBL_MIN
+#define DEFINE_EXPECT(func) \
- static BOOL expect_ ## func = FALSE, called_ ## func = FALSE
+#define SET_EXPECT(func) \
- do { called_ ## func = FALSE; expect_ ## func = TRUE; } while(0)
+#define CHECK_EXPECT2(func) \
- do { \
ok(expect_ ##func, "unexpected call " #func "\n"); \
called_ ## func = TRUE; \
- }while(0)
+#define CHECK_EXPECT(func) \
- do { \
CHECK_EXPECT2(func); \
expect_ ## func = FALSE; \
- }while(0)
+#define CHECK_CALLED(func) \
- do { \
ok(called_ ## func, "expected " #func "\n"); \
expect_ ## func = called_ ## func = FALSE; \
- }while(0)
+DEFINE_EXPECT(dispatch_invoke);
+typedef struct +{
- IDispatch IDispatch_iface;
- VARTYPE vt;
- HRESULT result;
+} DummyDispatch;
+static inline DummyDispatch *impl_from_IDispatch(IDispatch *iface) +{
- return CONTAINING_RECORD(iface, DummyDispatch, IDispatch_iface);
+}
+static ULONG WINAPI DummyDispatch_AddRef(IDispatch *iface) +{
- return 2;
+}
+static ULONG WINAPI DummyDispatch_Release(IDispatch *iface) +{
- return 1;
+}
+static HRESULT WINAPI DummyDispatch_QueryInterface(IDispatch *iface,
REFIID riid,
void** ppvObject)
+{
- *ppvObject = NULL;
- if (IsEqualIID(riid, &IID_IDispatch) ||
IsEqualIID(riid, &IID_IUnknown))
- {
*ppvObject = iface;
IDispatch_AddRef(iface);
- }
- return *ppvObject ? S_OK : E_NOINTERFACE;
+}
+static HRESULT WINAPI DummyDispatch_GetTypeInfoCount(IDispatch *iface, UINT *pctinfo) +{
- ok(0, "Unexpected call\n");
- return E_NOTIMPL;
+}
+static HRESULT WINAPI DummyDispatch_GetTypeInfo(IDispatch *iface, UINT tinfo, LCID lcid, ITypeInfo **ti) +{
- ok(0, "Unexpected call\n");
- return E_NOTIMPL;
+}
+static HRESULT WINAPI DummyDispatch_GetIDsOfNames(IDispatch *iface, REFIID riid, LPOLESTR *names,
- UINT cnames, LCID lcid, DISPID *dispid)
+{
- ok(0, "Unexpected call\n");
- return E_NOTIMPL;
+}
+static HRESULT WINAPI DummyDispatch_Invoke(IDispatch *iface,
DISPID dispid, REFIID riid,
LCID lcid, WORD wFlags,
DISPPARAMS *params,
VARIANT *res,
EXCEPINFO *ei,
UINT *arg_err)
+{
- DummyDispatch *This = impl_from_IDispatch(iface);
- CHECK_EXPECT(dispatch_invoke);
- ok(dispid == DISPID_VALUE, "got dispid %d\n", dispid);
- ok(IsEqualIID(riid, &IID_NULL), "go riid %s\n", wine_dbgstr_guid(riid));
- ok(wFlags == DISPATCH_PROPERTYGET, "Flags wrong\n");
- ok(params->rgvarg == NULL, "got %p\n", params->rgvarg);
- ok(params->rgdispidNamedArgs == NULL, "got %p\n", params->rgdispidNamedArgs);
- ok(params->cArgs == 0, "got %d\n", params->cArgs);
- ok(params->cNamedArgs == 0, "got %d\n", params->cNamedArgs);
- ok(res != NULL, "got %p\n", res);
- ok(V_VT(res) == VT_EMPTY, "got %d\n", V_VT(res));
- ok(ei == NULL, "got %p\n", ei);
- ok(arg_err == NULL, "got %p\n", arg_err);
- if (FAILED(This->result))
return This->result;
- V_VT(res) = This->vt;
- if (This->vt == VT_UI1)
V_UI1(res) = 34;
- else if (This->vt == VT_NULL)
- {
V_VT(res) = VT_NULL;
V_BSTR(res) = NULL;
- }
- else
memset(res, 0, sizeof(*res));
- return S_OK;
+}
+static const IDispatchVtbl DummyDispatch_VTable = +{
- DummyDispatch_QueryInterface,
- DummyDispatch_AddRef,
- DummyDispatch_Release,
- DummyDispatch_GetTypeInfoCount,
- DummyDispatch_GetTypeInfo,
- DummyDispatch_GetIDsOfNames,
- DummyDispatch_Invoke
+};
+static void init_test_dispatch(VARTYPE vt, DummyDispatch *dispatch) +{
- dispatch->IDispatch_iface.lpVtbl = &DummyDispatch_VTable;
- dispatch->vt = vt;
- dispatch->result = S_OK;
+}
typedef struct IRecordInfoImpl { IRecordInfo IRecordInfo_iface; @@ -5607,6 +5751,7 @@ static void test_VarCat(void) HRESULT hres; HRESULT expected_error_num; int cmp;
DummyDispatch dispatch;
CHECKPTR(VarCat);
@@ -5941,6 +6086,78 @@ static void test_VarCat(void) VariantClear(&result); VariantClear(&expected);
- /* Dispatch conversion */
- init_test_dispatch(VT_NULL, &dispatch);
- V_VT(&left) = VT_DISPATCH;
- V_DISPATCH(&left) = &dispatch.IDispatch_iface;
- V_VT(&expected) = VT_BSTR;
- V_BSTR(&expected)= SysAllocString(sz_empty);
- SET_EXPECT(dispatch_invoke);
- hres = VarCat(&left, &right, &result);
- ok(hres == S_OK, "got 0x%08x\n", hres);
- ok(V_VT(&result) == VT_BSTR, "got %d\n", V_VT(&result));
- ok(SysStringLen(V_BSTR(&result)) == 0, "got %d\n", SysStringLen(V_BSTR(&result)));
- CHECK_CALLED(dispatch_invoke);
- ok(VarCmp(&result,&expected,lcid,0) == VARCMP_EQ, "VarCat: DISPATCH concat with EMPTY did not return empty VT_BSTR\n");
This VarCmp doesn't test any more than you tested a few lines earlier. I'd remove it.
- VariantClear(&left);
- VariantClear(&right);
- VariantClear(&result);
- VariantClear(&expected);
- init_test_dispatch(VT_NULL, &dispatch);
- V_VT(&right) = VT_DISPATCH;
- V_DISPATCH(&right) = &dispatch.IDispatch_iface;
- SET_EXPECT(dispatch_invoke);
- hres = VarCat(&left, &right, &result);
- ok(hres == S_OK, "got 0x%08x\n", hres);
- ok(V_VT(&result) == VT_BSTR, "got %d\n", V_VT(&result));
- ok(SysStringLen(V_BSTR(&result)) == 0, "got %d\n", SysStringLen(V_BSTR(&result)));
- CHECK_CALLED(dispatch_invoke);
- ok(VarCmp(&result,&expected,lcid,0) == VARCMP_EQ, "VarCat: DISPATCH concat with EMPTY did not return empty VT_BSTR\n");
Same here.
- VariantClear(&left);
- VariantClear(&right);
- VariantClear(&result);
- VariantClear(&expected);
- init_test_dispatch(VT_UI1, &dispatch);
- V_VT(&right) = VT_DISPATCH;
- V_DISPATCH(&right) = &dispatch.IDispatch_iface;
- V_VT(&expected) = VT_BSTR;
- V_VT(&left) = VT_BSTR;
- V_BSTR(&left) = SysAllocString(sz12);
- V_BSTR(&expected) = SysAllocString(sz1234);
- SET_EXPECT(dispatch_invoke);
- hres = pVarCat(&left,&right,&result);
- ok(hres == S_OK, "VarCat failed with error 0x%08x\n", hres);
- CHECK_CALLED(dispatch_invoke);
- ok(VarCmp(&result,&expected,lcid,0) == VARCMP_EQ, "VarCat: VT_DISPATCH concat with VT_BSTR failed to return correct result\n");
I'd replace that with explicit result tests, using strcmp_wa().
- VariantClear(&left);
- VariantClear(&right);
- VariantClear(&result);
- VariantClear(&expected);
- init_test_dispatch(VT_NULL, &dispatch);
- dispatch.result = E_OUTOFMEMORY;
- V_VT(&right) = VT_DISPATCH;
- V_DISPATCH(&right) = &dispatch.IDispatch_iface;
- SET_EXPECT(dispatch_invoke);
- hres = VarCat(&left, &right, &result);
- todo_wine ok(hres == E_OUTOFMEMORY, "got 0x%08x\n", hres);
- CHECK_CALLED(dispatch_invoke);
This test shows that there is still something wrong with error handling with your patch.
- VariantClear(&left);
- VariantClear(&right);
- VariantClear(&result);
- VariantClear(&expected);
- /* Test boolean conversion */ V_VT(&left) = VT_BOOL; V_BOOL(&left) = VARIANT_TRUE;
diff --git a/dlls/oleaut32/variant.c b/dlls/oleaut32/variant.c index 17f753a..aa86be0 100644 --- a/dlls/oleaut32/variant.c +++ b/dlls/oleaut32/variant.c @@ -2605,30 +2605,16 @@ HRESULT WINAPI VarCat(LPVARIANT left, LPVARIANT right, LPVARIANT out) else V_BSTR(&bstrvar_left) = SysAllocString(str_false); }
Note that V_BOOL case could also be handled by VariantChangeTypeEx if you used VARIANT_ALPHABOOL flag.
/* Fill with empty string for later concat with right side */
else if (leftvt == VT_NULL)
{
V_VT(&bstrvar_left) = VT_BSTR;
V_BSTR(&bstrvar_left) = SysAllocString(sz_empty);
} else {
/* The value may not be able to be coerced, ie. VT_NULL */ hres = VariantChangeTypeEx(&bstrvar_left,left,0,0,VT_BSTR);
if (hres != S_OK) {
VariantClear(&bstrvar_left);
VariantClear(&bstrvar_right);
if (leftvt == VT_NULL && (rightvt == VT_EMPTY ||
rightvt == VT_NULL || rightvt == VT_I2 ||
rightvt == VT_I4 || rightvt == VT_R4 ||
rightvt == VT_R8 || rightvt == VT_CY ||
rightvt == VT_DATE || rightvt == VT_BSTR ||
rightvt == VT_BOOL || rightvt == VT_DECIMAL ||
rightvt == VT_I1 || rightvt == VT_UI1 ||
rightvt == VT_UI2 || rightvt == VT_UI4 ||
rightvt == VT_I8 || rightvt == VT_UI8 ||
rightvt == VT_INT || rightvt == VT_UINT))
return DISP_E_BADVARTYPE;
Removing dead code should be a separated patch.
if (hres != S_OK && hres != DISP_E_TYPEMISMATCH) return hres;
else if(FAILED(hres))
{
V_VT(&bstrvar_left) = VT_BSTR;
V_BSTR(&bstrvar_left) = SysAllocString(sz_empty); }
Something like:
if (hres == DISP_E_TYPEMISMATCH) { // set empty string }else if(hres != S_OK) return hres;
would be cleaner. However, more importantly, this needs more tests. There are more cases than IDispatch when your patch changes behaviour, so we need to make sure what behaviour is desired first.
} }
@@ -2645,30 +2631,16 @@ HRESULT WINAPI VarCat(LPVARIANT left, LPVARIANT right, LPVARIANT out) else V_BSTR(&bstrvar_right) = SysAllocString(str_false); }
/* Fill with empty string for later concat with right side */
else if (rightvt == VT_NULL)
{
V_VT(&bstrvar_right) = VT_BSTR;
V_BSTR(&bstrvar_right) = SysAllocString(sz_empty);
} else {
/* The value may not be able to be coerced, ie. VT_NULL */ hres = VariantChangeTypeEx(&bstrvar_right,right,0,0,VT_BSTR);
if (hres != S_OK) {
VariantClear(&bstrvar_left);
VariantClear(&bstrvar_right);
if (rightvt == VT_NULL && (leftvt == VT_EMPTY ||
leftvt == VT_NULL || leftvt == VT_I2 ||
leftvt == VT_I4 || leftvt == VT_R4 ||
leftvt == VT_R8 || leftvt == VT_CY ||
leftvt == VT_DATE || leftvt == VT_BSTR ||
leftvt == VT_BOOL || leftvt == VT_DECIMAL ||
leftvt == VT_I1 || leftvt == VT_UI1 ||
leftvt == VT_UI2 || leftvt == VT_UI4 ||
leftvt == VT_I8 || leftvt == VT_UI8 ||
leftvt == VT_INT || leftvt == VT_UINT))
return DISP_E_BADVARTYPE;
if (hres != S_OK && hres != DISP_E_TYPEMISMATCH) return hres;
else if(FAILED(hres))
{
V_VT(&bstrvar_right) = VT_BSTR;
V_BSTR(&bstrvar_right) = SysAllocString(sz_empty); } } }
-- 1.9.1
Same for those changes: remove dead code in separated patch, add tests and consider using VARIANT_ALPHABOOL.
Thanks, Jacek