From: Francis De Brabandere <francisdb@gmail.com> When a For-Each loop iterates over a SAFEARRAY, bypass the IEnumVARIANT COM vtable dispatch and copy elements directly from the array data. This eliminates the COM call overhead and one intermediate VariantCopy per iteration. For non-refcounted element types (VT_I2, VT_I4, VT_R8, etc.), also skip VariantCopyInd and VariantClear entirely, replacing them with a direct struct assignment. Non-SAFEARRAY iterators (e.g. IDispatch collections) continue to use the standard IEnumVARIANT_Next path. For-Each over a 10M-element integer array: 6,339ms -> 58ms (109x faster). --- dlls/vbscript/interp.c | 38 +++++++++++++++++++------------ dlls/vbscript/utils.c | 49 ++++++++++++++++++++++++++++++++++++++++ dlls/vbscript/vbscript.h | 1 + 3 files changed, 74 insertions(+), 14 deletions(-) diff --git a/dlls/vbscript/interp.c b/dlls/vbscript/interp.c index f95db175035..61f4b7a4112 100644 --- a/dlls/vbscript/interp.c +++ b/dlls/vbscript/interp.c @@ -1879,11 +1879,16 @@ static HRESULT interp_enumnext(exec_ctx_t *ctx) iter = (IEnumVARIANT*)V_UNKNOWN(stack_top(ctx, 0)); V_VT(&v) = VT_EMPTY; - hres = IEnumVARIANT_Next(iter, 1, &v, NULL); - if(FAILED(hres)) + hres = safearray_iter_next(iter, &v, &do_continue); + if(hres == E_UNEXPECTED) { + hres = IEnumVARIANT_Next(iter, 1, &v, NULL); + if(FAILED(hres)) + return hres; + do_continue = hres == S_OK; + }else if(FAILED(hres)) { return hres; + } - do_continue = hres == S_OK; hres = assign_ident(ctx, ident, DISPATCH_PROPERTYPUT|DISPATCH_PROPERTYPUTREF, &dp); VariantClear(&v); if(FAILED(hres)) @@ -1902,7 +1907,6 @@ 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; @@ -1917,21 +1921,27 @@ static HRESULT interp_enumnext_local(exec_ctx_t *ctx) 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)) + hres = safearray_iter_next(iter, local_var, &do_continue); + if(hres == E_UNEXPECTED) { + VARIANT v; + + V_VT(&v) = VT_EMPTY; + hres = IEnumVARIANT_Next(iter, 1, &v, NULL); + if(FAILED(hres)) + return hres; + + do_continue = hres == S_OK; + hres = assign_value(ctx, local_var, &v, DISPATCH_PROPERTYPUT|DISPATCH_PROPERTYPUTREF); + VariantClear(&v); + if(FAILED(hres)) + return hres; + }else if(FAILED(hres)) { return hres; + } if(do_continue) { ctx->instr++; diff --git a/dlls/vbscript/utils.c b/dlls/vbscript/utils.c index 4bfc2c65a0a..e73495ef3f9 100644 --- a/dlls/vbscript/utils.c +++ b/dlls/vbscript/utils.c @@ -196,3 +196,52 @@ HRESULT create_safearray_iter(SAFEARRAY *sa, BOOL owned, IEnumVARIANT **ev) *ev = &iter->IEnumVARIANT_iface; return S_OK; } + +HRESULT safearray_iter_next(IEnumVARIANT *iface, VARIANT *dst, BOOL *fetched) +{ + safearray_iter *iter; + VARIANT *src; + + if(iface->lpVtbl != &safearray_iter_EnumVARIANTVtbl) + return E_UNEXPECTED; + + iter = impl_from_IEnumVARIANT(iface); + + if(iter->i >= iter->size) { + VariantClear(dst); + *fetched = FALSE; + return S_OK; + } + + src = (VARIANT*)(((BYTE*)iter->sa->pvData) + iter->i * iter->sa->cbElements); + + /* Fast path for non-refcounted types (VT_I2, VT_I4, VT_R8, VT_BOOL, etc.). + * SAFEARRAY elements are stored by value (never VT_BYREF), so VariantCopyInd + * reduces to VariantCopy, which for simple types is just a struct assignment. */ + if(!(V_VT(src) & ~VT_TYPEMASK) && V_VT(src) != VT_BSTR + && V_VT(src) != VT_DISPATCH && V_VT(src) != VT_UNKNOWN) + { + if(V_VT(dst) == VT_BSTR || V_VT(dst) == VT_DISPATCH + || V_VT(dst) == VT_UNKNOWN || (V_VT(dst) & VT_ARRAY)) + VariantClear(dst); + *dst = *src; + } + else + { + VARIANT value; + HRESULT hres; + + V_VT(&value) = VT_EMPTY; + hres = VariantCopyInd(&value, src); + if(FAILED(hres)) + return hres; + + VariantClear(dst); + *dst = value; + } + + iter->i++; + *fetched = TRUE; + return S_OK; +} + diff --git a/dlls/vbscript/vbscript.h b/dlls/vbscript/vbscript.h index aed2ccaf5e5..b70d5f23324 100644 --- a/dlls/vbscript/vbscript.h +++ b/dlls/vbscript/vbscript.h @@ -455,6 +455,7 @@ BSTR string_replace(BSTR,BSTR,BSTR,int,int,int); void map_vbs_exception(EXCEPINFO *); HRESULT create_safearray_iter(SAFEARRAY *sa, BOOL owned, IEnumVARIANT **ev); +HRESULT safearray_iter_next(IEnumVARIANT *iface, VARIANT *dst, BOOL *fetched); #define FACILITY_VBS 0xa #define MAKE_VBSERROR(code) MAKE_HRESULT(SEVERITY_ERROR, FACILITY_VBS, code) -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10515