[PATCH 0/1] MR10455: vbscript: Return "Object required" error (424) instead of E_FAIL/E_NOTIMPL.
Fix several interpreter code paths that returned wrong error codes when an object was expected but not provided: - stack_pop_disp: E_FAIL -> 424 for non-dispatch types. - do_mcall: E_FAIL -> 424 when calling a member on Nothing. - interp_assign_member: E_FAIL -> 424 for Nothing.Prop = value. - interp_set_member: E_FAIL -> 424 for Set Nothing.Child = obj. - interp_is: E_NOTIMPL -> 424 for non-object Is operands. Also add OP_with opcode to validate the With expression is a dispatch object at entry time, matching Windows behavior where With on non-object types (integers, strings, Empty, Null, booleans) raises error 424 immediately, while With Nothing succeeds. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10455
From: Francis De Brabandere <francisdb@gmail.com> Fix several interpreter code paths that returned wrong error codes when an object was expected but not provided: - stack_pop_disp: E_FAIL -> 424 for non-dispatch types. - do_mcall: E_FAIL -> 424 when calling a member on Nothing. - interp_assign_member: E_FAIL -> 424 for Nothing.Prop = value. - interp_set_member: E_FAIL -> 424 for Set Nothing.Child = obj. - interp_is: E_NOTIMPL -> 424 for non-object Is operands. Also add OP_with opcode to validate the With expression is a dispatch object at entry time, matching Windows behavior where With on non-object types (integers, strings, Empty, Null, booleans) raises error 424 immediately, while With Nothing succeeds. --- dlls/vbscript/compile.c | 3 ++ dlls/vbscript/interp.c | 42 +++++++++++------ dlls/vbscript/tests/error.vbs | 85 +++++++++++++++++++++++++++++++++++ dlls/vbscript/vbscript.h | 1 + 4 files changed, 117 insertions(+), 14 deletions(-) diff --git a/dlls/vbscript/compile.c b/dlls/vbscript/compile.c index 345e3b5291b..f1072252ff1 100644 --- a/dlls/vbscript/compile.c +++ b/dlls/vbscript/compile.c @@ -954,6 +954,9 @@ static HRESULT compile_with_statement(compile_ctx_t *ctx, with_statement_t *stat if(FAILED(hres)) return hres; + if(!push_instr(ctx, OP_with)) + return E_OUTOFMEMORY; + if(!emit_catch(ctx, 1)) return E_OUTOFMEMORY; diff --git a/dlls/vbscript/interp.c b/dlls/vbscript/interp.c index 00ef7d93eb6..088c23d0cf0 100644 --- a/dlls/vbscript/interp.c +++ b/dlls/vbscript/interp.c @@ -449,15 +449,15 @@ static HRESULT stack_pop_disp(exec_ctx_t *ctx, IDispatch **ret) } if(V_VT(v) != (VT_VARIANT|VT_BYREF)) { - FIXME("not supported type: %s\n", debugstr_variant(v)); + WARN("not supported type: %s\n", debugstr_variant(v)); VariantClear(v); - return E_FAIL; + return MAKE_VBSERROR(VBSE_OBJECT_REQUIRED); } v = V_BYREF(v); if(V_VT(v) != VT_DISPATCH) { - FIXME("not disp %s\n", debugstr_variant(v)); - return E_FAIL; + WARN("not disp %s\n", debugstr_variant(v)); + return MAKE_VBSERROR(VBSE_OBJECT_REQUIRED); } if(V_DISPATCH(v)) @@ -768,8 +768,8 @@ static HRESULT do_mcall(exec_ctx_t *ctx, VARIANT *res) return hres; if(!obj) { - FIXME("NULL obj\n"); - return E_FAIL; + WARN("NULL obj\n"); + return MAKE_VBSERROR(VBSE_OBJECT_REQUIRED); } vbstack_to_dp(ctx, arg_cnt, FALSE, &dp); @@ -997,8 +997,8 @@ static HRESULT interp_assign_member(exec_ctx_t *ctx) return hres; if(!obj) { - FIXME("NULL obj\n"); - return E_FAIL; + WARN("NULL obj\n"); + return MAKE_VBSERROR(VBSE_OBJECT_REQUIRED); } hres = disp_get_id(obj, identifier, VBDISP_LET, FALSE, &id); @@ -1029,8 +1029,8 @@ static HRESULT interp_set_member(exec_ctx_t *ctx) return hres; if(!obj) { - FIXME("NULL obj\n"); - return E_FAIL; + WARN("NULL obj\n"); + return MAKE_VBSERROR(VBSE_OBJECT_REQUIRED); } hres = stack_assume_disp(ctx, arg_cnt, NULL); @@ -2127,8 +2127,8 @@ static HRESULT interp_is(exec_ctx_t *ctx) stack_pop_deref(ctx, &v); if(V_VT(v.v) != VT_DISPATCH && V_VT(v.v) != VT_UNKNOWN) { - FIXME("Unhandled type %s\n", debugstr_variant(v.v)); - hres = E_NOTIMPL; + WARN("Unhandled type %s\n", debugstr_variant(v.v)); + hres = MAKE_VBSERROR(VBSE_OBJECT_REQUIRED); }else if(V_UNKNOWN(v.v)) { hres = IUnknown_QueryInterface(V_UNKNOWN(v.v), &IID_IUnknown, (void**)&r); } @@ -2138,8 +2138,8 @@ static HRESULT interp_is(exec_ctx_t *ctx) stack_pop_deref(ctx, &v); if(V_VT(v.v) != VT_DISPATCH && V_VT(v.v) != VT_UNKNOWN) { - FIXME("Unhandled type %s\n", debugstr_variant(v.v)); - hres = E_NOTIMPL; + WARN("Unhandled type %s\n", debugstr_variant(v.v)); + hres = MAKE_VBSERROR(VBSE_OBJECT_REQUIRED); }else if(V_UNKNOWN(v.v)) { hres = IUnknown_QueryInterface(V_UNKNOWN(v.v), &IID_IUnknown, (void**)&l); } @@ -2407,6 +2407,20 @@ static HRESULT interp_incc(exec_ctx_t *ctx) return S_OK; } +static HRESULT interp_with(exec_ctx_t *ctx) +{ + VARIANT *v; + + TRACE("\n"); + + v = stack_top(ctx, 0); + if(V_VT(v) == (VT_VARIANT|VT_BYREF)) + v = V_VARIANTREF(v); + if(V_VT(v) != VT_DISPATCH && V_VT(v) != VT_UNKNOWN) + return MAKE_VBSERROR(VBSE_OBJECT_REQUIRED); + return S_OK; +} + static HRESULT interp_catch(exec_ctx_t *ctx) { /* Nothing to do here, the OP is for unwinding only. */ diff --git a/dlls/vbscript/tests/error.vbs b/dlls/vbscript/tests/error.vbs index a9d4dc9c0be..1ffe88318e9 100644 --- a/dlls/vbscript/tests/error.vbs +++ b/dlls/vbscript/tests/error.vbs @@ -515,4 +515,89 @@ unused = undeclaredVar2 todo_wine_ok err.number = 500, "err.number = " & err.number on error goto 0 +sub testObjectRequired() + on error resume next + dim x, r + + ' Is operator with non-object operands + err.clear + r = "hello" Is Nothing + ok err.number = 424, "Is string: err.number = " & err.number + + err.clear + r = Nothing Is "hello" + ok err.number = 424, "Is string rhs: err.number = " & err.number + + err.clear + r = 42 Is 42 + ok err.number = 424, "Is int: err.number = " & err.number + + err.clear + r = Empty Is Empty + ok err.number = 424, "Is empty: err.number = " & err.number + + err.clear + r = Null Is Nothing + ok err.number = 424, "Is null: err.number = " & err.number + + ' Member call on Nothing + err.clear + set x = Nothing + x.Prop + ok err.number = 424, "Nothing.Prop: err.number = " & err.number + + ' Assign member on Nothing + err.clear + set x = Nothing + x.Prop = 1 + ok err.number = 424, "Nothing.Prop = 1: err.number = " & err.number + + ' Set member on Nothing + err.clear + set x = Nothing + set x.Child = Nothing + ok err.number = 424, "Set Nothing.Child: err.number = " & err.number + + ' With on non-object types (no member access) + err.clear + with 42 + end with + ok err.number = 424, "With 42: err.number = " & err.number + + err.clear + with "hello" + end with + ok err.number = 424, "With string: err.number = " & err.number + + err.clear + with empty + end with + ok err.number = 424, "With empty: err.number = " & err.number + + err.clear + with null + end with + ok err.number = 424, "With null: err.number = " & err.number + + err.clear + with true + end with + ok err.number = 424, "With true: err.number = " & err.number + + ' With Nothing should NOT raise 424 at entry + err.clear + with Nothing + end with + ok err.number = 0, "With Nothing: err.number = " & err.number + + ' With Nothing + member access should raise 424 + err.clear + with Nothing + .Prop = 1 + end with + ok err.number = 424, "With Nothing .Prop: err.number = " & err.number +end sub + +call testObjectRequired() + call reportSuccess() diff --git a/dlls/vbscript/vbscript.h b/dlls/vbscript/vbscript.h index a0c8b9f82e8..0f1bfc5fb11 100644 --- a/dlls/vbscript/vbscript.h +++ b/dlls/vbscript/vbscript.h @@ -296,6 +296,7 @@ typedef enum { X(val, 1, 0, 0) \ X(vcall, 1, ARG_UINT, 0) \ X(vcallv, 1, ARG_UINT, 0) \ + X(with, 1, 0, 0) \ X(xor, 1, 0, 0) typedef enum { -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10455
@jacek in how far should wine 100% reproduce windows. This extra `OP_with` is for a corner case. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10455#note_133848
On Thu Mar 26 14:09:08 2026 +0000, Francis De Brabandere wrote:
@jacek in how far should wine 100% reproduce windows. This extra `OP_with` is for a corner case. We generally aim for Windows compatibility, but it is also fine if we are not fully complete right away (especially if there is no known application depending on it). In this case, either your solution or keeping it as is would seem fine to me.
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/10455#note_133881
This merge request was approved by Jacek Caban. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10455
On Thu Mar 26 14:19:42 2026 +0000, Jacek Caban wrote:
We generally aim for Windows compatibility, but it is also fine if we are not fully complete right away (especially if there is no known application depending on it). In this case, either your solution or keeping it as is would seem fine to me. Let's keep the OP then. I don't think there's much overhead.
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/10455#note_133888
participants (3)
-
Francis De Brabandere -
Francis De Brabandere (@francisdb) -
Jacek Caban (@jacek)