From: Francis De Brabandere <francisdb@gmail.com> When a For..To loop's bound expression fails (e.g. UBound on Empty) and On Error Resume Next is active, the error handler fills the step/to stack slots with VT_EMPTY. On the next iteration, VarCmp fails on these values and the error propagates back into the loop, causing an infinite loop. Fix this by handling VarCmp failures in interp_step: exit the loop and report error 92 (For loop not initialized). Also make interp_incc ignore VarAdd failures so execution reaches interp_step for proper cleanup. Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=54291 --- dlls/vbscript/interp.c | 22 +++++++++++++++------- dlls/vbscript/tests/lang.vbs | 13 +++++++++++++ dlls/vbscript/vbscript.rc | 1 + dlls/vbscript/vbscript_defs.h | 1 + 4 files changed, 30 insertions(+), 7 deletions(-) diff --git a/dlls/vbscript/interp.c b/dlls/vbscript/interp.c index 127fcc2c05e..6b6256cd83e 100644 --- a/dlls/vbscript/interp.c +++ b/dlls/vbscript/interp.c @@ -1438,7 +1438,7 @@ static HRESULT interp_step(exec_ctx_t *ctx) V_I2(&zero) = 0; hres = VarCmp(stack_top(ctx, 0), &zero, ctx->script->lcid, 0); if(FAILED(hres)) - return hres; + goto loop_not_initialized; gteq_zero = hres == VARCMP_GT || hres == VARCMP_EQ; @@ -1453,7 +1453,7 @@ static HRESULT interp_step(exec_ctx_t *ctx) hres = VarCmp(ref.u.v, stack_top(ctx, 1), ctx->script->lcid, 0); if(FAILED(hres)) - return hres; + goto loop_not_initialized; if(hres == VARCMP_EQ || hres == (gteq_zero ? VARCMP_LT : VARCMP_GT)) { ctx->instr++; @@ -1462,6 +1462,15 @@ static HRESULT interp_step(exec_ctx_t *ctx) instr_jmp(ctx, ctx->instr->arg1.uint); } return S_OK; + +loop_not_initialized: + WARN("For loop not initialized\n"); + stack_popn(ctx, 2); + instr_jmp(ctx, ctx->instr->arg1.uint); + clear_ei(&ctx->script->ei); + ctx->script->ei.scode = MAKE_VBSERROR(VBSE_FOR_LOOP_NOT_INITIALIZED); + map_vbs_exception(&ctx->script->ei); + return S_OK; } static HRESULT interp_newenum(exec_ctx_t *ctx) @@ -2386,11 +2395,10 @@ static HRESULT interp_incc(exec_ctx_t *ctx) } hres = VarAdd(stack_top(ctx, 0), ref.u.v, &v); - if(FAILED(hres)) - return hres; - - VariantClear(ref.u.v); - *ref.u.v = v; + if(SUCCEEDED(hres)) { + VariantClear(ref.u.v); + *ref.u.v = v; + } return S_OK; } diff --git a/dlls/vbscript/tests/lang.vbs b/dlls/vbscript/tests/lang.vbs index 12d91a16548..4ed488b82e1 100644 --- a/dlls/vbscript/tests/lang.vbs +++ b/dlls/vbscript/tests/lang.vbs @@ -743,6 +743,19 @@ do while true next loop +On Error Resume Next +x = Empty +y = 0 +z = 99 +For z = 1 To UBound(x) + y = y + 1 +Next +call ok(Err.Number = 92, "for to UBound(Empty): Err.Number = " & Err.Number) +call ok(y = 1, "for to UBound(Empty): y = " & y) +call todo_wine_ok(z = 99, "for to UBound(Empty): z = " & z) +Err.Clear +On Error GoTo 0 + if null then call ok(false, "if null evaluated") while null diff --git a/dlls/vbscript/vbscript.rc b/dlls/vbscript/vbscript.rc index cf157f77526..5f3bde6cee9 100644 --- a/dlls/vbscript/vbscript.rc +++ b/dlls/vbscript/vbscript.rc @@ -40,6 +40,7 @@ STRINGTABLE VBSE_PATH_FILE_ACCESS "Path/File access error" VBSE_PATH_NOT_FOUND "Path not found" VBSE_OBJECT_VARIABLE_NOT_SET "Object variable not set" + VBSE_FOR_LOOP_NOT_INITIALIZED "For loop not initialized" VBSE_ILLEGAL_NULL_USE "Invalid use of Null" VBSE_CANT_CREATE_TMP_FILE "Can't create necessary temporary file" VBSE_CANT_CREATE_OBJECT "ActiveX component can't create object" diff --git a/dlls/vbscript/vbscript_defs.h b/dlls/vbscript/vbscript_defs.h index 139b71255a0..8cd73998b80 100644 --- a/dlls/vbscript/vbscript_defs.h +++ b/dlls/vbscript/vbscript_defs.h @@ -251,6 +251,7 @@ #define VBSE_PATH_FILE_ACCESS 75 #define VBSE_PATH_NOT_FOUND 76 #define VBSE_OBJECT_VARIABLE_NOT_SET 91 +#define VBSE_FOR_LOOP_NOT_INITIALIZED 92 #define VBSE_ILLEGAL_NULL_USE 94 #define VBSE_CANT_CREATE_TMP_FILE 322 #define VBSE_CANT_CREATE_OBJECT 429 -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10366