Commit 3b56e49e authored by Jacek Caban's avatar Jacek Caban Committed by Alexandre Julliard

jscript: Added bytecode version of try statement.

parent 02ff8d18
......@@ -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:
......
......@@ -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);
......
......@@ -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;
......
......@@ -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
......
......@@ -564,6 +564,29 @@ 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);
state = "try";
}catch(ex) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment