[PATCH v10 0/2] MR10895: vbscript: Resolve UBound/LBound default member and match native errors
Resolve VT_DISPATCH arguments via DISPID_VALUE so an object whose default member returns an array works, and map the failure modes to native error codes (91 for Nothing, 438 for no default, 450 for default needing args, 13 for default returning a scalar). Scalar arguments collapse to err 13. -- v10: vbscript: Match native error codes for non-array arguments to UBound/LBound. https://gitlab.winehq.org/wine/wine/-/merge_requests/10895
From: Francis De Brabandere <francisdb@gmail.com> --- dlls/vbscript/tests/api.vbs | 82 +++++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) diff --git a/dlls/vbscript/tests/api.vbs b/dlls/vbscript/tests/api.vbs index 8cbd5824b39..d62efc419d5 100644 --- a/dlls/vbscript/tests/api.vbs +++ b/dlls/vbscript/tests/api.vbs @@ -332,6 +332,9 @@ Call ok(Lbound(arr2, 1) = 0, "Lbound(x) = " & Lbound(x)) Call ok(Lbound(arr2, 2) = 0, "Lbound(x) = " & Lbound(x)) sub testLBoundError() + Dim nothingObj : Set nothingObj = Nothing + Dim emptyObj : Set emptyObj = new EmptyClass + Dim dictObj : Set dictObj = CreateObject("Scripting.Dictionary") on error resume next call Err.clear() call LBound() @@ -343,6 +346,27 @@ sub testLBoundError() call LBound(Null) 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 Err.clear() + call LBound("str") + todo_wine_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 Err.clear() + call LBound(3.14) + todo_wine_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 Err.clear() + call LBound(emptyObj) + todo_wine_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 Err.clear() call LBound(arr, 1, 2) call ok(Err.number = 450, "Err.number = " & Err.number) if isEnglishLang then call ok(Err.description = "Wrong number of arguments or invalid property assignment", _ @@ -350,6 +374,9 @@ sub testLBoundError() end sub sub testUBoundError() + Dim nothingObj : Set nothingObj = Nothing + Dim emptyObj : Set emptyObj = new EmptyClass + Dim dictObj : Set dictObj = CreateObject("Scripting.Dictionary") on error resume next call Err.clear() call UBound() @@ -361,6 +388,27 @@ sub testUBoundError() call UBound(Null) 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 Err.clear() + call UBound("str") + todo_wine_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 Err.clear() + call UBound(3.14) + todo_wine_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 Err.clear() + call UBound(emptyObj) + todo_wine_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 Err.clear() call UBound(arr, 1, 2) call ok(Err.number = 450, "Err.number = " & Err.number) if isEnglishLang then call ok(Err.description = "Wrong number of arguments or invalid property assignment", _ @@ -370,6 +418,40 @@ end sub call testLBoundError() call testUBoundError() +Class ArrayDefaultClass + Public Default Function GetArr + GetArr = Array(10, 20, 30) + End Function +End Class + +Class ScalarDefaultClass + Public Default Function GetIt + GetIt = 42 + End Function +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 + + Err.Clear() + call LBound(scalarObj) + todo_wine_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 +end sub + +call testBoundDispatchDefault() + sub testBoundUninitArray() Dim u() on error resume next -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10895
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 | 86 +++++++++++++++++++++++++------------ dlls/vbscript/tests/api.vbs | 43 ++++++++----------- 2 files changed, 77 insertions(+), 52 deletions(-) diff --git a/dlls/vbscript/global.c b/dlls/vbscript/global.c index bd949f7b370..b077b022c80 100644 --- a/dlls/vbscript/global.c +++ b/dlls/vbscript/global.c @@ -1168,6 +1168,8 @@ static HRESULT Global_Timer(BuiltinDisp *This, VARIANT *arg, unsigned args_cnt, static HRESULT Global_LBound(BuiltinDisp *This, VARIANT *arg, unsigned args_cnt, VARIANT *res) { + VARIANT default_value; + VARIANT *array_arg = arg; SAFEARRAY *sa; HRESULT hres; LONG lbound; @@ -1177,41 +1179,56 @@ 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"); - switch(V_VT(arg)) { + V_VT(&default_value) = VT_EMPTY; + + if(V_VT(arg) == VT_DISPATCH) { + DISPPARAMS dp = {0}; + if(!V_DISPATCH(arg)) + return MAKE_VBSERROR(VBSE_OBJECT_VARIABLE_NOT_SET); + hres = disp_call(This->ctx, V_DISPATCH(arg), DISPID_VALUE, TRUE, &dp, &default_value); + if(FAILED(hres)) + return hres; + array_arg = &default_value; + } + + switch(V_VT(array_arg)) { case VT_VARIANT|VT_ARRAY: - sa = V_ARRAY(arg); + sa = V_ARRAY(array_arg); break; case VT_VARIANT|VT_ARRAY|VT_BYREF: - sa = *V_ARRAYREF(arg); + sa = *V_ARRAYREF(array_arg); break; - case VT_EMPTY: - case VT_NULL: - return MAKE_VBSERROR(VBSE_TYPE_MISMATCH); default: - FIXME("arg %s not supported\n", debugstr_variant(arg)); - return E_NOTIMPL; + VariantClear(&default_value); + 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; + VARIANT *array_arg = arg; SAFEARRAY *sa; HRESULT hres; LONG ubound; @@ -1221,37 +1238,50 @@ 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"); - switch(V_VT(arg)) { + V_VT(&default_value) = VT_EMPTY; + + if(V_VT(arg) == VT_DISPATCH) { + DISPPARAMS dp = {0}; + if(!V_DISPATCH(arg)) + return MAKE_VBSERROR(VBSE_OBJECT_VARIABLE_NOT_SET); + hres = disp_call(This->ctx, V_DISPATCH(arg), DISPID_VALUE, TRUE, &dp, &default_value); + if(FAILED(hres)) + return hres; + array_arg = &default_value; + } + + switch(V_VT(array_arg)) { case VT_VARIANT|VT_ARRAY: - sa = V_ARRAY(arg); + sa = V_ARRAY(array_arg); break; case VT_VARIANT|VT_ARRAY|VT_BYREF: - sa = *V_ARRAYREF(arg); + sa = *V_ARRAYREF(array_arg); break; - case VT_EMPTY: - case VT_NULL: - return MAKE_VBSERROR(VBSE_TYPE_MISMATCH); default: - FIXME("arg %s not supported\n", debugstr_variant(arg)); - return E_NOTIMPL; + VariantClear(&default_value); + 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
On Mon May 18 15:17:31 2026 +0000, Francis De Brabandere wrote:
changed this line in [version 10 of the diff](/wine/wine/-/merge_requests/10895/diffs?diff_id=268362&start_sha=df01557c3074a6d48599149f494077e993d8e0ba#f705ff2124202b08096a71599eedf1d31dff92a3_1177_1169) inlined
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/10895#note_140405
This merge request was approved by Jacek Caban. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10895
participants (3)
-
Francis De Brabandere -
Francis De Brabandere (@francisdb) -
Jacek Caban (@jacek)