From: Francis De Brabandere <francisdb@gmail.com> Native cscript raises VBSE_ILLEGAL_NULL_USE (94) when Null is passed to a Cxxx coercion builtin, and VBSE_OBJECT_VARIABLE_NOT_SET (91) for Nothing. Wine was returning whatever VariantChangeType bubbled up (DISP_E_TYPEMISMATCH -> 13, DISP_E_BADVARTYPE -> 458). Factor the checks into a single helper and run it at the top of each Cxxx builtin. --- dlls/vbscript/global.c | 49 +++++++++++++++++++++++++++++++++--- dlls/vbscript/tests/lang.vbs | 32 +++++++++++------------ 2 files changed, 61 insertions(+), 20 deletions(-) diff --git a/dlls/vbscript/global.c b/dlls/vbscript/global.c index bd949f7b370..60011e2e313 100644 --- a/dlls/vbscript/global.c +++ b/dlls/vbscript/global.c @@ -647,6 +647,17 @@ static HRESULT show_msgbox(script_ctx_t *ctx, BSTR prompt, unsigned type, BSTR o return return_short(res, ret); } +/* Cxxx() coercion functions raise err 94 (Illegal Null use) on Null and + * err 91 (Object variable not set) on Nothing, matching native VBScript. */ +static HRESULT check_coerce_arg(const VARIANT *arg) +{ + if(V_VT(arg) == VT_NULL) + return MAKE_VBSERROR(VBSE_ILLEGAL_NULL_USE); + if(V_VT(arg) == VT_DISPATCH && !V_DISPATCH(arg)) + return MAKE_VBSERROR(VBSE_OBJECT_VARIABLE_NOT_SET); + return S_OK; +} + static HRESULT Global_CCur(BuiltinDisp *This, VARIANT *arg, unsigned args_cnt, VARIANT *res) { VARIANT v; @@ -656,6 +667,10 @@ static HRESULT Global_CCur(BuiltinDisp *This, VARIANT *arg, unsigned args_cnt, V assert(args_cnt == 1); + hres = check_coerce_arg(arg); + if(FAILED(hres)) + return hres; + V_VT(&v) = VT_EMPTY; hres = VariantChangeType(&v, arg, 0, VT_CY); if(FAILED(hres)) @@ -679,6 +694,10 @@ static HRESULT Global_CInt(BuiltinDisp *This, VARIANT *arg, unsigned args_cnt, V assert(args_cnt == 1); + hres = check_coerce_arg(arg); + if(FAILED(hres)) + return hres; + V_VT(&v) = VT_EMPTY; hres = VariantChangeType(&v, arg, 0, VT_I2); if(FAILED(hres)) @@ -701,6 +720,10 @@ static HRESULT Global_CLng(BuiltinDisp *This, VARIANT *arg, unsigned args_cnt, V assert(args_cnt == 1); + hres = check_coerce_arg(arg); + if(FAILED(hres)) + return hres; + hres = to_int(arg, &i); if(FAILED(hres)) return hres; @@ -719,6 +742,10 @@ static HRESULT Global_CBool(BuiltinDisp *This, VARIANT *arg, unsigned args_cnt, assert(args_cnt == 1); + hres = check_coerce_arg(arg); + if(FAILED(hres)) + return hres; + V_VT(&v) = VT_EMPTY; hres = VariantChangeType(&v, arg, VARIANT_LOCALBOOL, VT_BOOL); if(FAILED(hres)) @@ -740,6 +767,10 @@ static HRESULT Global_CByte(BuiltinDisp *This, VARIANT *arg, unsigned args_cnt, assert(args_cnt == 1); + hres = check_coerce_arg(arg); + if(FAILED(hres)) + return hres; + V_VT(&v) = VT_EMPTY; hres = VariantChangeType(&v, arg, VARIANT_LOCALBOOL, VT_UI1); if(FAILED(hres)) @@ -763,8 +794,9 @@ static HRESULT Global_CDate(BuiltinDisp *This, VARIANT *arg, unsigned args_cnt, assert(args_cnt == 1); - if(V_VT(arg) == VT_NULL) - return MAKE_VBSERROR(VBSE_ILLEGAL_NULL_USE); + hres = check_coerce_arg(arg); + if(FAILED(hres)) + return hres; V_VT(&v) = VT_EMPTY; hres = VariantChangeType(&v, arg, 0, VT_DATE); @@ -792,6 +824,10 @@ static HRESULT Global_CDbl(BuiltinDisp *This, VARIANT *arg, unsigned args_cnt, V assert(args_cnt == 1); + hres = check_coerce_arg(arg); + if(FAILED(hres)) + return hres; + V_VT(&v) = VT_EMPTY; hres = VariantChangeType(&v, arg, 0, VT_R8); if(FAILED(hres)) @@ -814,6 +850,10 @@ static HRESULT Global_CSng(BuiltinDisp *This, VARIANT *arg, unsigned args_cnt, V assert(args_cnt == 1); + hres = check_coerce_arg(arg); + if(FAILED(hres)) + return hres; + V_VT(&v) = VT_EMPTY; hres = VariantChangeType(&v, arg, 0, VT_R4); if(FAILED(hres)) @@ -833,8 +873,9 @@ static HRESULT Global_CStr(BuiltinDisp *This, VARIANT *arg, unsigned args_cnt, V TRACE("%s\n", debugstr_variant(arg)); - if(V_VT(arg) == VT_NULL) - return MAKE_VBSERROR(VBSE_ILLEGAL_NULL_USE); + hres = check_coerce_arg(arg); + if(FAILED(hres)) + return hres; hres = to_string(This->ctx->lcid, arg, &str); if(FAILED(hres)) diff --git a/dlls/vbscript/tests/lang.vbs b/dlls/vbscript/tests/lang.vbs index 98e273e1e57..e0ebb71b3b1 100644 --- a/dlls/vbscript/tests/lang.vbs +++ b/dlls/vbscript/tests/lang.vbs @@ -4246,28 +4246,28 @@ Call ok(Err.Number = 11, "division by zero: err.number = " & Err.Number) Err.Clear Dim nullResult nullResult = CLng(Null) -todo_wine_ok Err.Number = 94, "CLng(Null): err.number = " & Err.Number +call ok(Err.Number = 94, "CLng(Null): err.number = " & Err.Number) ' Each Cxxx coercion raises err 94 on Null and err 91 on Nothing. Sub testCoerceNullNothing Dim nothingObj : Set nothingObj = Nothing on error resume next - Err.Clear : call CInt(Null) : todo_wine_ok Err.Number = 94, "CInt(Null) err=" & Err.Number - Err.Clear : call CInt(nothingObj) : todo_wine_ok Err.Number = 91, "CInt(Nothing) err=" & Err.Number - Err.Clear : call CLng(nothingObj) : todo_wine_ok Err.Number = 91, "CLng(Nothing) err=" & Err.Number - Err.Clear : call CBool(Null) : todo_wine_ok Err.Number = 94, "CBool(Null) err=" & Err.Number - Err.Clear : call CBool(nothingObj) : todo_wine_ok Err.Number = 91, "CBool(Nothing) err=" & Err.Number - Err.Clear : call CByte(Null) : todo_wine_ok Err.Number = 94, "CByte(Null) err=" & Err.Number - Err.Clear : call CByte(nothingObj) : todo_wine_ok Err.Number = 91, "CByte(Nothing) err=" & Err.Number - Err.Clear : call CDbl(Null) : todo_wine_ok Err.Number = 94, "CDbl(Null) err=" & Err.Number - Err.Clear : call CDbl(nothingObj) : todo_wine_ok Err.Number = 91, "CDbl(Nothing) err=" & Err.Number - Err.Clear : call CSng(Null) : todo_wine_ok Err.Number = 94, "CSng(Null) err=" & Err.Number - Err.Clear : call CSng(nothingObj) : todo_wine_ok Err.Number = 91, "CSng(Nothing) err=" & Err.Number - Err.Clear : call CCur(Null) : todo_wine_ok Err.Number = 94, "CCur(Null) err=" & Err.Number - Err.Clear : call CCur(nothingObj) : todo_wine_ok Err.Number = 91, "CCur(Nothing) err=" & Err.Number - Err.Clear : call CDate(nothingObj) : todo_wine_ok Err.Number = 91, "CDate(Nothing) err=" & Err.Number - Err.Clear : call CStr(nothingObj) : todo_wine_ok Err.Number = 91, "CStr(Nothing) err=" & Err.Number + Err.Clear : call CInt(Null) : call ok(Err.Number = 94, "CInt(Null) err=" & Err.Number) + Err.Clear : call CInt(nothingObj) : call ok(Err.Number = 91, "CInt(Nothing) err=" & Err.Number) + Err.Clear : call CLng(nothingObj) : call ok(Err.Number = 91, "CLng(Nothing) err=" & Err.Number) + Err.Clear : call CBool(Null) : call ok(Err.Number = 94, "CBool(Null) err=" & Err.Number) + Err.Clear : call CBool(nothingObj) : call ok(Err.Number = 91, "CBool(Nothing) err=" & Err.Number) + Err.Clear : call CByte(Null) : call ok(Err.Number = 94, "CByte(Null) err=" & Err.Number) + Err.Clear : call CByte(nothingObj) : call ok(Err.Number = 91, "CByte(Nothing) err=" & Err.Number) + Err.Clear : call CDbl(Null) : call ok(Err.Number = 94, "CDbl(Null) err=" & Err.Number) + Err.Clear : call CDbl(nothingObj) : call ok(Err.Number = 91, "CDbl(Nothing) err=" & Err.Number) + Err.Clear : call CSng(Null) : call ok(Err.Number = 94, "CSng(Null) err=" & Err.Number) + Err.Clear : call CSng(nothingObj) : call ok(Err.Number = 91, "CSng(Nothing) err=" & Err.Number) + Err.Clear : call CCur(Null) : call ok(Err.Number = 94, "CCur(Null) err=" & Err.Number) + Err.Clear : call CCur(nothingObj) : call ok(Err.Number = 91, "CCur(Nothing) err=" & Err.Number) + Err.Clear : call CDate(nothingObj) : call ok(Err.Number = 91, "CDate(Nothing) err=" & Err.Number) + Err.Clear : call CStr(nothingObj) : call ok(Err.Number = 91, "CStr(Nothing) err=" & Err.Number) End Sub Call testCoerceNullNothing -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10902