From: Francis De Brabandere <francisdb@gmail.com> Native VBScript appends the identifier name only to the error description passed to the host through IActiveScriptError, while the Err object exposes the bare error text. Store the identifier separately from the recorded error and append it when reporting to the host. --- dlls/vbscript/global.c | 5 +++- dlls/vbscript/interp.c | 48 ++++++++++++++--------------------- dlls/vbscript/tests/error.vbs | 16 ++++++++++++ dlls/vbscript/vbdisp.c | 4 +-- dlls/vbscript/vbscript.c | 24 +++++++++++++++++- dlls/vbscript/vbscript.h | 2 ++ 6 files changed, 66 insertions(+), 33 deletions(-) diff --git a/dlls/vbscript/global.c b/dlls/vbscript/global.c index 5cf167fc4f1..1bd58335eb5 100644 --- a/dlls/vbscript/global.c +++ b/dlls/vbscript/global.c @@ -5058,7 +5058,7 @@ static HRESULT Err_Clear(BuiltinDisp *This, VARIANT *args, unsigned args_cnt, VA { TRACE("\n"); - clear_ei(&This->ctx->ei); + clear_error(This->ctx); return S_OK; } @@ -5088,6 +5088,9 @@ static HRESULT Err_Raise(BuiltinDisp *This, VARIANT *args, unsigned args_cnt, VA if(SUCCEEDED(hres)) { script_ctx_t *ctx = This->ctx; + SysFreeString(ctx->ei_identifier); + ctx->ei_identifier = NULL; + if(source) { SysFreeString(ctx->ei.bstrSource); ctx->ei.bstrSource = source; diff --git a/dlls/vbscript/interp.c b/dlls/vbscript/interp.c index 1b071d6f077..c57b28ab90e 100644 --- a/dlls/vbscript/interp.c +++ b/dlls/vbscript/interp.c @@ -316,6 +316,13 @@ void clear_ei(EXCEPINFO *ei) memset(ei, 0, sizeof(*ei)); } +void clear_error(script_ctx_t *ctx) +{ + clear_ei(&ctx->ei); + SysFreeString(ctx->ei_identifier); + ctx->ei_identifier = NULL; +} + void clear_error_loc(script_ctx_t *ctx) { if(ctx->error_loc_code) { @@ -324,34 +331,17 @@ void clear_error_loc(script_ctx_t *ctx) } } +/* The identifier is not part of the error description visible to scripts + through the Err object; report_script_error appends it to the description + passed to the host. */ 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); + clear_error(ctx); ctx->ei.scode = error; - ctx->ei.bstrDescription = desc; - ctx->ei.bstrSource = source; + ctx->ei.bstrDescription = get_vbscript_string(HRESULT_CODE(error)); + ctx->ei.bstrSource = get_vbscript_string(VBS_RUNTIME_ERROR); + if(identifier) + ctx->ei_identifier = SysAllocString(identifier); return SCRIPT_E_RECORDED; } @@ -1845,7 +1835,7 @@ static HRESULT interp_step(exec_ctx_t *ctx) * and exit the loop. */ if(V_VT(stack_top(ctx, 0)) == VT_EMPTY && V_VT(stack_top(ctx, 1)) == VT_EMPTY) { WARN("For loop not initialized\n"); - clear_ei(&ctx->script->ei); + clear_error(ctx->script); ctx->script->ei.scode = MAKE_VBSERROR(VBSE_FOR_LOOP_NOT_INITIALIZED); map_vbs_exception(&ctx->script->ei); stack_popn(ctx, 3); @@ -1876,7 +1866,7 @@ static HRESULT interp_step_local(exec_ctx_t *ctx) if(V_VT(stack_top(ctx, 0)) == VT_EMPTY && V_VT(stack_top(ctx, 1)) == VT_EMPTY) { WARN("For loop not initialized\n"); - clear_ei(&ctx->script->ei); + clear_error(ctx->script); ctx->script->ei.scode = MAKE_VBSERROR(VBSE_FOR_LOOP_NOT_INITIALIZED); map_vbs_exception(&ctx->script->ei); stack_popn(ctx, 3); @@ -2123,7 +2113,7 @@ static HRESULT interp_errmode(exec_ctx_t *ctx) TRACE("%d\n", err_mode); ctx->resume_next = err_mode; - clear_ei(&ctx->script->ei); + clear_error(ctx->script); return S_OK; } @@ -3245,7 +3235,7 @@ HRESULT exec_script(script_ctx_t *ctx, BOOL extern_caller, function_t *func, vbd if(FAILED(hres)) { if(hres != SCRIPT_E_RECORDED) { /* SCRIPT_E_RECORDED means ctx->ei is already populated */ - clear_ei(&ctx->ei); + clear_error(ctx); ctx->ei.scode = hres; } diff --git a/dlls/vbscript/tests/error.vbs b/dlls/vbscript/tests/error.vbs index 843c27a01bb..f8747a2580b 100644 --- a/dlls/vbscript/tests/error.vbs +++ b/dlls/vbscript/tests/error.vbs @@ -492,6 +492,22 @@ end sub call testDivisionByZero +sub testUndefinedVarDescription() + on error resume next + + Err.Clear() + ExecuteGlobal "Option Explicit" & vbCrLf & "Dim q : q = undefinedReadVar" + call ok(Err.Number = 500, "read undefined Err.Number = " & Err.Number) + call ok(Err.Description = "Variable is undefined", "read undefined Err.Description = " & Err.Description) + + Err.Clear() + ExecuteGlobal "Option Explicit" & vbCrLf & "undefinedAssignVar = 1" + call ok(Err.Number = 500, "assign undefined Err.Number = " & Err.Number) + call ok(Err.Description = "Variable is undefined", "assign undefined Err.Description = " & Err.Description) +end sub + +call testUndefinedVarDescription + on error resume next throwWithDesc diff --git a/dlls/vbscript/vbdisp.c b/dlls/vbscript/vbdisp.c index 38e4ed140ab..d953c19bb30 100644 --- a/dlls/vbscript/vbdisp.c +++ b/dlls/vbscript/vbdisp.c @@ -2046,7 +2046,7 @@ HRESULT disp_call(script_ctx_t *ctx, IDispatch *disp, DISPID id, BOOL is_call, D } if(hres == DISP_E_EXCEPTION) { - clear_ei(&ctx->ei); + clear_error(ctx); ctx->ei = ei; hres = SCRIPT_E_RECORDED; } @@ -2084,7 +2084,7 @@ HRESULT disp_propput(script_ctx_t *ctx, IDispatch *disp, DISPID id, WORD flags, } if(hres == DISP_E_EXCEPTION) { - clear_ei(&ctx->ei); + clear_error(ctx); ctx->ei = ei; hres = SCRIPT_E_RECORDED; } diff --git a/dlls/vbscript/vbscript.c b/dlls/vbscript/vbscript.c index ff28b3b17ef..4899354ea48 100644 --- a/dlls/vbscript/vbscript.c +++ b/dlls/vbscript/vbscript.c @@ -284,7 +284,7 @@ static void release_script(script_ctx_t *ctx) vbscode_t *code, *code_next; collect_objects(ctx); - clear_ei(&ctx->ei); + clear_error(ctx); LIST_FOR_EACH_ENTRY_SAFE(code, code_next, &ctx->code_list, vbscode_t, entry) { @@ -606,6 +606,28 @@ HRESULT report_script_error(script_ctx_t *ctx, vbscode_t *code, unsigned loc, BO memset(&ctx->ei, 0, sizeof(ctx->ei)); result = error->ei.scode; + /* The description passed to the host names the offending identifier, + while the Err object exposes only the bare error text. */ + if(ctx->ei_identifier) { + if(error->ei.bstrDescription) { + unsigned desc_len = SysStringLen(error->ei.bstrDescription); + unsigned ident_len = SysStringLen(ctx->ei_identifier); + BSTR new_desc = SysAllocStringLen(NULL, desc_len + 3 + ident_len + 1); + if(new_desc) { + memcpy(new_desc, error->ei.bstrDescription, desc_len * sizeof(WCHAR)); + new_desc[desc_len] = ':'; + new_desc[desc_len + 1] = ' '; + new_desc[desc_len + 2] = '\''; + memcpy(new_desc + desc_len + 3, ctx->ei_identifier, ident_len * sizeof(WCHAR)); + new_desc[desc_len + 3 + ident_len] = '\''; + SysFreeString(error->ei.bstrDescription); + error->ei.bstrDescription = new_desc; + } + } + SysFreeString(ctx->ei_identifier); + ctx->ei_identifier = NULL; + } + p = code->source; error->cookie = code->cookie; error->line = code->start_line; diff --git a/dlls/vbscript/vbscript.h b/dlls/vbscript/vbscript.h index e3c57b0f8e5..9450851e4a4 100644 --- a/dlls/vbscript/vbscript.h +++ b/dlls/vbscript/vbscript.h @@ -225,6 +225,7 @@ struct _script_ctx_t { unsigned call_depth; EXCEPINFO ei; + BSTR ei_identifier; vbscode_t *error_loc_code; unsigned error_loc_offset; @@ -438,6 +439,7 @@ named_item_t *lookup_named_item(script_ctx_t*,const WCHAR*,unsigned); void release_named_item(named_item_t*); void clear_error_loc(script_ctx_t*); void clear_ei(EXCEPINFO*); +void clear_error(script_ctx_t*); HRESULT report_script_error(script_ctx_t*,vbscode_t*,unsigned,BOOL); void detach_global_objects(script_ctx_t*); HRESULT get_builtin_id(BuiltinDisp*,const WCHAR*,DISPID*); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/11126