jscript: Added bytecode version of try statement.
This commit is contained in:
parent
02ff8d1853
commit
3b56e49ef9
|
@ -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)) {
|
||||
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
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue