diff --git a/dlls/jscript/compile.c b/dlls/jscript/compile.c index 9e8cb471e15..476f7d14ab7 100644 --- a/dlls/jscript/compile.c +++ b/dlls/jscript/compile.c @@ -1311,6 +1311,96 @@ static HRESULT compile_throw_statement(compiler_ctx_t *ctx, expression_statement return push_instr(ctx, OP_throw) == -1 ? E_OUTOFMEMORY : S_OK; } +/* ECMA-262 3rd Edition 12.14 */ +static HRESULT compile_try_statement(compiler_ctx_t *ctx, try_statement_t *stat) +{ + unsigned off_backup, push_except; + BOOL prev_no_fallback; + BSTR ident; + HRESULT hres; + + off_backup = ctx->code_off; + prev_no_fallback = ctx->no_fallback; + + push_except = push_instr(ctx, OP_push_except); + if(push_except == -1) + return E_OUTOFMEMORY; + + if(stat->catch_block) { + ident = compiler_alloc_bstr(ctx, stat->catch_block->identifier); + if(!ident) + return E_OUTOFMEMORY; + }else { + ident = NULL; + } + + instr_ptr(ctx, push_except)->arg2.bstr = ident; + + ctx->no_fallback = TRUE; + hres = compile_statement(ctx, stat->try_statement); + ctx->no_fallback = prev_no_fallback; + if(hres == E_NOTIMPL) { + ctx->code_off = off_backup; + stat->stat.eval = try_statement_eval; + return compile_interp_fallback(ctx, &stat->stat); + } + if(FAILED(hres)) + return hres; + + if(push_instr(ctx, OP_pop_except) == -1) + return E_OUTOFMEMORY; + + if(stat->catch_block) { + unsigned jmp_finally; + + jmp_finally = push_instr(ctx, OP_jmp); + if(jmp_finally == -1) + return E_OUTOFMEMORY; + + instr_ptr(ctx, push_except)->arg1.uint = ctx->code_off; + + ctx->no_fallback = TRUE; + hres = compile_statement(ctx, stat->catch_block->statement); + ctx->no_fallback = prev_no_fallback; + if(hres == E_NOTIMPL) { + ctx->code_off = off_backup; + stat->stat.eval = try_statement_eval; + return compile_interp_fallback(ctx, &stat->stat); + } + if(FAILED(hres)) + return hres; + + if(push_instr(ctx, OP_pop_scope) == -1) + return E_OUTOFMEMORY; + + instr_ptr(ctx, jmp_finally)->arg1.uint = ctx->code_off; + }else { + instr_ptr(ctx, push_except)->arg1.uint = ctx->code_off; + } + + if(stat->finally_statement) { + /* FIXME: avoid */ + if(push_instr(ctx, OP_pop) == -1) + return E_OUTOFMEMORY; + + ctx->no_fallback = TRUE; + hres = compile_statement(ctx, stat->finally_statement); + ctx->no_fallback = prev_no_fallback; + if(hres == E_NOTIMPL) { + ctx->code_off = off_backup; + stat->stat.eval = try_statement_eval; + return compile_interp_fallback(ctx, &stat->stat); + } + if(FAILED(hres)) + return hres; + + if(!stat->catch_block && push_instr(ctx, OP_end_finally) == -1) + return E_OUTOFMEMORY; + } + + return S_OK; +} + static HRESULT compile_statement(compiler_ctx_t *ctx, statement_t *stat) { switch(stat->type) { @@ -1332,6 +1422,8 @@ static HRESULT compile_statement(compiler_ctx_t *ctx, statement_t *stat) return compile_switch_statement(ctx, (switch_statement_t*)stat); case STAT_THROW: return compile_throw_statement(ctx, (expression_statement_t*)stat); + case STAT_TRY: + return compile_try_statement(ctx, (try_statement_t*)stat); case STAT_VAR: return compile_var_statement(ctx, (var_statement_t*)stat); case STAT_WHILE: diff --git a/dlls/jscript/engine.c b/dlls/jscript/engine.c index b7b3248a1ef..753e7e6c969 100644 --- a/dlls/jscript/engine.c +++ b/dlls/jscript/engine.c @@ -47,6 +47,15 @@ struct _return_type_t { jsexcept_t ei; }; +struct _except_frame_t { + unsigned stack_top; + scope_chain_t *scope; + unsigned catch_off; + BSTR ident; + + except_frame_t *next; +}; + static inline HRESULT stat_eval(script_ctx_t *ctx, statement_t *stat, return_type_t *rt, VARIANT *ret) { return stat->eval(ctx, stat, rt, ret); @@ -1324,6 +1333,77 @@ HRESULT try_statement_eval(script_ctx_t *ctx, statement_t *_stat, return_type_t return S_OK; } +/* ECMA-262 3rd Edition 12.14 */ +static HRESULT interp_push_except(exec_ctx_t *ctx) +{ + const unsigned arg1 = ctx->parser->code->instrs[ctx->ip].arg1.uint; + const BSTR arg2 = ctx->parser->code->instrs[ctx->ip].arg2.bstr; + except_frame_t *except; + unsigned stack_top; + + TRACE("\n"); + + stack_top = ctx->top; + + if(!arg2) { + HRESULT hres; + + hres = stack_push_bool(ctx, TRUE); + if(FAILED(hres)) + return hres; + } + + except = heap_alloc(sizeof(*except)); + if(!except) + return E_OUTOFMEMORY; + + except->stack_top = stack_top; + except->scope = ctx->scope_chain; + except->catch_off = arg1; + except->ident = arg2; + except->next = ctx->except_frame; + ctx->except_frame = except; + return S_OK; +} + +/* ECMA-262 3rd Edition 12.14 */ +static HRESULT interp_pop_except(exec_ctx_t *ctx) +{ + except_frame_t *except; + + TRACE("\n"); + + except = ctx->except_frame; + assert(except != NULL); + + ctx->except_frame = except->next; + heap_free(except); + return S_OK; +} + +/* ECMA-262 3rd Edition 12.14 */ +static HRESULT interp_end_finally(exec_ctx_t *ctx) +{ + VARIANT *v; + + TRACE("\n"); + + v = stack_pop(ctx); + + assert(V_VT(stack_top(ctx)) == VT_BOOL); + if(!V_BOOL(stack_top(ctx))) { + TRACE("passing exception\n"); + + VariantClear(v); + stack_popn(ctx, 1); + ctx->rt->ei.var = *stack_pop(ctx); + return DISP_E_EXCEPTION; + } + + *stack_top(ctx) = *v; + return S_OK; +} + /* ECMA-262 3rd Edition 13 */ static HRESULT interp_func(exec_ctx_t *ctx) { @@ -3072,9 +3152,68 @@ OP_LIST #undef X }; +static HRESULT unwind_exception(exec_ctx_t *ctx) +{ + except_frame_t *except_frame; + VARIANT except_val; + BSTR ident; + HRESULT hres; + + except_frame = ctx->except_frame; + ctx->except_frame = except_frame->next; + + assert(except_frame->stack_top <= ctx->top); + stack_popn(ctx, ctx->top - except_frame->stack_top); + + while(except_frame->scope != ctx->scope_chain) + scope_pop(&ctx->scope_chain); + + ctx->ip = except_frame->catch_off; + + assert(ctx->rt->type == RT_NORMAL); + except_val = ctx->rt->ei.var; + memset(&ctx->rt->ei, 0, sizeof(ctx->rt->ei)); + + ident = except_frame->ident; + heap_free(except_frame); + + if(ident) { + jsdisp_t *scope_obj; + + hres = create_dispex(ctx->parser->script, NULL, NULL, &scope_obj); + if(SUCCEEDED(hres)) { + hres = jsdisp_propput_name(scope_obj, ident, &except_val, &ctx->rt->ei, NULL/*FIXME*/); + if(FAILED(hres)) + jsdisp_release(scope_obj); + } + VariantClear(&except_val); + if(FAILED(hres)) + return hres; + + hres = scope_push(ctx->scope_chain, scope_obj, &ctx->scope_chain); + jsdisp_release(scope_obj); + }else { + VARIANT v; + + hres = stack_push(ctx, &except_val); + if(FAILED(hres)) + return hres; + + hres = stack_push_bool(ctx, FALSE); + if(FAILED(hres)) + return hres; + + V_VT(&v) = VT_EMPTY; + hres = stack_push(ctx, &v); + } + + return hres; +} + HRESULT compiled_statement_eval(script_ctx_t *ctx, statement_t *stat, return_type_t *rt, VARIANT *ret) { exec_ctx_t *exec_ctx = ctx->exec_ctx; + except_frame_t *prev_except_frame; unsigned prev_ip, prev_top; scope_chain_t *prev_scope; return_type_t *prev_rt; @@ -3093,23 +3232,35 @@ HRESULT compiled_statement_eval(script_ctx_t *ctx, statement_t *stat, return_typ prev_rt = exec_ctx->rt; prev_top = exec_ctx->top; prev_scope = exec_ctx->scope_chain; + prev_except_frame = exec_ctx->except_frame; prev_ip = exec_ctx->ip; prev_ei = exec_ctx->ei; exec_ctx->ip = stat->instr_off; exec_ctx->rt = rt; exec_ctx->ei = &rt->ei; + exec_ctx->except_frame = NULL; while(exec_ctx->ip != -1 && exec_ctx->rt->type == RT_NORMAL) { op = exec_ctx->parser->code->instrs[exec_ctx->ip].op; hres = op_funcs[op](exec_ctx); - if(FAILED(hres)) - break; - exec_ctx->ip += op_move[op]; + if(FAILED(hres)) { + TRACE("EXCEPTION\n"); + + if(!exec_ctx->except_frame) + break; + + hres = unwind_exception(exec_ctx); + if(FAILED(hres)) + break; + }else { + exec_ctx->ip += op_move[op]; + } } exec_ctx->rt = prev_rt; exec_ctx->ip = prev_ip; exec_ctx->ei = prev_ei; + exec_ctx->except_frame = prev_except_frame; if(FAILED(hres)) { stack_popn(exec_ctx, exec_ctx->top-prev_top); diff --git a/dlls/jscript/engine.h b/dlls/jscript/engine.h index 9aa56166e03..53f14cc6a67 100644 --- a/dlls/jscript/engine.h +++ b/dlls/jscript/engine.h @@ -58,7 +58,8 @@ typedef struct _func_stack { X(delete, 1, 0,0) \ X(delete_ident,1,ARG_BSTR, 0) \ X(div, 1, 0,0) \ - X(double, 1, ARG_SBL, 0) \ + X(double, 1, ARG_DBL, 0) \ + X(end_finally,1, 0,0) \ X(eq, 1, 0,0) \ X(eq2, 1, 0,0) \ X(forin, 0, ARG_UINT, 0) \ @@ -90,9 +91,11 @@ typedef struct _func_stack { X(obj_prop, 1, ARG_BSTR, 0) \ X(or, 1, 0,0) \ X(pop, 1, 0,0) \ + X(pop_except, 1, 0,0) \ X(pop_scope, 1, 0,0) \ X(postinc, 1, ARG_INT, 0) \ X(preinc, 1, ARG_INT, 0) \ + X(push_except,1, ARG_UINT, ARG_BSTR) \ X(push_scope, 1, 0,0) \ X(regexp, 1, ARG_STR, ARG_INT) \ X(rshift, 1, 0,0) \ @@ -222,6 +225,7 @@ static inline void scope_addref(scope_chain_t *scope) } typedef struct _return_type_t return_type_t; +typedef struct _except_frame_t except_frame_t; struct _exec_ctx_t { LONG ref; @@ -235,6 +239,7 @@ struct _exec_ctx_t { VARIANT *stack; unsigned stack_size; unsigned top; + except_frame_t *except_frame; unsigned ip; jsexcept_t *ei; diff --git a/dlls/jscript/parser.y b/dlls/jscript/parser.y index 570eb5b55d7..f9b2ca3e4d3 100644 --- a/dlls/jscript/parser.y +++ b/dlls/jscript/parser.y @@ -848,7 +848,7 @@ static const statement_eval_t stat_eval_table[] = { return_statement_eval, compiled_statement_eval, compiled_statement_eval, - try_statement_eval, + compiled_statement_eval, compiled_statement_eval, compiled_statement_eval, compiled_statement_eval diff --git a/dlls/jscript/tests/lang.js b/dlls/jscript/tests/lang.js index b7d7ca5b0fe..7eabdcf3c41 100644 --- a/dlls/jscript/tests/lang.js +++ b/dlls/jscript/tests/lang.js @@ -562,6 +562,29 @@ try { } ok(state === "finally", "state = " + state + " expected finally"); +state = ""; +try { + try { + throw 0; + }finally { + state = "finally"; + } +}catch(e) { + ok(state === "finally", "state = " + state + " expected finally"); + state = "catch"; +} +ok(state === "catch", "state = " + state + " expected catch"); + +try { + try { + throw 0; + }finally { + throw 1; + } +}catch(e) { + ok(e === 1, "e = " + e); +} + state = ""; try { ok(state === "", "try: state = " + state);