From: Francis De Brabandere <francisdb@gmail.com> When the loop variable in a For-Each statement can be resolved at compile time (local Dim variable or argument), emit OP_enumnext_local with the variable index instead of OP_enumnext with a string identifier. This bypasses the lookup_identifier() string scan on every iteration. --- dlls/vbscript/compile.c | 34 ++++++++++++++++++++++++------- dlls/vbscript/interp.c | 44 ++++++++++++++++++++++++++++++++++++++++ dlls/vbscript/vbscript.h | 1 + 3 files changed, 72 insertions(+), 7 deletions(-) diff --git a/dlls/vbscript/compile.c b/dlls/vbscript/compile.c index 93a869e6af9..0ab944d2718 100644 --- a/dlls/vbscript/compile.c +++ b/dlls/vbscript/compile.c @@ -914,9 +914,13 @@ static HRESULT compile_dowhile_statement(compile_ctx_t *ctx, while_statement_t * static HRESULT compile_foreach_statement(compile_ctx_t *ctx, foreach_statement_t *stat) { statement_ctx_t loop_ctx = {1}; - unsigned loop_start; + unsigned loop_start, instr; + int local_ref; + BOOL is_local; HRESULT hres; + is_local = bind_local(ctx, stat->identifier, &local_ref); + /* Preserve a place on the stack in case we throw before having proper enum collection. */ if(!push_instr(ctx, OP_empty)) return E_OUTOFMEMORY; @@ -931,9 +935,17 @@ static HRESULT compile_foreach_statement(compile_ctx_t *ctx, foreach_statement_t if(!(loop_ctx.for_end_label = alloc_label(ctx))) return E_OUTOFMEMORY; - hres = push_instr_uint_bstr(ctx, OP_enumnext, loop_ctx.for_end_label, stat->identifier); - if(FAILED(hres)) - return hres; + if(is_local) { + instr = push_instr(ctx, OP_enumnext_local); + if(!instr) + return E_OUTOFMEMORY; + instr_ptr(ctx, instr)->arg1.uint = loop_ctx.for_end_label; + instr_ptr(ctx, instr)->arg2.lng = local_ref; + }else { + hres = push_instr_uint_bstr(ctx, OP_enumnext, loop_ctx.for_end_label, stat->identifier); + if(FAILED(hres)) + return hres; + } if(!emit_catch(ctx, 1)) return E_OUTOFMEMORY; @@ -945,9 +957,17 @@ static HRESULT compile_foreach_statement(compile_ctx_t *ctx, foreach_statement_t /* We need a separated enumnext here, because we need to jump out of the loop on exception. */ ctx->loc = stat->stat.loc; - hres = push_instr_uint_bstr(ctx, OP_enumnext, loop_ctx.for_end_label, stat->identifier); - if(FAILED(hres)) - return hres; + if(is_local) { + instr = push_instr(ctx, OP_enumnext_local); + if(!instr) + return E_OUTOFMEMORY; + instr_ptr(ctx, instr)->arg1.uint = loop_ctx.for_end_label; + instr_ptr(ctx, instr)->arg2.lng = local_ref; + }else { + hres = push_instr_uint_bstr(ctx, OP_enumnext, loop_ctx.for_end_label, stat->identifier); + if(FAILED(hres)) + return hres; + } hres = push_instr_addr(ctx, OP_jmp, loop_start); if(FAILED(hres)) diff --git a/dlls/vbscript/interp.c b/dlls/vbscript/interp.c index ca662e4d877..f95db175035 100644 --- a/dlls/vbscript/interp.c +++ b/dlls/vbscript/interp.c @@ -1898,6 +1898,50 @@ static HRESULT interp_enumnext(exec_ctx_t *ctx) return S_OK; } +static HRESULT interp_enumnext_local(exec_ctx_t *ctx) +{ + const unsigned loop_end = ctx->instr->arg1.uint; + const int ref = ctx->instr->arg2.lng; + VARIANT v; + IEnumVARIANT *iter; + VARIANT *local_var; + BOOL do_continue; + HRESULT hres; + + TRACE("%d\n", ref); + + if(V_VT(stack_top(ctx, 0)) == VT_EMPTY) { + return MAKE_VBSERROR(VBSE_NOT_ENUM); + } + + assert(V_VT(stack_top(ctx, 0)) == VT_UNKNOWN); + iter = (IEnumVARIANT*)V_UNKNOWN(stack_top(ctx, 0)); + + V_VT(&v) = VT_EMPTY; + hres = IEnumVARIANT_Next(iter, 1, &v, NULL); + if(FAILED(hres)) + return hres; + + do_continue = hres == S_OK; + + local_var = get_local_var(ctx, ref); + if(V_VT(local_var) == (VT_VARIANT|VT_BYREF)) + local_var = V_VARIANTREF(local_var); + + hres = assign_value(ctx, local_var, &v, DISPATCH_PROPERTYPUT|DISPATCH_PROPERTYPUTREF); + VariantClear(&v); + if(FAILED(hres)) + return hres; + + if(do_continue) { + ctx->instr++; + }else { + stack_popn(ctx, 1); + instr_jmp(ctx, loop_end); + } + return S_OK; +} + static HRESULT interp_jmp(exec_ctx_t *ctx) { const unsigned arg = ctx->instr->arg1.uint; diff --git a/dlls/vbscript/vbscript.h b/dlls/vbscript/vbscript.h index 1bc3f788577..aed2ccaf5e5 100644 --- a/dlls/vbscript/vbscript.h +++ b/dlls/vbscript/vbscript.h @@ -256,6 +256,7 @@ typedef enum { X(double, 1, ARG_DOUBLE, 0) \ X(empty, 1, 0, 0) \ X(enumnext, 0, ARG_ADDR, ARG_BSTR) \ + X(enumnext_local, 0, ARG_ADDR, ARG_INT) \ X(equal, 1, 0, 0) \ X(hres, 1, ARG_UINT, 0) \ X(errmode, 1, ARG_INT, 0) \ -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10515