From 948dd66603533acd6f74c03d00d34e28936158e2 Mon Sep 17 00:00:00 2001 From: Michael Stefaniuc Date: Sun, 29 May 2005 20:00:31 +0000 Subject: [PATCH] - Reimplement VarAdd, had missing functionality and wrong behaviour. - Fix a typo in VarMul. --- dlls/oleaut32/variant.c | 338 +++++++++++++++++++++++------------------------- 1 file changed, 164 insertions(+), 174 deletions(-) diff --git a/dlls/oleaut32/variant.c b/dlls/oleaut32/variant.c index f57f0df77b2..d7e7f3cad1c 100644 --- a/dlls/oleaut32/variant.c +++ b/dlls/oleaut32/variant.c @@ -2717,198 +2717,188 @@ HRESULT WINAPI VarAnd(LPVARIANT left, LPVARIANT right, LPVARIANT result) /********************************************************************** * VarAdd [OLEAUT32.141] - * FIXME: From MSDN: If ... Then - * Both expressions are of the string type Concatenated. - * One expression is a string type and the other a character Addition. - * One expression is numeric and the other is a string Addition. - * Both expressions are numeric Addition. - * Either expression is NULL NULL is returned. - * Both expressions are empty Integer subtype is returned. * + * Add two variants. + * + * PARAMS + * left [I] First variant + * right [I] Second variant + * result [O] Result variant + * + * RETURNS + * Success: S_OK. + * Failure: An HRESULT error code indicating the error. + * + * NOTES + * Native VarAdd up to and including WinXP dosn't like as input variants + * I1, UI2, UI4, UI8, INT and UINT. + * + * Native VarAdd dosn't check for NULL in/out pointers and crashes. We do the + * same here. + * + * FIXME + * Overflow checking for R8 (double) overflow. Return DISP_E_OVERFLOW in that + * case. */ HRESULT WINAPI VarAdd(LPVARIANT left, LPVARIANT right, LPVARIANT result) { - HRESULT rc = E_FAIL; - - TRACE("(%p->(%s%s),%p->(%s%s),%p)\n", left, debugstr_VT(left), - debugstr_VF(left), right, debugstr_VT(right), debugstr_VF(right), result); - - if ((V_VT(left)&VT_TYPEMASK) == VT_EMPTY) - return VariantCopy(result,right); + HRESULT hres; + VARTYPE lvt, rvt, resvt, tvt; + VARIANT lv, rv, tv; + double r8res; - if ((V_VT(right)&VT_TYPEMASK) == VT_EMPTY) - return VariantCopy(result,left); + /* Variant priority for coercion. Sorted from lowest to highest. + VT_ERROR shows an invalid input variant type. */ + enum coerceprio { vt_EMPTY, vt_UI1, vt_I2, vt_I4, vt_I8, vt_BSTR,vt_R4, + vt_R8, vt_CY, vt_DATE, vt_DECIMAL, vt_DISPATCH, vt_NULL, + vt_ERROR }; + /* Mapping from priority to variant type. Keep in sync with coerceprio! */ + VARTYPE prio2vt[] = { VT_EMPTY, VT_UI1, VT_I2, VT_I4, VT_I8, VT_BSTR, VT_R4, + VT_R8, VT_CY, VT_DATE, VT_DECIMAL, VT_DISPATCH, + VT_NULL, VT_ERROR }; - /* check if we add doubles */ - if (((V_VT(left)&VT_TYPEMASK) == VT_R8) || ((V_VT(right)&VT_TYPEMASK) == VT_R8)) { - BOOL lOk = TRUE; - BOOL rOk = TRUE; - double lVal = -1; - double rVal = -1; - double res = -1; + /* Mapping for coercion from input variant to priority of result variant. */ + static VARTYPE coerce[] = { + /* VT_EMPTY, VT_NULL, VT_I2, VT_I4, VT_R4 */ + vt_EMPTY, vt_NULL, vt_I2, vt_I4, vt_R4, + /* VT_R8, VT_CY, VT_DATE, VT_BSTR, VT_DISPATCH */ + vt_R8, vt_CY, vt_DATE, vt_BSTR, vt_DISPATCH, + /* VT_ERROR, VT_BOOL, VT_VARIANT, VT_UNKNOWN, VT_DECIMAL */ + vt_ERROR, vt_I2, vt_ERROR, vt_ERROR, vt_DECIMAL, + /* 15, VT_I1, VT_UI1, VT_UI2, VT_UI4 VT_I8 */ + vt_ERROR, vt_ERROR, vt_UI1, vt_ERROR, vt_ERROR, vt_I8 + }; - lOk = TRUE; - switch (V_VT(left)&VT_TYPEMASK) { - case VT_I1 : lVal = V_I1(left); break; - case VT_I2 : lVal = V_I2(left); break; - case VT_I4 : - case VT_INT : lVal = V_I4(left); break; - case VT_UI1 : lVal = V_UI1(left); break; - case VT_UI2 : lVal = V_UI2(left); break; - case VT_UI4 : - case VT_UINT : lVal = V_UI4(left); break; - case VT_R4 : lVal = V_R4(left); break; - case VT_R8 : lVal = V_R8(left); break; - case VT_NULL : lVal = 0.0; break; - default: lOk = FALSE; - } + TRACE("(%p->(%s%s),%p->(%s%s),%p)\n", left, debugstr_VT(left), + debugstr_VF(left), right, debugstr_VT(right), debugstr_VF(right), + result); - rOk = TRUE; - switch (V_VT(right)&VT_TYPEMASK) { - case VT_I1 : rVal = V_I1(right); break; - case VT_I2 : rVal = V_I2(right); break; - case VT_I4 : - case VT_INT : rVal = V_I4(right); break; - case VT_UI1 : rVal = V_UI1(right); break; - case VT_UI2 : rVal = V_UI2(right); break; - case VT_UI4 : - case VT_UINT : rVal = V_UI4(right); break; - case VT_R4 : rVal = V_R4(right);break; - case VT_R8 : rVal = V_R8(right);break; - case VT_NULL : rVal = 0.0; break; - default: rOk = FALSE; - } + VariantInit(&lv); + VariantInit(&rv); + VariantInit(&tv); + lvt = V_VT(left)&VT_TYPEMASK; + rvt = V_VT(right)&VT_TYPEMASK; - if (lOk && rOk) { - res = (lVal + rVal); - V_VT(result) = VT_R8; - V_R8(result) = res; - rc = S_OK; - } else { - FIXME("Unhandled type pair %d / %d in double addition.\n", - (V_VT(left)&VT_TYPEMASK), - (V_VT(right)&VT_TYPEMASK) - ); - } - return rc; + /* If we have any flag set (VT_ARRAY, VT_VECTOR, etc.) bail out. + Same for any input variant type > VT_I8 */ + if (V_VT(left) & ~VT_TYPEMASK || V_VT(right) & ~VT_TYPEMASK || + lvt > VT_I8 || rvt > VT_I8) { + hres = DISP_E_BADVARTYPE; + goto end; } - /* now check if we add floats. VT_R8 can no longer happen here! */ - if (((V_VT(left)&VT_TYPEMASK) == VT_R4) || ((V_VT(right)&VT_TYPEMASK) == VT_R4)) { - BOOL lOk = TRUE; - BOOL rOk = TRUE; - float lVal = -1; - float rVal = -1; - float res = -1; - - lOk = TRUE; - switch (V_VT(left)&VT_TYPEMASK) { - case VT_I1 : lVal = V_I1(left); break; - case VT_I2 : lVal = V_I2(left); break; - case VT_I4 : - case VT_INT : lVal = V_I4(left); break; - case VT_UI1 : lVal = V_UI1(left); break; - case VT_UI2 : lVal = V_UI2(left); break; - case VT_UI4 : - case VT_UINT : lVal = V_UI4(left); break; - case VT_R4 : lVal = V_R4(left); break; - case VT_NULL : lVal = 0.0; break; - default: lOk = FALSE; - } - - rOk = TRUE; - switch (V_VT(right)&VT_TYPEMASK) { - case VT_I1 : rVal = V_I1(right); break; - case VT_I2 : rVal = V_I2(right); break; - case VT_I4 : - case VT_INT : rVal = V_I4(right); break; - case VT_UI1 : rVal = V_UI1(right); break; - case VT_UI2 : rVal = V_UI2(right); break; - case VT_UI4 : - case VT_UINT : rVal = V_UI4(right); break; - case VT_R4 : rVal = V_R4(right);break; - case VT_NULL : rVal = 0.0; break; - default: rOk = FALSE; - } - - if (lOk && rOk) { - res = (lVal + rVal); - V_VT(result) = VT_R4; - V_R4(result) = res; - rc = S_OK; - } else { - FIXME("Unhandled type pair %d / %d in float addition.\n", - (V_VT(left)&VT_TYPEMASK), - (V_VT(right)&VT_TYPEMASK) - ); - } - return rc; + /* Determine the variant type to coerce to. */ + if (coerce[lvt] > coerce[rvt]) { + resvt = prio2vt[coerce[lvt]]; + tvt = prio2vt[coerce[rvt]]; + } else { + resvt = prio2vt[coerce[rvt]]; + tvt = prio2vt[coerce[lvt]]; } - /* Handle strings as concat */ - if ((V_VT(left)&VT_TYPEMASK) == VT_BSTR && - (V_VT(right)&VT_TYPEMASK) == VT_BSTR) { - V_VT(result) = VT_BSTR; - return VarBstrCat(V_BSTR(left), V_BSTR(right), &V_BSTR(result)); - } else { + /* Special cases where the result variant type is defined by both + input variants and not only that with the highest priority */ + if (resvt == VT_BSTR) { + if (tvt == VT_EMPTY || tvt == VT_BSTR) + resvt = VT_BSTR; + else + resvt = VT_R8; + } + if (resvt == VT_R4 && (tvt == VT_BSTR || tvt == VT_I8 || tvt == VT_I4)) + resvt = VT_R8; - /* Integers */ - BOOL lOk = TRUE; - BOOL rOk = TRUE; - LONGLONG lVal = -1; - LONGLONG rVal = -1; - LONGLONG res = -1; - int resT = 0; /* Testing has shown I2 + I2 == I2, all else - becomes I4 */ + /* For overflow detection use the biggest compatible type for the + addition */ + switch (resvt) { + case VT_ERROR: + hres = DISP_E_BADVARTYPE; + goto end; + case VT_NULL: + hres = S_OK; + V_VT(result) = VT_NULL; + goto end; + case VT_DISPATCH: + FIXME("can not handle variant type VT_DISPATCH\n"); + hres = DISP_E_TYPEMISMATCH; + goto end; + case VT_EMPTY: + resvt = VT_I2; + /* Fall through */ + case VT_UI1: + case VT_I2: + case VT_I4: + case VT_I8: + tvt = VT_I8; + break; + case VT_DATE: + case VT_R4: + tvt = VT_R8; + break; + default: + tvt = resvt; + } - lOk = TRUE; - switch (V_VT(left)&VT_TYPEMASK) { - case VT_I1 : lVal = V_I1(left); resT=VT_I4; break; - case VT_I2 : lVal = V_I2(left); resT=VT_I2; break; - case VT_I4 : - case VT_INT : lVal = V_I4(left); resT=VT_I4; break; - case VT_UI1 : lVal = V_UI1(left); resT=VT_I4; break; - case VT_UI2 : lVal = V_UI2(left); resT=VT_I4; break; - case VT_UI4 : - case VT_UINT : lVal = V_UI4(left); resT=VT_I4; break; - case VT_NULL : lVal = 0; resT = VT_I4; break; - default: lOk = FALSE; - } + /* Now coerce the variants */ + hres = VariantChangeType(&lv, left, 0, tvt); + if (FAILED(hres)) + goto end; + hres = VariantChangeType(&rv, right, 0, tvt); + if (FAILED(hres)) + goto end; - rOk = TRUE; - switch (V_VT(right)&VT_TYPEMASK) { - case VT_I1 : rVal = V_I1(right); resT=VT_I4; break; - case VT_I2 : rVal = V_I2(right); resT=max(VT_I2, resT); break; - case VT_I4 : - case VT_INT : rVal = V_I4(right); resT=VT_I4; break; - case VT_UI1 : rVal = V_UI1(right); resT=VT_I4; break; - case VT_UI2 : rVal = V_UI2(right); resT=VT_I4; break; - case VT_UI4 : - case VT_UINT : rVal = V_UI4(right); resT=VT_I4; break; - case VT_NULL : rVal = 0; resT=VT_I4; break; - default: rOk = FALSE; + /* Do the math */ + hres = S_OK; + V_VT(&tv) = tvt; + V_VT(result) = resvt; + switch (tvt) { + case VT_DECIMAL: + hres = VarDecAdd(&V_DECIMAL(&lv), &V_DECIMAL(&rv), + &V_DECIMAL(result)); + goto end; + case VT_CY: + hres = VarCyAdd(V_CY(&lv), V_CY(&rv), &V_CY(result)); + goto end; + case VT_BSTR: + /* We do not add those, we concatenate them. */ + hres = VarBstrCat(V_BSTR(&lv), V_BSTR(&rv), &V_BSTR(result)); + goto end; + case VT_I8: + /* Overflow detection */ + r8res = (double)V_I8(&lv) + (double)V_I8(&rv); + if (r8res > (double)I8_MAX || r8res < (double)I8_MIN) { + V_VT(result) = VT_R8; + V_R8(result) = r8res; + goto end; + } else + V_I8(&tv) = V_I8(&lv) + V_I8(&rv); + break; + case VT_R8: + /* FIXME: overflow detection */ + V_R8(&tv) = V_R8(&lv) + V_R8(&rv); + break; + default: + ERR("We shouldn't get here! tvt = %d!\n", tvt); + break; + } + if (resvt != tvt) { + if ((hres = VariantChangeType(result, &tv, 0, resvt)) != S_OK) { + /* Overflow! Change to the vartype with the next higher priority */ + resvt = prio2vt[coerce[resvt] + 1]; + hres = VariantChangeType(result, &tv, 0, resvt); } + } else + hres = VariantCopy(result, &tv); - if (lOk && rOk) { - res = (lVal + rVal); - V_VT(result) = resT; - switch (resT) { - case VT_I2 : V_I2(result) = res; break; - case VT_I4 : V_I4(result) = res; break; - default: - FIXME("Unexpected result variant type %x\n", resT); - V_I4(result) = res; - } - rc = S_OK; - - } else { - FIXME("unimplemented part (0x%x + 0x%x)\n",V_VT(left), V_VT(right)); - } +end: + if (hres != S_OK) { + V_VT(result) = VT_EMPTY; + V_I4(result) = 0; /* No V_EMPTY */ } - - TRACE("returning 0x%8lx (%s%s),%ld\n", rc, debugstr_VT(result), - debugstr_VF(result), V_VT(result) == VT_I4 ? V_I4(result) : V_I2(result)); - return rc; + VariantClear(&lv); + VariantClear(&rv); + VariantClear(&tv); + TRACE("returning 0x%8lx (variant type %s)\n", hres, debugstr_VT(result)); + return hres; } /********************************************************************** @@ -3058,7 +3048,7 @@ HRESULT WINAPI VarMul(LPVARIANT left, LPVARIANT right, LPVARIANT result) ERR("We shouldn't get here! tvt = %d!\n", tvt); break; } - if (rvt != tvt) { + if (resvt != tvt) { while ((hres = VariantChangeType(result, &tv, 0, resvt)) != S_OK) { /* Overflow! Change to the vartype with the next higher priority */ resvt = prio2vt[coerce[resvt] + 1]; -- 2.11.4.GIT