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 | 57 ++++++++++++++++++++++++++--------- dlls/vbscript/tests/run.c | 33 ++++++++++++++++++++ dlls/vbscript/vbscript.rc | 1 + dlls/vbscript/vbscript_defs.h | 1 + 4 files changed, 78 insertions(+), 14 deletions(-) diff --git a/dlls/vbscript/interp.c b/dlls/vbscript/interp.c index 779f3f881a5..f075fd38be5 100644 --- a/dlls/vbscript/interp.c +++ b/dlls/vbscript/interp.c @@ -301,6 +301,37 @@ static 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); @@ -671,6 +702,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_NOT_DEFINED), identifier); FIXME("%s not found\n", debugstr_w(identifier)); return DISP_E_UNKNOWNNAME; } @@ -906,22 +939,18 @@ static HRESULT assign_ident(exec_ctx_t *ctx, BSTR name, WORD flags, DISPPARAMS * 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; - - if(arg_cnt(dp)) { - FIXME("arg_cnt %d not supported\n", arg_cnt(dp)); - return E_NOTIMPL; - } + if(ctx->func->code_ctx->option_explicit) + return throw_error(ctx->script, MAKE_VBSERROR(VBSE_VARIABLE_NOT_DEFINED), 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)) { + FIXME("arg_cnt %d not supported\n", arg_cnt(dp)); + return E_NOTIMPL; } + + TRACE("creating variable %s\n", debugstr_w(name)); + hres = add_dynamic_var(ctx, name, FALSE, &ref.u.v); + if(SUCCEEDED(hres)) + hres = assign_value(ctx, ref.u.v, dp->rgvarg, flags); } return hres; diff --git a/dlls/vbscript/tests/run.c b/dlls/vbscript/tests/run.c index 4b69bc18ffa..5a3bf80bbc4 100644 --- a/dlls/vbscript/tests/run.c +++ b/dlls/vbscript/tests/run.c @@ -2189,6 +2189,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; @@ -3498,6 +3530,7 @@ static void run_tests(void) test_gc(); test_msgbox(); test_isexpression(); + test_option_explicit_errors(); test_parse_errors(); test_parse_context(); test_callbacks(); diff --git a/dlls/vbscript/vbscript.rc b/dlls/vbscript/vbscript.rc index 96b1f41a9a2..2088375c0ca 100644 --- a/dlls/vbscript/vbscript.rc +++ b/dlls/vbscript/vbscript.rc @@ -57,6 +57,7 @@ STRINGTABLE VBSE_INVALID_DLL_FUNCTION_NAME "Specified DLL function not found" VBSE_INVALID_TYPELIB_VARIABLE "Variable uses an Automation type not supported in VBScript" VBSE_SERVER_NOT_FOUND "The remote server machine does not exist or is unavailable" + VBSE_VARIABLE_NOT_DEFINED "Variable is undefined" VBSE_UNQUALIFIED_REFERENCE "Invalid or unqualified reference" VBS_COMPILE_ERROR "Microsoft VBScript compilation error" diff --git a/dlls/vbscript/vbscript_defs.h b/dlls/vbscript/vbscript_defs.h index 19c71b62975..d20cf088fea 100644 --- a/dlls/vbscript/vbscript_defs.h +++ b/dlls/vbscript/vbscript_defs.h @@ -268,6 +268,7 @@ #define VBSE_INVALID_DLL_FUNCTION_NAME 453 #define VBSE_INVALID_TYPELIB_VARIABLE 458 #define VBSE_SERVER_NOT_FOUND 462 +#define VBSE_VARIABLE_NOT_DEFINED 500 #define VBSE_UNQUALIFIED_REFERENCE 505 #define VBS_COMPILE_ERROR 4096 -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10405