From 3aa7cee00e69c6a912abd1f23e846f6af35d9f36 Mon Sep 17 00:00:00 2001 From: Jacek Caban Date: Wed, 7 Dec 2011 11:00:20 +0100 Subject: [PATCH] jscript: Use bytecode for calls on identifier and member expressions. --- dlls/jscript/compile.c | 164 ++++++++++++++++++++++++++++----------- dlls/jscript/engine.c | 69 ++++++++++++---- dlls/jscript/engine.h | 7 +- dlls/jscript/parser.y | 2 +- dlls/jscript/tests/run.c | 2 +- 5 files changed, 178 insertions(+), 66 deletions(-) diff --git a/dlls/jscript/compile.c b/dlls/jscript/compile.c index 645217a2284..34ac0a4d60c 100644 --- a/dlls/jscript/compile.c +++ b/dlls/jscript/compile.c @@ -154,6 +154,24 @@ static HRESULT push_instr_bstr(compiler_ctx_t *ctx, jsop_t op, const WCHAR *arg) return S_OK; } +static HRESULT push_instr_bstr_uint(compiler_ctx_t *ctx, jsop_t op, const WCHAR *arg1, unsigned arg2) +{ + unsigned instr; + WCHAR *str; + + str = compiler_alloc_bstr(ctx, arg1); + if(!str) + return E_OUTOFMEMORY; + + instr = push_instr(ctx, op); + if(instr == -1) + return E_OUTOFMEMORY; + + instr_ptr(ctx, instr)->arg1.bstr = str; + instr_ptr(ctx, instr)->arg2.uint = arg2; + return S_OK; +} + static HRESULT push_instr_double(compiler_ctx_t *ctx, jsop_t op, double arg) { unsigned instr; @@ -222,6 +240,58 @@ static HRESULT compile_member_expression(compiler_ctx_t *ctx, member_expression_ return push_instr_bstr(ctx, OP_member, expr->identifier); } +static inline BOOL is_memberid_expr(expression_type_t type) +{ + return type == EXPR_IDENT || type == EXPR_MEMBER || type == EXPR_ARRAY; +} + +static HRESULT compile_memberid_expression(compiler_ctx_t *ctx, expression_t *expr, unsigned flags) +{ + HRESULT hres = S_OK; + + switch(expr->type) { + case EXPR_IDENT: { + identifier_expression_t *ident_expr = (identifier_expression_t*)expr; + + hres = push_instr_bstr_uint(ctx, OP_identid, ident_expr->identifier, flags); + break; + } + case EXPR_ARRAY: { + array_expression_t *array_expr = (array_expression_t*)expr; + + hres = compile_expression(ctx, array_expr->member_expr); + if(FAILED(hres)) + return hres; + + hres = compile_expression(ctx, array_expr->expression); + if(FAILED(hres)) + return hres; + + hres = push_instr_uint(ctx, OP_memberid, flags); + break; + } + case EXPR_MEMBER: { + member_expression_t *member_expr = (member_expression_t*)expr; + + hres = compile_expression(ctx, member_expr->expression); + if(FAILED(hres)) + return hres; + + /* FIXME: Potential optimization */ + hres = push_instr_str(ctx, OP_str, member_expr->identifier); + if(FAILED(hres)) + return hres; + + hres = push_instr_uint(ctx, OP_memberid, flags); + break; + } + default: + assert(0); + } + + return hres; +} + /* ECMA-262 3rd Edition 11.14 */ static HRESULT compile_comma_expression(compiler_ctx_t *ctx, binary_expression_t *expr) { @@ -325,6 +395,40 @@ static HRESULT compile_interp_fallback(compiler_ctx_t *ctx, expression_t *expr) return S_OK; } +static HRESULT compile_call_expression(compiler_ctx_t *ctx, call_expression_t *expr, BOOL *no_ret) +{ + unsigned arg_cnt = 0; + argument_t *arg; + unsigned instr; + HRESULT hres; + + if(!is_memberid_expr(expr->expression->type)) { + expr->expr.eval = call_expression_eval; + return compile_interp_fallback(ctx, &expr->expr); + } + + hres = compile_memberid_expression(ctx, expr->expression, 0); + if(FAILED(hres)) + return hres; + + for(arg = expr->argument_list; arg; arg = arg->next) { + hres = compile_expression(ctx, arg->expr); + if(FAILED(hres)) + return hres; + arg_cnt++; + } + + instr = push_instr(ctx, OP_call_member); + if(instr == -1) + return E_OUTOFMEMORY; + + instr_ptr(ctx, instr)->arg1.uint = arg_cnt; + instr_ptr(ctx, instr)->arg2.lng = no_ret == NULL; + if(no_ret) + *no_ret = TRUE; + return S_OK; +} + static HRESULT compile_delete_expression(compiler_ctx_t *ctx, unary_expression_t *expr) { HRESULT hres; @@ -373,47 +477,7 @@ static HRESULT compile_assign_expression(compiler_ctx_t *ctx, binary_expression_ { HRESULT hres; - switch(expr->expression1->type) { - case EXPR_IDENT: { - identifier_expression_t *ident_expr = (identifier_expression_t*)expr->expression1; - - hres = push_instr_bstr(ctx, OP_identid, ident_expr->identifier); - if(FAILED(hres)) - return hres; - break; - } - case EXPR_ARRAY: { - array_expression_t *array_expr = (array_expression_t*)expr->expression1; - - hres = compile_expression(ctx, array_expr->member_expr); - if(FAILED(hres)) - return hres; - - hres = compile_expression(ctx, array_expr->expression); - if(FAILED(hres)) - return hres; - - if(push_instr(ctx, OP_memberid) == -1) - return E_OUTOFMEMORY; - break; - } - case EXPR_MEMBER: { - member_expression_t *member_expr = (member_expression_t*)expr->expression1; - - hres = compile_expression(ctx, member_expr->expression); - if(FAILED(hres)) - return hres; - - /* FIXME: Potential optimization */ - hres = push_instr_str(ctx, OP_str, member_expr->identifier); - if(FAILED(hres)) - return hres; - - if(push_instr(ctx, OP_memberid) == -1) - return E_OUTOFMEMORY; - break; - } - default: + if(!is_memberid_expr(expr->expression1->type)) { hres = compile_expression(ctx, expr->expression1); if(FAILED(hres)) return hres; @@ -428,6 +492,10 @@ static HRESULT compile_assign_expression(compiler_ctx_t *ctx, binary_expression_ return push_instr_uint(ctx, OP_throw, JS_E_ILLEGAL_ASSIGN); } + hres = compile_memberid_expression(ctx, expr->expression1, fdexNameEnsure); + if(FAILED(hres)) + return hres; + if(op != OP_LAST && push_instr(ctx, OP_refval) == -1) return E_OUTOFMEMORY; @@ -480,7 +548,7 @@ static HRESULT compile_literal(compiler_ctx_t *ctx, literal_t *literal) } } -static HRESULT compile_expression(compiler_ctx_t *ctx, expression_t *expr) +static HRESULT compile_expression_noret(compiler_ctx_t *ctx, expression_t *expr, BOOL *no_ret) { switch(expr->type) { case EXPR_ADD: @@ -507,6 +575,8 @@ static HRESULT compile_expression(compiler_ctx_t *ctx, expression_t *expr) return compile_unary_expression(ctx, (unary_expression_t*)expr, OP_bneg); case EXPR_BOR: return compile_binary_expression(ctx, (binary_expression_t*)expr, OP_or); + case EXPR_CALL: + return compile_call_expression(ctx, (call_expression_t*)expr, no_ret); case EXPR_COMMA: return compile_comma_expression(ctx, (binary_expression_t*)expr); case EXPR_COND: @@ -569,6 +639,11 @@ static HRESULT compile_expression(compiler_ctx_t *ctx, expression_t *expr) return S_OK; } +static HRESULT compile_expression(compiler_ctx_t *ctx, expression_t *expr) +{ + return compile_expression_noret(ctx, expr, NULL); +} + void release_bytecode(bytecode_t *code) { unsigned i; @@ -587,8 +662,9 @@ void release_compiler(compiler_ctx_t *ctx) heap_free(ctx); } -HRESULT compile_subscript(parser_ctx_t *parser, expression_t *expr, unsigned *ret_off) +HRESULT compile_subscript(parser_ctx_t *parser, expression_t *expr, BOOL do_ret, unsigned *ret_off) { + BOOL no_ret = FALSE; HRESULT hres; if(!parser->code) { @@ -608,7 +684,7 @@ HRESULT compile_subscript(parser_ctx_t *parser, expression_t *expr, unsigned *re } *ret_off = parser->compiler->code_off; - hres = compile_expression(parser->compiler, expr); + hres = compile_expression_noret(parser->compiler, expr, do_ret ? NULL : &no_ret); if(FAILED(hres)) return hres; diff --git a/dlls/jscript/engine.c b/dlls/jscript/engine.c index b40cd75b0b9..017030387ca 100644 --- a/dlls/jscript/engine.c +++ b/dlls/jscript/engine.c @@ -182,12 +182,12 @@ static inline IDispatch *stack_pop_objid(exec_ctx_t *ctx, DISPID *id) return V_DISPATCH(stack_pop(ctx)); } -static inline IDispatch *stack_top_objid(exec_ctx_t *ctx, DISPID *id) +static inline IDispatch *stack_topn_objid(exec_ctx_t *ctx, unsigned n, DISPID *id) { - assert(V_VT(stack_top(ctx)) == VT_INT && V_VT(stack_topn(ctx, 1)) == VT_DISPATCH); + assert(V_VT(stack_topn(ctx, n)) == VT_INT && V_VT(stack_topn(ctx, n+1)) == VT_DISPATCH); - *id = V_INT(stack_top(ctx)); - return V_DISPATCH(stack_topn(ctx, 1)); + *id = V_INT(stack_topn(ctx, n)); + return V_DISPATCH(stack_topn(ctx, n+1)); } static void exprval_release(exprval_t *val) @@ -1638,13 +1638,14 @@ static HRESULT interp_member(exec_ctx_t *ctx) /* ECMA-262 3rd Edition 11.2.1 */ static HRESULT interp_memberid(exec_ctx_t *ctx) { + const unsigned arg = ctx->parser->code->instrs[ctx->ip].arg1.lng; VARIANT *objv, *namev; IDispatch *obj; BSTR name; DISPID id; HRESULT hres; - TRACE("\n"); + TRACE("%x\n", arg); namev = stack_pop(ctx); objv = stack_pop(ctx); @@ -1660,11 +1661,16 @@ static HRESULT interp_memberid(exec_ctx_t *ctx) if(FAILED(hres)) return hres; - hres = disp_get_id(ctx->parser->script, obj, name, fdexNameEnsure, &id); + hres = disp_get_id(ctx->parser->script, obj, name, arg, &id); SysFreeString(name); if(FAILED(hres)) { IDispatch_Release(obj); - return hres; + if(hres == DISP_E_UNKNOWNNAME && !(arg & fdexNameEnsure)) { + obj = NULL; + id = JS_E_INVALID_PROPERTY; + }else { + return hres; + } } return stack_push_objid(ctx, obj, id); @@ -1680,7 +1686,7 @@ static HRESULT interp_refval(exec_ctx_t *ctx) TRACE("\n"); - disp = stack_top_objid(ctx, &id); + disp = stack_topn_objid(ctx, 0, &id); if(!disp) return throw_reference_error(ctx->parser->script, &ctx->ei, JS_E_ILLEGAL_ASSIGN, NULL); @@ -1817,9 +1823,7 @@ HRESULT call_expression_eval(script_ctx_t *ctx, expression_t *_expr, DWORD flags hres = throw_type_error(ctx, ei, JS_E_INVALID_PROPERTY, NULL); break; case EXPRVAL_IDREF: - hres = disp_call(ctx, exprval.u.idref.disp, exprval.u.idref.id, - DISPATCH_METHOD, &dp, flags & EXPR_NOVAL ? NULL : &var, ei, NULL/*FIXME*/); - break; + assert(0); case EXPRVAL_INVALID: hres = throw_type_error(ctx, ei, JS_E_OBJECT_EXPECTED, NULL); break; @@ -1845,6 +1849,33 @@ HRESULT call_expression_eval(script_ctx_t *ctx, expression_t *_expr, DWORD flags return S_OK; } +/* ECMA-262 3rd Edition 11.2.3 */ +static HRESULT interp_call_member(exec_ctx_t *ctx) +{ + const unsigned argn = ctx->parser->code->instrs[ctx->ip].arg1.uint; + const int do_ret = ctx->parser->code->instrs[ctx->ip].arg2.lng; + IDispatch *obj; + DISPPARAMS dp; + VARIANT v; + DISPID id; + HRESULT hres; + + TRACE("%d %d\n", argn, do_ret); + + obj = stack_topn_objid(ctx, argn, &id); + if(!obj) + return throw_type_error(ctx->parser->script, &ctx->ei, id, NULL); + + jsstack_to_dp(ctx, argn, &dp); + hres = disp_call(ctx->parser->script, obj, id, DISPATCH_METHOD, &dp, do_ret ? &v : NULL, &ctx->ei, NULL/*FIXME*/); + if(FAILED(hres)) + return hres; + + stack_popn(ctx, argn+2); + return do_ret ? stack_push(ctx, &v) : S_OK; + +} + /* ECMA-262 3rd Edition 11.1.1 */ static HRESULT interp_this(exec_ctx_t *ctx) { @@ -1903,19 +1934,20 @@ static HRESULT interp_ident(exec_ctx_t *ctx) static HRESULT interp_identid(exec_ctx_t *ctx) { const BSTR arg = ctx->parser->code->instrs[ctx->ip].arg1.bstr; + const unsigned flags = ctx->parser->code->instrs[ctx->ip].arg2.uint; exprval_t exprval; HRESULT hres; - TRACE("%s\n", debugstr_w(arg)); + TRACE("%s %x\n", debugstr_w(arg), flags); - hres = identifier_eval(ctx->parser->script, arg, EXPR_NEWREF, &ctx->ei, &exprval); + hres = identifier_eval(ctx->parser->script, arg, (flags&fdexNameEnsure) ? EXPR_NEWREF : 0, &ctx->ei, &exprval); if(FAILED(hres)) return hres; if(exprval.type != EXPRVAL_IDREF) { WARN("invalid ref\n"); exprval_release(&exprval); - return stack_push_objid(ctx, NULL, -1); + return stack_push_objid(ctx, NULL, JS_E_OBJECT_EXPECTED); } return stack_push_objid(ctx, exprval.u.idref.disp, exprval.u.idref.id); @@ -3437,10 +3469,13 @@ static HRESULT interp_expression_eval(script_ctx_t *ctx, expression_t *expr, DWO return hres; } - assert(exec_ctx->top == prev_top+1); + assert(exec_ctx->top == prev_top+1 || ((flags&EXPR_NOVAL) && exec_ctx->top == prev_top)); ret->type = EXPRVAL_VARIANT; - ret->u.var = *stack_pop(exec_ctx); + if(exec_ctx->top == prev_top) + V_VT(&ret->u.var) = VT_EMPTY; + else + ret->u.var = *stack_pop(exec_ctx); return S_OK; } @@ -3450,7 +3485,7 @@ HRESULT compiled_expression_eval(script_ctx_t *ctx, expression_t *expr, DWORD fl TRACE("\n"); - hres = compile_subscript(ctx->exec_ctx->parser, expr, &expr->instr_off); + hres = compile_subscript(ctx->exec_ctx->parser, expr, !(flags & EXPR_NOVAL), &expr->instr_off); if(FAILED(hres)) return hres; diff --git a/dlls/jscript/engine.h b/dlls/jscript/engine.h index d613267ea6f..88c61fc0277 100644 --- a/dlls/jscript/engine.h +++ b/dlls/jscript/engine.h @@ -46,6 +46,7 @@ typedef struct _func_stack { X(assign, 1, 0,0) \ X(bool, 1, ARG_INT, 0) \ X(bneg, 1, 0,0) \ + X(call_member,1, ARG_UINT, ARG_UINT) \ X(delete, 1, 0,0) \ X(div, 1, 0,0) \ X(double, 1, ARG_SBL, 0) \ @@ -54,7 +55,7 @@ typedef struct _func_stack { X(gt, 1, 0,0) \ X(gteq, 1, 0,0) \ X(ident, 1, ARG_BSTR, 0) \ - X(identid, 1, ARG_BSTR, 0) \ + X(identid, 1, ARG_BSTR, ARG_INT) \ X(in, 1, 0,0) \ X(int, 1, ARG_INT, 0) \ X(jmp, 0, ARG_ADDR, 0) \ @@ -63,7 +64,7 @@ typedef struct _func_stack { X(lt, 1, 0,0) \ X(lteq, 1, 0,0) \ X(member, 1, ARG_BSTR, 0) \ - X(memberid, 1, 0,0) \ + X(memberid, 1, ARG_UINT, 0) \ X(minus, 1, 0,0) \ X(mod, 1, 0,0) \ X(mul, 1, 0,0) \ @@ -580,4 +581,4 @@ HRESULT assign_and_expression_eval(script_ctx_t*,expression_t*,DWORD,jsexcept_t* HRESULT compiled_expression_eval(script_ctx_t*,expression_t*,DWORD,jsexcept_t*,exprval_t*) DECLSPEC_HIDDEN; -HRESULT compile_subscript(parser_ctx_t*,expression_t*,unsigned*) DECLSPEC_HIDDEN; +HRESULT compile_subscript(parser_ctx_t*,expression_t*,BOOL,unsigned*) DECLSPEC_HIDDEN; diff --git a/dlls/jscript/parser.y b/dlls/jscript/parser.y index bf3732defc9..f02fc67c4f3 100644 --- a/dlls/jscript/parser.y +++ b/dlls/jscript/parser.y @@ -1356,7 +1356,7 @@ static const expression_eval_t expression_eval_table[] = { array_expression_eval, member_expression_eval, compiled_expression_eval, - call_expression_eval, + compiled_expression_eval, compiled_expression_eval, function_expression_eval, identifier_expression_eval, diff --git a/dlls/jscript/tests/run.c b/dlls/jscript/tests/run.c index 3ad9600631b..85e57ba0190 100644 --- a/dlls/jscript/tests/run.c +++ b/dlls/jscript/tests/run.c @@ -729,7 +729,7 @@ static HRESULT WINAPI Global_InvokeEx(IDispatchEx *iface, DISPID id, LCID lcid, ok(wFlags == INVOKE_FUNC, "wFlags = %x\n", wFlags); ok(pdp != NULL, "pdp == NULL\n"); - todo_wine ok(pdp->rgvarg != NULL, "rgvarg == NULL\n"); + ok(pdp->rgvarg != NULL, "rgvarg == NULL\n"); ok(!pdp->rgdispidNamedArgs, "rgdispidNamedArgs != NULL\n"); ok(!pdp->cArgs, "cArgs = %d\n", pdp->cArgs); ok(!pdp->cNamedArgs, "cNamedArgs = %d\n", pdp->cNamedArgs);