From d3d2f063b617d3c979bc62c0a6edbcd3b944d344 Mon Sep 17 00:00:00 2001 From: Jacek Caban Date: Mon, 5 Dec 2011 11:11:29 +0100 Subject: [PATCH] jscript: Use bytecode for assignment to identifier. --- dlls/jscript/compile.c | 35 ++++++++++++++++-- dlls/jscript/engine.c | 72 ++++++++++++++++++++++++++++++++++++++ dlls/jscript/engine.h | 4 ++- dlls/jscript/parser.y | 2 +- dlls/jscript/tests/api.js | 4 +++ dlls/jscript/tests/lang.js | 3 ++ 6 files changed, 116 insertions(+), 4 deletions(-) diff --git a/dlls/jscript/compile.c b/dlls/jscript/compile.c index ed42b4d0c67..3679dfe32e8 100644 --- a/dlls/jscript/compile.c +++ b/dlls/jscript/compile.c @@ -345,6 +345,35 @@ static HRESULT compile_delete_expression(compiler_ctx_t *ctx, unary_expression_t return S_OK; } +static HRESULT compile_assign_expression(compiler_ctx_t *ctx, binary_expression_t *expr) +{ + 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; + } + default: + expr->expr.eval = assign_expression_eval; + return compile_interp_fallback(ctx, &expr->expr); + } + + + hres = compile_expression(ctx, expr->expression2); + if(FAILED(hres)) + return hres; + + if(push_instr(ctx, OP_assign) == -1) + return E_OUTOFMEMORY; + + return S_OK; +} + static HRESULT compile_literal(compiler_ctx_t *ctx, literal_t *literal) { switch(literal->type) { @@ -384,10 +413,12 @@ static HRESULT compile_literal(compiler_ctx_t *ctx, literal_t *literal) static HRESULT compile_expression(compiler_ctx_t *ctx, expression_t *expr) { switch(expr->type) { - case EXPR_AND: - return compile_logical_expression(ctx, (binary_expression_t*)expr, OP_jmp_z); case EXPR_ADD: return compile_binary_expression(ctx, (binary_expression_t*)expr, OP_add); + case EXPR_AND: + return compile_logical_expression(ctx, (binary_expression_t*)expr, OP_jmp_z); + case EXPR_ASSIGN: + return compile_assign_expression(ctx, (binary_expression_t*)expr); case EXPR_BITNEG: return compile_unary_expression(ctx, (unary_expression_t*)expr, OP_bneg); case EXPR_BOR: diff --git a/dlls/jscript/engine.c b/dlls/jscript/engine.c index d24392ca697..c445eab15eb 100644 --- a/dlls/jscript/engine.c +++ b/dlls/jscript/engine.c @@ -102,6 +102,22 @@ static inline HRESULT stack_push_int(exec_ctx_t *ctx, INT n) return stack_push(ctx, &v); } +static HRESULT stack_push_objid(exec_ctx_t *ctx, IDispatch *disp, DISPID id) +{ + VARIANT v; + HRESULT hres; + + V_VT(&v) = VT_DISPATCH; + V_DISPATCH(&v) = disp; + hres = stack_push(ctx, &v); + if(FAILED(hres)) + return hres; + + V_VT(&v) = VT_INT; + V_INT(&v) = id; + return stack_push(ctx, &v); +} + static inline VARIANT *stack_top(exec_ctx_t *ctx) { assert(ctx->top); @@ -142,6 +158,14 @@ static inline HRESULT stack_pop_int(exec_ctx_t *ctx, INT *r) return to_int32(ctx->parser->script, stack_pop(ctx), &ctx->ei, r); } +static inline IDispatch *stack_pop_objid(exec_ctx_t *ctx, DISPID *id) +{ + assert(V_VT(stack_top(ctx)) == VT_INT && V_VT(stack_topn(ctx, 1)) == VT_DISPATCH); + + *id = V_INT(stack_pop(ctx)); + return V_DISPATCH(stack_pop(ctx)); +} + static void exprval_release(exprval_t *val) { switch(val->type) { @@ -1757,6 +1781,28 @@ static HRESULT interp_ident(exec_ctx_t *ctx) return stack_push(ctx, &v); } +/* ECMA-262 3rd Edition 10.1.4 */ +static HRESULT interp_identid(exec_ctx_t *ctx) +{ + const BSTR arg = ctx->parser->code->instrs[ctx->ip].arg1.bstr; + exprval_t exprval; + HRESULT hres; + + TRACE("%s\n", debugstr_w(arg)); + + hres = identifier_eval(ctx->parser->script, arg, EXPR_NEWREF, &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, exprval.u.idref.disp, exprval.u.idref.id); +} + /* ECMA-262 3rd Edition 7.8.1 */ static HRESULT interp_null(exec_ctx_t *ctx) { @@ -3261,6 +3307,32 @@ HRESULT assign_expression_eval(script_ctx_t *ctx, expression_t *_expr, DWORD fla return S_OK; } +/* ECMA-262 3rd Edition 11.13.1 */ +static HRESULT interp_assign(exec_ctx_t *ctx) +{ + IDispatch *disp; + DISPID id; + VARIANT *v; + HRESULT hres; + + TRACE("\n"); + + v = stack_pop(ctx); + disp = stack_pop_objid(ctx, &id); + + if(!disp) + return throw_reference_error(ctx->parser->script, &ctx->ei, JS_E_ILLEGAL_ASSIGN, NULL); + + hres = disp_propput(ctx->parser->script, disp, id, v, &ctx->ei, NULL/*FIXME*/); + IDispatch_Release(disp); + if(FAILED(hres)) { + VariantClear(v); + return hres; + } + + return stack_push(ctx, v); +} + /* ECMA-262 3rd Edition 11.13.2 */ HRESULT assign_lshift_expression_eval(script_ctx_t *ctx, expression_t *_expr, DWORD flags, jsexcept_t *ei, exprval_t *ret) { diff --git a/dlls/jscript/engine.h b/dlls/jscript/engine.h index e86db0f79d1..12329e87b32 100644 --- a/dlls/jscript/engine.h +++ b/dlls/jscript/engine.h @@ -43,6 +43,7 @@ typedef struct _func_stack { #define OP_LIST \ X(add, 1, 0,0) \ + X(assign, 1, 0,0) \ X(bool, 1, ARG_INT, 0) \ X(bneg, 1, 0,0) \ X(delete, 1, 0,0) \ @@ -51,8 +52,9 @@ typedef struct _func_stack { X(eq, 1, 0,0) \ X(eq2, 1, 0,0) \ X(gt, 1, 0,0) \ - X(gteq, 1, 0,0) \ + X(gteq, 1, 0,0) \ X(ident, 1, ARG_BSTR, 0) \ + X(identid, 1, ARG_BSTR, 0) \ X(in, 1, 0,0) \ X(int, 1, ARG_INT, 0) \ X(jmp, 0, ARG_ADDR, 0) \ diff --git a/dlls/jscript/parser.y b/dlls/jscript/parser.y index c1c5df66282..55f8aacd8fa 100644 --- a/dlls/jscript/parser.y +++ b/dlls/jscript/parser.y @@ -1340,7 +1340,7 @@ static const expression_eval_t expression_eval_table[] = { left_shift_expression_eval, right_shift_expression_eval, right2_shift_expression_eval, - assign_expression_eval, + compiled_expression_eval, assign_lshift_expression_eval, assign_rshift_expression_eval, assign_rrshift_expression_eval, diff --git a/dlls/jscript/tests/api.js b/dlls/jscript/tests/api.js index d99364f1760..b5ac66a7426 100644 --- a/dlls/jscript/tests/api.js +++ b/dlls/jscript/tests/api.js @@ -2072,6 +2072,10 @@ testSyntaxError("/* @cc_on @*/ @_jscript_version", "E_DISABLED_CC"); // ReferenceError tests testException(function() {test = function() {}}, "E_ILLEGAL_ASSIGN"); +tmp = false; +testException(function() {test = (tmp = true);}, "E_ILLEGAL_ASSIGN"); +ok(tmp, "expr value on invalid assign not evaluated"); + // RegExpError tests testException(function() {RegExp(/a/, "g");}, "E_REGEXP_SYNTAX_ERROR"); diff --git a/dlls/jscript/tests/lang.js b/dlls/jscript/tests/lang.js index 4f3d5099d1a..1dd0c0276b1 100644 --- a/dlls/jscript/tests/lang.js +++ b/dlls/jscript/tests/lang.js @@ -118,6 +118,9 @@ ok(testFunc1(true, "test") === true, "testFunc1 not returned true"); ok(testFunc1.arguments === null, "testFunc1.arguments = " + testFunc1.arguments); +(tmp) = 3; +ok(tmp === 3, "tmp = " + tmp); + function testRecFunc(x) { ok(testRecFunc.arguments === arguments, "testRecFunc.arguments = " + testRecFunc.arguments); if(x) {