From: Francis De Brabandere <francisdb@gmail.com> When Option Explicit is set and an undefined variable is used, return VBS error 500 (Variable is undefined) with the variable name in the description, matching Windows behavior. Previously Wine returned E_FAIL with no description. --- dlls/vbscript/interp.c | 58 +++++++++++++++++++++++++++-------- dlls/vbscript/tests/error.vbs | 4 +-- dlls/vbscript/tests/lang.vbs | 4 +-- dlls/vbscript/tests/run.c | 33 ++++++++++++++++++++ 4 files changed, 82 insertions(+), 17 deletions(-) diff --git a/dlls/vbscript/interp.c b/dlls/vbscript/interp.c index 2a73a7fb51a..7fe49b7eb3f 100644 --- a/dlls/vbscript/interp.c +++ b/dlls/vbscript/interp.c @@ -326,6 +326,37 @@ void clear_error_loc(script_ctx_t *ctx) } } +static HRESULT throw_error(script_ctx_t *ctx, HRESULT error, const WCHAR *identifier) +{ + BSTR desc, source; + + desc = get_vbscript_string(HRESULT_CODE(error)); + if(desc && identifier) { + unsigned desc_len = SysStringLen(desc); + unsigned ident_len = lstrlenW(identifier); + /* format: "Error text: 'identifier'" */ + BSTR new_desc = SysAllocStringLen(NULL, desc_len + 3 + ident_len + 1); + if(new_desc) { + memcpy(new_desc, desc, desc_len * sizeof(WCHAR)); + new_desc[desc_len] = ':'; + new_desc[desc_len + 1] = ' '; + new_desc[desc_len + 2] = '\''; + memcpy(new_desc + desc_len + 3, identifier, ident_len * sizeof(WCHAR)); + new_desc[desc_len + 3 + ident_len] = '\''; + SysFreeString(desc); + desc = new_desc; + } + } + + source = get_vbscript_string(VBS_RUNTIME_ERROR); + + clear_ei(&ctx->ei); + ctx->ei.scode = error; + ctx->ei.bstrDescription = desc; + ctx->ei.bstrSource = source; + return SCRIPT_E_RECORDED; +} + static inline VARIANT *stack_pop(exec_ctx_t *ctx) { assert(ctx->top); @@ -712,6 +743,8 @@ static HRESULT do_icall(exec_ctx_t *ctx, VARIANT *res, BSTR identifier, unsigned V_BYREF(res) = new; break; } + if(ctx->func->code_ctx->option_explicit) + return throw_error(ctx->script, MAKE_VBSERROR(VBSE_VARIABLE_UNDEFINED), identifier); FIXME("%s not found\n", debugstr_w(identifier)); return DISP_E_UNKNOWNNAME; } @@ -951,21 +984,20 @@ static HRESULT assign_ident(exec_ctx_t *ctx, BSTR name, WORD flags, DISPPARAMS * case REF_CONST: FIXME("REF_CONST\n"); return E_NOTIMPL; - case REF_NONE: - if(ctx->func->code_ctx->option_explicit) { - FIXME("throw exception\n"); - hres = E_FAIL; - }else { - VARIANT *new_var; + case REF_NONE: { + VARIANT *new_var; - if(arg_cnt(dp)) - return DISP_E_TYPEMISMATCH; + if(ctx->func->code_ctx->option_explicit) + return throw_error(ctx->script, MAKE_VBSERROR(VBSE_VARIABLE_UNDEFINED), name); - TRACE("creating variable %s\n", debugstr_w(name)); - hres = add_dynamic_var(ctx, name, FALSE, &new_var); - if(SUCCEEDED(hres)) - hres = assign_value(ctx, new_var, dp->rgvarg, flags); - } + if(arg_cnt(dp)) + return DISP_E_TYPEMISMATCH; + + TRACE("creating variable %s\n", debugstr_w(name)); + hres = add_dynamic_var(ctx, name, FALSE, &new_var); + if(SUCCEEDED(hres)) + hres = assign_value(ctx, new_var, dp->rgvarg, flags); + } } return hres; diff --git a/dlls/vbscript/tests/error.vbs b/dlls/vbscript/tests/error.vbs index ce08bcec67a..9de287bbf84 100644 --- a/dlls/vbscript/tests/error.vbs +++ b/dlls/vbscript/tests/error.vbs @@ -518,13 +518,13 @@ err.clear ' Option Explicit: assigning to undeclared variable should give error 500 undeclaredVar = 1 -todo_wine_ok err.number = 500, "err.number = " & err.number +ok err.number = 500, "err.number = " & err.number err.clear ' Option Explicit: reading undeclared variable should give error 500 dim unused unused = undeclaredVar2 -todo_wine_ok err.number = 500, "err.number = " & err.number +ok err.number = 500, "err.number = " & err.number on error goto 0 sub testObjectRequired() diff --git a/dlls/vbscript/tests/lang.vbs b/dlls/vbscript/tests/lang.vbs index ed483602176..2c11e30280a 100644 --- a/dlls/vbscript/tests/lang.vbs +++ b/dlls/vbscript/tests/lang.vbs @@ -2890,13 +2890,13 @@ On Error Resume Next Err.Clear Execute "Option Explicit : noDimExec = 101" Call ok(Err.Number <> 0, "Execute Option Explicit undeclared should error: err=" & Err.Number) -todo_wine_ok Err.Number = 500, "Execute Option Explicit undeclared: err=" & Err.Number & " expected 500" +ok Err.Number = 500, "Execute Option Explicit undeclared: err=" & Err.Number & " expected 500" ' Option Explicit inside same ExecuteGlobal string enforces Dim requirement Err.Clear ExecuteGlobal "Option Explicit : noDimExecGlobal = 102" Call ok(Err.Number <> 0, "ExecuteGlobal Option Explicit undeclared should error: err=" & Err.Number) -todo_wine_ok Err.Number = 500, "ExecuteGlobal Option Explicit undeclared: err=" & Err.Number & " expected 500" +ok Err.Number = 500, "ExecuteGlobal Option Explicit undeclared: err=" & Err.Number & " expected 500" ' Option Explicit with Dim in same string works Err.Clear diff --git a/dlls/vbscript/tests/run.c b/dlls/vbscript/tests/run.c index da6f31ff4df..16344891bc1 100644 --- a/dlls/vbscript/tests/run.c +++ b/dlls/vbscript/tests/run.c @@ -2205,6 +2205,38 @@ static HRESULT parse_script_wr(const WCHAR *src) return hres; } +static void test_option_explicit_errors(void) +{ + IActiveScriptError *error; + EXCEPINFO ei; + HRESULT hres; + + /* Without Option Explicit: assigning to undefined variable should succeed (implicit creation) */ + parse_script_wf(SCRIPTITEM_GLOBALMEMBERS, L"x = 1\nCall ok(x = 1, \"x = \" & x)"); + + /* Without Option Explicit: reading undefined variable should succeed and return Empty */ + parse_script_wf(SCRIPTITEM_GLOBALMEMBERS, L"Call ok(getVT(y) = \"VT_EMPTY*\", \"getVT(y) = \" & getVT(y))"); + + /* Option Explicit: assigning to undefined variable should give error 500 */ + store_script_error = &error; + SET_EXPECT(OnScriptError); + hres = parse_script_wr(L"Option Explicit\nx = 1"); + ok(hres == MAKE_VBSERROR(500), "expected MAKE_VBSERROR(500), got: %08lx\n", hres); + CHECK_CALLED(OnScriptError); + + memset(&ei, 0, sizeof(ei)); + hres = IActiveScriptError_GetExceptionInfo(error, &ei); + ok(hres == S_OK, "GetExceptionInfo returned %08lx\n", hres); + ok(ei.scode == MAKE_VBSERROR(500), "scode = %lx\n", ei.scode); + if(is_english) + ok(ei.bstrDescription && !wcscmp(ei.bstrDescription, L"Variable is undefined: 'x'"), + "bstrDescription = %s\n", wine_dbgstr_w(ei.bstrDescription)); + SysFreeString(ei.bstrSource); + SysFreeString(ei.bstrDescription); + SysFreeString(ei.bstrHelpFile); + IActiveScriptError_Release(error); +} + static void test_parse_context(void) { IActiveScriptParse *parser; @@ -3747,6 +3779,7 @@ static void run_tests(void) test_gc(); test_msgbox(); test_isexpression(); + test_option_explicit_errors(); test_parse_errors(); test_parse_context(); test_callbacks(); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10405