From: Francis De Brabandere <francisdb@gmail.com> Resolve class property names to direct indices during compilation of class methods, emitting OP_local_prop, OP_assign_local_prop, and OP_set_local_prop instead of the string-based OP_ident/OP_assign_ident/ OP_set_ident. This addresses the FIXME in lookup_identifier() that noted these should be bound while generating bytecode. This mirrors jscript's approach of resolving identifiers at compile time rather than doing runtime string comparisons. --- dlls/vbscript/compile.c | 54 +++++++++++++++++++++++++++++++++++++++ dlls/vbscript/interp.c | 55 ++++++++++++++++++++++++++++++++++++++++ dlls/vbscript/vbscript.h | 11 +++++--- 3 files changed, 116 insertions(+), 4 deletions(-) diff --git a/dlls/vbscript/compile.c b/dlls/vbscript/compile.c index f2abed5cc82..c969b9a0058 100644 --- a/dlls/vbscript/compile.c +++ b/dlls/vbscript/compile.c @@ -63,6 +63,7 @@ typedef struct { function_t *func; function_decl_t *func_decls; + dim_decl_t *class_props; } compile_ctx_t; static HRESULT compile_expression(compile_ctx_t*,expression_t*); @@ -205,6 +206,19 @@ static HRESULT push_instr_int_uint(compile_ctx_t *ctx, vbsop_t op, LONG arg1, un return S_OK; } +static HRESULT push_instr_uint_uint(compile_ctx_t *ctx, vbsop_t op, unsigned arg1, unsigned arg2) +{ + unsigned ret; + + ret = push_instr(ctx, op); + if(!ret) + return E_OUTOFMEMORY; + + instr_ptr(ctx, ret)->arg1.uint = arg1; + instr_ptr(ctx, ret)->arg2.uint = arg2; + return S_OK; +} + static HRESULT push_instr_addr(compile_ctx_t *ctx, vbsop_t op, unsigned arg) { unsigned ret; @@ -497,6 +511,26 @@ static BOOL bind_local(compile_ctx_t *ctx, const WCHAR *name, int *ret) return FALSE; } +/* Resolve an identifier to a class property index at compile time. + * Only valid when compiling inside a class method (ctx->class_props != NULL). */ +static BOOL bind_class_prop(compile_ctx_t *ctx, const WCHAR *name, unsigned *ret) +{ + dim_decl_t *prop; + unsigned i; + + if(!ctx->class_props) + return FALSE; + + for(prop = ctx->class_props, i = 0; prop; prop = prop->next, i++) { + if(!vbs_wcsicmp(prop->name, name)) { + *ret = i; + return TRUE; + } + } + + return FALSE; +} + static HRESULT compile_args(compile_ctx_t *ctx, expression_t *args, unsigned *ret) { unsigned arg_cnt = 0; @@ -547,6 +581,7 @@ static HRESULT compile_member_call_expression(compile_ctx_t *ctx, member_express static HRESULT compile_member_expression(compile_ctx_t *ctx, member_expression_t *expr) { expression_t *const_expr; + unsigned prop_ref; int local_ref; if (expr->obj_expr) /* FIXME: we should probably have a dedicated opcode as well */ @@ -555,6 +590,9 @@ static HRESULT compile_member_expression(compile_ctx_t *ctx, member_expression_t if(bind_local(ctx, expr->identifier, &local_ref)) return push_instr_int(ctx, OP_local, local_ref); + if(bind_class_prop(ctx, expr->identifier, &prop_ref)) + return push_instr_uint(ctx, OP_local_prop, prop_ref); + const_expr = lookup_const_decls(ctx, expr->identifier, TRUE); if(const_expr) return compile_expression(ctx, const_expr); @@ -1233,6 +1271,7 @@ static HRESULT compile_assignment(compile_ctx_t *ctx, expression_t *left, expres if(!member_expr->obj_expr) { int local_ref; + unsigned prop_ref; if(bind_local(ctx, member_expr->identifier, &local_ref)) { hres = push_instr_int_uint(ctx, is_set ? OP_set_local : OP_assign_local, local_ref, args_cnt); @@ -1244,6 +1283,17 @@ static HRESULT compile_assignment(compile_ctx_t *ctx, expression_t *left, expres return S_OK; } + if(bind_class_prop(ctx, member_expr->identifier, &prop_ref)) { + hres = push_instr_uint_uint(ctx, is_set ? OP_set_local_prop : OP_assign_local_prop, + prop_ref, args_cnt); + if(FAILED(hres)) + return hres; + + if(!emit_catch(ctx, 0)) + return E_OUTOFMEMORY; + + return S_OK; + } } hres = push_instr_bstr_uint(ctx, op, member_expr->identifier, args_cnt); @@ -2023,6 +2073,8 @@ static HRESULT compile_class(compile_ctx_t *ctx, class_decl_t *class_decl) return E_OUTOFMEMORY; memset(class_desc->funcs, 0, class_desc->func_cnt*sizeof(*class_desc->funcs)); + ctx->class_props = class_decl->props; + for(func_decl = class_decl->funcs, i=1; func_decl; func_decl = func_decl->next, i++) { for(func_prop_decl = func_decl; func_prop_decl; func_prop_decl = func_prop_decl->next_prop_func) { if(func_prop_decl->is_default) { @@ -2060,6 +2112,8 @@ static HRESULT compile_class(compile_ctx_t *ctx, class_decl_t *class_decl) return hres; } + ctx->class_props = NULL; + for(prop_decl = class_decl->props; prop_decl; prop_decl = prop_decl->next) class_desc->prop_cnt++; diff --git a/dlls/vbscript/interp.c b/dlls/vbscript/interp.c index d89904fb9d5..0196901573a 100644 --- a/dlls/vbscript/interp.c +++ b/dlls/vbscript/interp.c @@ -894,6 +894,21 @@ static HRESULT interp_local(exec_ctx_t *ctx) return stack_push(ctx, &r); } +static HRESULT interp_local_prop(exec_ctx_t *ctx) +{ + const unsigned arg = ctx->instr->arg1.uint; + VARIANT *v; + VARIANT r; + + v = ctx->vbthis->props + arg; + + TRACE("%s\n", debugstr_variant(v)); + + V_VT(&r) = VT_BYREF|VT_VARIANT; + V_BYREF(&r) = v; + return stack_push(ctx, &r); +} + static HRESULT interp_ident(exec_ctx_t *ctx) { BSTR identifier = ctx->instr->arg1.bstr; @@ -1116,6 +1131,46 @@ static HRESULT interp_set_local(exec_ctx_t *ctx) return S_OK; } +static HRESULT interp_assign_local_prop(exec_ctx_t *ctx) +{ + const unsigned ref = ctx->instr->arg1.uint; + const unsigned arg_cnt = ctx->instr->arg2.uint; + DISPPARAMS dp; + HRESULT hres; + + TRACE("%u\n", ref); + + vbstack_to_dp(ctx, arg_cnt, TRUE, &dp); + hres = assign_local_var(ctx, ctx->vbthis->props + ref, DISPATCH_PROPERTYPUT, &dp); + if(FAILED(hres)) + return hres; + + stack_popn(ctx, arg_cnt+1); + return S_OK; +} + +static HRESULT interp_set_local_prop(exec_ctx_t *ctx) +{ + const unsigned ref = ctx->instr->arg1.uint; + const unsigned arg_cnt = ctx->instr->arg2.uint; + DISPPARAMS dp; + HRESULT hres; + + TRACE("%u %u\n", ref, arg_cnt); + + hres = stack_assume_disp(ctx, arg_cnt, NULL); + if(FAILED(hres)) + return hres; + + vbstack_to_dp(ctx, arg_cnt, TRUE, &dp); + hres = assign_local_var(ctx, ctx->vbthis->props + ref, DISPATCH_PROPERTYPUTREF, &dp); + if(FAILED(hres)) + return hres; + + stack_popn(ctx, arg_cnt + 1); + return S_OK; +} + static HRESULT interp_assign_member(exec_ctx_t *ctx) { BSTR identifier = ctx->instr->arg1.bstr; diff --git a/dlls/vbscript/vbscript.h b/dlls/vbscript/vbscript.h index 73c1841823d..1bc3f788577 100644 --- a/dlls/vbscript/vbscript.h +++ b/dlls/vbscript/vbscript.h @@ -240,9 +240,10 @@ typedef enum { #define OP_LIST \ X(add, 1, 0, 0) \ X(and, 1, 0, 0) \ - X(assign_ident, 1, ARG_BSTR, ARG_UINT) \ - X(assign_local, 1, ARG_INT, ARG_UINT) \ - X(assign_member, 1, ARG_BSTR, ARG_UINT) \ + X(assign_ident, 1, ARG_BSTR, ARG_UINT) \ + X(assign_local, 1, ARG_INT, ARG_UINT) \ + X(assign_local_prop, 1, ARG_UINT, ARG_UINT) \ + X(assign_member, 1, ARG_BSTR, ARG_UINT) \ X(bool, 1, ARG_INT, 0) \ X(catch, 1, ARG_ADDR, ARG_UINT) \ X(case, 0, ARG_ADDR, 0) \ @@ -272,10 +273,11 @@ typedef enum { X(incc_local, 1, ARG_INT, 0) \ X(int, 1, ARG_INT, 0) \ X(is, 1, 0, 0) \ - X(local, 1, ARG_INT, 0) \ X(jmp, 0, ARG_ADDR, 0) \ X(jmp_false, 0, ARG_ADDR, 0) \ X(jmp_true, 0, ARG_ADDR, 0) \ + X(local, 1, ARG_INT, 0) \ + X(local_prop, 1, ARG_UINT, 0) \ X(lt, 1, 0, 0) \ X(lteq, 1, 0, 0) \ X(mcall, 1, ARG_BSTR, ARG_UINT) \ @@ -299,6 +301,7 @@ typedef enum { X(retval, 1, 0, 0) \ X(set_ident, 1, ARG_BSTR, ARG_UINT) \ X(set_local, 1, ARG_INT, ARG_UINT) \ + X(set_local_prop, 1, ARG_UINT, ARG_UINT) \ X(set_member, 1, ARG_BSTR, ARG_UINT) \ X(stack, 1, ARG_UINT, 0) \ X(step, 0, ARG_ADDR, ARG_BSTR) \ -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10515