From: Francis De Brabandere <francisdb@gmail.com> For VT_DISPATCH arguments, try to resolve the default value via DISPID_VALUE and map the failure mode to the same VBScript error code native cscript raises: err 91 for a NULL dispatch (Nothing), err 438 when the object has no default member, err 450 when the default member exists but needs arguments. If the default resolves to an array, use it; if it resolves to a scalar, that is a type mismatch. All other non-array variant types (scalars) collapse into a plain type-mismatch via the default arm. --- dlls/vbscript/global.c | 94 ++++++++++++++++++++++++++++--------- dlls/vbscript/tests/api.vbs | 43 ++++++++--------- 2 files changed, 91 insertions(+), 46 deletions(-) diff --git a/dlls/vbscript/global.c b/dlls/vbscript/global.c index bd949f7b370..214fc27b5d3 100644 --- a/dlls/vbscript/global.c +++ b/dlls/vbscript/global.c @@ -1166,8 +1166,27 @@ static HRESULT Global_Timer(BuiltinDisp *This, VARIANT *arg, unsigned args_cnt, } +static HRESULT bound_arg_resolve_dispatch(script_ctx_t *ctx, IDispatch *disp, + VARIANT *out) +{ + DISPPARAMS dp = {0}; + HRESULT hres; + + if(!disp) + return MAKE_VBSERROR(VBSE_OBJECT_VARIABLE_NOT_SET); + + VariantInit(out); + hres = disp_call(ctx, disp, DISPID_VALUE, TRUE, &dp, out); + if(hres == DISP_E_MEMBERNOTFOUND) + return MAKE_VBSERROR(VBSE_OLE_NO_PROP_OR_METHOD); + if(hres == DISP_E_BADPARAMCOUNT || hres == DISP_E_PARAMNOTOPTIONAL) + return MAKE_VBSERROR(VBSE_FUNC_ARITY_MISMATCH); + return hres; +} + static HRESULT Global_LBound(BuiltinDisp *This, VARIANT *arg, unsigned args_cnt, VARIANT *res) { + VARIANT default_value; SAFEARRAY *sa; HRESULT hres; LONG lbound; @@ -1177,6 +1196,8 @@ static HRESULT Global_LBound(BuiltinDisp *This, VARIANT *arg, unsigned args_cnt, TRACE("%s %s\n", debugstr_variant(arg), args_cnt == 2 ? debugstr_variant(arg + 1) : "1"); + V_VT(&default_value) = VT_EMPTY; + switch(V_VT(arg)) { case VT_VARIANT|VT_ARRAY: sa = V_ARRAY(arg); @@ -1184,34 +1205,48 @@ static HRESULT Global_LBound(BuiltinDisp *This, VARIANT *arg, unsigned args_cnt, case VT_VARIANT|VT_ARRAY|VT_BYREF: sa = *V_ARRAYREF(arg); break; - case VT_EMPTY: - case VT_NULL: - return MAKE_VBSERROR(VBSE_TYPE_MISMATCH); + case VT_DISPATCH: + hres = bound_arg_resolve_dispatch(This->ctx, V_DISPATCH(arg), &default_value); + if(FAILED(hres)) + return hres; + if(V_VT(&default_value) == (VT_VARIANT|VT_ARRAY)) + sa = V_ARRAY(&default_value); + else if(V_VT(&default_value) == (VT_VARIANT|VT_ARRAY|VT_BYREF)) + sa = *V_ARRAYREF(&default_value); + else { + VariantClear(&default_value); + return MAKE_VBSERROR(VBSE_TYPE_MISMATCH); + } + break; default: - FIXME("arg %s not supported\n", debugstr_variant(arg)); - return E_NOTIMPL; + return MAKE_VBSERROR(VBSE_TYPE_MISMATCH); } - if(!sa) - return MAKE_VBSERROR(VBSE_OUT_OF_BOUNDS); + if(!sa) { + hres = MAKE_VBSERROR(VBSE_OUT_OF_BOUNDS); + goto done; + } if(args_cnt == 2) { hres = to_int(arg + 1, &dim); if(FAILED(hres)) - return hres; + goto done; }else { dim = 1; } hres = SafeArrayGetLBound(sa, dim, &lbound); - if(FAILED(hres)) - return hres; + if(SUCCEEDED(hres)) + hres = return_int(res, lbound); - return return_int(res, lbound); +done: + VariantClear(&default_value); + return hres; } static HRESULT Global_UBound(BuiltinDisp *This, VARIANT *arg, unsigned args_cnt, VARIANT *res) { + VARIANT default_value; SAFEARRAY *sa; HRESULT hres; LONG ubound; @@ -1221,6 +1256,8 @@ static HRESULT Global_UBound(BuiltinDisp *This, VARIANT *arg, unsigned args_cnt, TRACE("%s %s\n", debugstr_variant(arg), args_cnt == 2 ? debugstr_variant(arg + 1) : "1"); + V_VT(&default_value) = VT_EMPTY; + switch(V_VT(arg)) { case VT_VARIANT|VT_ARRAY: sa = V_ARRAY(arg); @@ -1228,30 +1265,43 @@ static HRESULT Global_UBound(BuiltinDisp *This, VARIANT *arg, unsigned args_cnt, case VT_VARIANT|VT_ARRAY|VT_BYREF: sa = *V_ARRAYREF(arg); break; - case VT_EMPTY: - case VT_NULL: - return MAKE_VBSERROR(VBSE_TYPE_MISMATCH); + case VT_DISPATCH: + hres = bound_arg_resolve_dispatch(This->ctx, V_DISPATCH(arg), &default_value); + if(FAILED(hres)) + return hres; + if(V_VT(&default_value) == (VT_VARIANT|VT_ARRAY)) + sa = V_ARRAY(&default_value); + else if(V_VT(&default_value) == (VT_VARIANT|VT_ARRAY|VT_BYREF)) + sa = *V_ARRAYREF(&default_value); + else { + VariantClear(&default_value); + return MAKE_VBSERROR(VBSE_TYPE_MISMATCH); + } + break; default: - FIXME("arg %s not supported\n", debugstr_variant(arg)); - return E_NOTIMPL; + return MAKE_VBSERROR(VBSE_TYPE_MISMATCH); } - if(!sa) - return MAKE_VBSERROR(VBSE_OUT_OF_BOUNDS); + if(!sa) { + hres = MAKE_VBSERROR(VBSE_OUT_OF_BOUNDS); + goto done; + } if(args_cnt == 2) { hres = to_int(arg + 1, &dim); if(FAILED(hres)) - return hres; + goto done; }else { dim = 1; } hres = SafeArrayGetUBound(sa, dim, &ubound); - if(FAILED(hres)) - return hres; + if(SUCCEEDED(hres)) + hres = return_int(res, ubound); - return return_int(res, ubound); +done: + VariantClear(&default_value); + return hres; } static HRESULT Global_RGB(BuiltinDisp *This, VARIANT *arg, unsigned args_cnt, VARIANT *res) diff --git a/dlls/vbscript/tests/api.vbs b/dlls/vbscript/tests/api.vbs index d62efc419d5..1a8e79c619e 100644 --- a/dlls/vbscript/tests/api.vbs +++ b/dlls/vbscript/tests/api.vbs @@ -347,25 +347,25 @@ sub testLBoundError() call ok(Err.number = 13, "Err.number = " & Err.number) call Err.clear() call LBound(42) - todo_wine_ok Err.number = 13, "LBound(int) Err.number = " & Err.number + call ok(Err.number = 13, "LBound(int) Err.number = " & Err.number) call Err.clear() call LBound("str") - todo_wine_ok Err.number = 13, "LBound(string) Err.number = " & Err.number + call ok(Err.number = 13, "LBound(string) Err.number = " & Err.number) call Err.clear() call LBound(True) - todo_wine_ok Err.number = 13, "LBound(bool) Err.number = " & Err.number + call ok(Err.number = 13, "LBound(bool) Err.number = " & Err.number) call Err.clear() call LBound(3.14) - todo_wine_ok Err.number = 13, "LBound(double) Err.number = " & Err.number + call ok(Err.number = 13, "LBound(double) Err.number = " & Err.number) call Err.clear() call LBound(nothingObj) - todo_wine_ok Err.number = 91, "LBound(Nothing) Err.number = " & Err.number + call ok(Err.number = 91, "LBound(Nothing) Err.number = " & Err.number) call Err.clear() call LBound(emptyObj) - todo_wine_ok Err.number = 438, "LBound(no-default-object) Err.number = " & Err.number + call ok(Err.number = 438, "LBound(no-default-object) Err.number = " & Err.number) call Err.clear() call LBound(dictObj) - todo_wine_ok Err.number = 450, "LBound(default-needs-arg) Err.number = " & Err.number + call ok(Err.number = 450, "LBound(default-needs-arg) Err.number = " & Err.number) call Err.clear() call LBound(arr, 1, 2) call ok(Err.number = 450, "Err.number = " & Err.number) @@ -389,25 +389,25 @@ sub testUBoundError() call ok(Err.number = 13, "Err.number = " & Err.number) call Err.clear() call UBound(42) - todo_wine_ok Err.number = 13, "UBound(int) Err.number = " & Err.number + call ok(Err.number = 13, "UBound(int) Err.number = " & Err.number) call Err.clear() call UBound("str") - todo_wine_ok Err.number = 13, "UBound(string) Err.number = " & Err.number + call ok(Err.number = 13, "UBound(string) Err.number = " & Err.number) call Err.clear() call UBound(True) - todo_wine_ok Err.number = 13, "UBound(bool) Err.number = " & Err.number + call ok(Err.number = 13, "UBound(bool) Err.number = " & Err.number) call Err.clear() call UBound(3.14) - todo_wine_ok Err.number = 13, "UBound(double) Err.number = " & Err.number + call ok(Err.number = 13, "UBound(double) Err.number = " & Err.number) call Err.clear() call UBound(nothingObj) - todo_wine_ok Err.number = 91, "UBound(Nothing) Err.number = " & Err.number + call ok(Err.number = 91, "UBound(Nothing) Err.number = " & Err.number) call Err.clear() call UBound(emptyObj) - todo_wine_ok Err.number = 438, "UBound(no-default-object) Err.number = " & Err.number + call ok(Err.number = 438, "UBound(no-default-object) Err.number = " & Err.number) call Err.clear() call UBound(dictObj) - todo_wine_ok Err.number = 450, "UBound(default-needs-arg) Err.number = " & Err.number + call ok(Err.number = 450, "UBound(default-needs-arg) Err.number = " & Err.number) call Err.clear() call UBound(arr, 1, 2) call ok(Err.number = 450, "Err.number = " & Err.number) @@ -433,21 +433,16 @@ End Class sub testBoundDispatchDefault() Dim arrObj : Set arrObj = new ArrayDefaultClass Dim scalarObj : Set scalarObj = new ScalarDefaultClass - Dim r - on error resume next - r = -1 : r = LBound(arrObj) - todo_wine_ok r = 0 and Err.number = 0, "LBound(array-default) r=" & r & " err=" & Err.number - Err.Clear() - r = -1 : r = UBound(arrObj) - todo_wine_ok r = 2 and Err.number = 0, "UBound(array-default) r=" & r & " err=" & Err.number + call ok(LBound(arrObj) = 0, "LBound(array-default) = " & LBound(arrObj)) + call ok(UBound(arrObj) = 2, "UBound(array-default) = " & UBound(arrObj)) - Err.Clear() + on error resume next call LBound(scalarObj) - todo_wine_ok Err.number = 13, "LBound(scalar-default) Err.number = " & Err.number + call ok(Err.number = 13, "LBound(scalar-default) Err.number = " & Err.number) Err.Clear() call UBound(scalarObj) - todo_wine_ok Err.number = 13, "UBound(scalar-default) Err.number = " & Err.number + call ok(Err.number = 13, "UBound(scalar-default) Err.number = " & Err.number) end sub call testBoundDispatchDefault() -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10895