diff --git a/dlls/oleaut32/variant.c b/dlls/oleaut32/variant.c index e8db49fc47b..f57f0df77b2 100644 --- a/dlls/oleaut32/variant.c +++ b/dlls/oleaut32/variant.c @@ -2914,58 +2914,168 @@ HRESULT WINAPI VarAdd(LPVARIANT left, LPVARIANT right, LPVARIANT result) /********************************************************************** * VarMul [OLEAUT32.156] * + * Multiply 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 VarMul up to and including WinXP dosn't like as input variants + * I1, UI2, UI4, UI8, INT and UINT. But it can multiply apples with oranges. + * + * Native VarMul 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 VarMul(LPVARIANT left, LPVARIANT right, LPVARIANT result) { - HRESULT rc = E_FAIL; - VARTYPE lvt,rvt,resvt; - VARIANT lv,rv; - BOOL found; + HRESULT hres; + VARTYPE lvt, rvt, resvt, tvt; + VARIANT lv, rv, tv; + double r8res; + + /* Variant priority for coercion. Sorted from lowest to highest. + VT_ERROR shows an invalid input variant type. */ + enum coerceprio { vt_UI1 = 0, vt_I2, vt_I4, vt_I8, vt_CY, vt_R4, vt_R8, + vt_DECIMAL, vt_NULL, vt_ERROR }; + /* Mapping from priority to variant type. Keep in sync with coerceprio! */ + VARTYPE prio2vt[] = { VT_UI1, VT_I2, VT_I4, VT_I8, VT_CY, VT_R4, VT_R8, + VT_DECIMAL, VT_NULL, VT_ERROR }; + + /* 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_UI1, vt_NULL, vt_I2, vt_I4, vt_R4, + /* VT_R8, VT_CY, VT_DATE, VT_BSTR, VT_DISPATCH */ + vt_R8, vt_CY, vt_R8, vt_R8, vt_ERROR, + /* 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 + }; TRACE("(%p->(%s%s),%p->(%s%s),%p)\n", left, debugstr_VT(left), - debugstr_VF(left), right, debugstr_VT(right), debugstr_VF(right), result); + debugstr_VF(left), right, debugstr_VT(right), debugstr_VF(right), + result); - VariantInit(&lv);VariantInit(&rv); + VariantInit(&lv); + VariantInit(&rv); + VariantInit(&tv); lvt = V_VT(left)&VT_TYPEMASK; rvt = V_VT(right)&VT_TYPEMASK; - found = FALSE;resvt=VT_VOID; - if (((1< 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; } - if (!found && (((1< coerce[rvt]) { + resvt = prio2vt[coerce[lvt]]; + tvt = prio2vt[coerce[rvt]]; + } else { + resvt = prio2vt[coerce[rvt]]; + tvt = prio2vt[coerce[lvt]]; } + + /* Special cases where the result variant type is defined by both + input variants and not only that with the highest priority */ + if (resvt == VT_R4 && (tvt == VT_CY || tvt == VT_I8 || tvt == VT_I4)) + resvt = VT_R8; + if (lvt == VT_EMPTY && rvt == VT_EMPTY) + resvt = VT_I2; + + /* For overflow detection use the biggest compatible type for the + multiplication */ switch (resvt) { - case VT_R8: - V_VT(result) = resvt; - V_R8(result) = V_R8(&lv) * V_R8(&rv); - rc = S_OK; - break; - case VT_I4: - V_VT(result) = resvt; - V_I4(result) = V_I4(&lv) * V_I4(&rv); - rc = S_OK; - break; + case VT_ERROR: + hres = DISP_E_BADVARTYPE; + goto end; + case VT_NULL: + hres = S_OK; + V_VT(result) = VT_NULL; + goto end; + case VT_UI1: + case VT_I2: + case VT_I4: + case VT_I8: + tvt = VT_I8; + break; + case VT_R4: + tvt = VT_R8; + break; + default: + tvt = resvt; } - TRACE("returning 0x%8lx (%s%s),%g\n", rc, debugstr_VT(result), - debugstr_VF(result), V_VT(result) == VT_R8 ? V_R8(result) : (double)V_I4(result)); - return rc; + + /* 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; + + /* Do the math */ + hres = S_OK; + V_VT(&tv) = tvt; + V_VT(result) = resvt; + switch (tvt) { + case VT_DECIMAL: + hres = VarDecMul(&V_DECIMAL(&lv), &V_DECIMAL(&rv), + &V_DECIMAL(result)); + goto end; + case VT_CY: + hres = VarCyMul(V_CY(&lv), V_CY(&rv), &V_CY(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 (rvt != 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]; + } + } else + hres = VariantCopy(result, &tv); + +end: + if (hres != S_OK) { + V_VT(result) = VT_EMPTY; + V_I4(result) = 0; /* No V_EMPTY */ + } + VariantClear(&lv); + VariantClear(&rv); + VariantClear(&tv); + TRACE("returning 0x%8lx (variant type %s)\n", hres, debugstr_VT(result)); + return hres; } /**********************************************************************