/* ECMA-262 3rd Edition 12.6.2 */ static HRESULT compile_while_statement(compiler_ctx_t *ctx, while_statement_t *stat) { statement_ctx_t stat_ctx = {0, FALSE, FALSE}; unsigned jmp_off; HRESULT hres; stat_ctx.break_label = alloc_label(ctx); if(!stat_ctx.break_label) return E_OUTOFMEMORY; stat_ctx.continue_label = alloc_label(ctx); if(!stat_ctx.continue_label) return E_OUTOFMEMORY; if(!stat->do_while) { /* FIXME: avoid */ if(!push_instr(ctx, OP_undefined)) return E_OUTOFMEMORY; jmp_off = ctx->code_off; label_set_addr(ctx, stat_ctx.continue_label); hres = compile_expression(ctx, stat->expr); if(FAILED(hres)) return hres; hres = push_instr_uint(ctx, OP_jmp_z, stat_ctx.break_label); if(FAILED(hres)) return hres; if(!push_instr(ctx, OP_pop)) return E_OUTOFMEMORY; }else { jmp_off = ctx->code_off; } hres = compile_statement(ctx, &stat_ctx, stat->statement); if(FAILED(hres)) return hres; if(stat->do_while) { label_set_addr(ctx, stat_ctx.continue_label); hres = compile_expression(ctx, stat->expr); if(FAILED(hres)) return hres; hres = push_instr_uint(ctx, OP_jmp_z, stat_ctx.break_label); if(FAILED(hres)) return hres; if(!push_instr(ctx, OP_pop)) return E_OUTOFMEMORY; } hres = push_instr_uint(ctx, OP_jmp, jmp_off); if(FAILED(hres)) return hres; label_set_addr(ctx, stat_ctx.break_label); return S_OK; }
static HRESULT compile_exitdo_statement(compile_ctx_t *ctx) { statement_ctx_t *iter; unsigned pop_cnt = 0; for(iter = ctx->stat_ctx; iter; iter = iter->next) { pop_cnt += iter->stack_use; if(iter->while_end_label) break; } if(!iter) { FIXME("Exit Do outside Do Loop\n"); return E_FAIL; } if(pop_cnt) { HRESULT hres; hres = push_instr_uint(ctx, OP_pop, pop_cnt); if(FAILED(hres)) return hres; } return push_instr_addr(ctx, OP_jmp, iter->while_end_label); }
static HRESULT compile_array_literal(compiler_ctx_t *ctx, array_literal_expression_t *expr) { unsigned i, elem_cnt = expr->length; array_element_t *iter; HRESULT hres; for(iter = expr->element_list; iter; iter = iter->next) { elem_cnt += iter->elision+1; for(i=0; i < iter->elision; i++) { if(!push_instr(ctx, OP_undefined)) return E_OUTOFMEMORY; } hres = compile_expression(ctx, iter->expr); if(FAILED(hres)) return hres; } for(i=0; i < expr->length; i++) { if(!push_instr(ctx, OP_undefined)) return E_OUTOFMEMORY; } return push_instr_uint(ctx, OP_carray, elem_cnt); }
/* ECMA-262 3rd Edition 12.8 */ static HRESULT compile_break_statement(compiler_ctx_t *ctx, branch_statement_t *stat) { statement_ctx_t *pop_ctx; HRESULT hres; for(pop_ctx = ctx->stat_ctx; pop_ctx; pop_ctx = pop_ctx->next) { if(pop_ctx->break_label) break; } if(!pop_ctx) { WARN("Break outside loop\n"); return JS_E_INVALID_BREAK; } if(stat->identifier) return push_instr(ctx, OP_label) ? S_OK : E_OUTOFMEMORY; /* FIXME */ hres = pop_to_stat(ctx, pop_ctx->next); if(FAILED(hres)) return hres; if(!push_instr(ctx, OP_undefined)) return E_OUTOFMEMORY; return push_instr_uint(ctx, OP_jmp, pop_ctx->break_label); }
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: { binary_expression_t *array_expr = (binary_expression_t*)expr; hres = compile_expression(ctx, array_expr->expression1); if(FAILED(hres)) return hres; hres = compile_expression(ctx, array_expr->expression2); 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; }
static HRESULT exit_label(compile_ctx_t *ctx, unsigned jmp_label) { statement_ctx_t *iter; unsigned pop_cnt = 0; for(iter = ctx->stat_ctx; iter; iter = iter->next) pop_cnt += iter->stack_use; if(pop_cnt) { HRESULT hres; hres = push_instr_uint(ctx, OP_pop, pop_cnt); if(FAILED(hres)) return hres; } return push_instr_addr(ctx, OP_jmp, jmp_label); }
static HRESULT compile_increment_expression(compiler_ctx_t *ctx, unary_expression_t *expr, jsop_t op, int n) { HRESULT hres; if(!is_memberid_expr(expr->expression->type)) { hres = compile_expression(ctx, expr->expression); if(FAILED(hres)) return hres; return push_instr_uint(ctx, OP_throw_ref, JS_E_ILLEGAL_ASSIGN); } hres = compile_memberid_expression(ctx, expr->expression, fdexNameEnsure); if(FAILED(hres)) return hres; return push_instr_int(ctx, op, n); }
static HRESULT compile_assign_expression(compiler_ctx_t *ctx, binary_expression_t *expr, jsop_t op) { HRESULT hres; if(!is_memberid_expr(expr->expression1->type)) { hres = compile_expression(ctx, expr->expression1); if(FAILED(hres)) return hres; hres = compile_expression(ctx, expr->expression2); if(FAILED(hres)) return hres; if(op != OP_LAST && !push_instr(ctx, op)) return E_OUTOFMEMORY; return push_instr_uint(ctx, OP_throw_ref, 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)) return E_OUTOFMEMORY; hres = compile_expression(ctx, expr->expression2); if(FAILED(hres)) return hres; if(op != OP_LAST && !push_instr(ctx, op)) return E_OUTOFMEMORY; if(!push_instr(ctx, OP_assign)) return E_OUTOFMEMORY; return S_OK; }
static HRESULT compile_select_statement(compile_ctx_t *ctx, select_statement_t *stat) { unsigned end_label, case_cnt = 0, *case_labels = NULL, i; case_clausule_t *case_iter; expression_t *expr_iter; HRESULT hres; hres = compile_expression(ctx, stat->expr); if(FAILED(hres)) return hres; if(!push_instr(ctx, OP_val)) return E_OUTOFMEMORY; end_label = alloc_label(ctx); if(!end_label) return E_OUTOFMEMORY; for(case_iter = stat->case_clausules; case_iter; case_iter = case_iter->next) case_cnt++; if(case_cnt) { case_labels = heap_alloc(case_cnt*sizeof(*case_labels)); if(!case_labels) return E_OUTOFMEMORY; } for(case_iter = stat->case_clausules, i=0; case_iter; case_iter = case_iter->next, i++) { case_labels[i] = alloc_label(ctx); if(!case_labels[i]) { hres = E_OUTOFMEMORY; break; } if(!case_iter->expr) break; for(expr_iter = case_iter->expr; expr_iter; expr_iter = expr_iter->next) { hres = compile_expression(ctx, expr_iter); if(FAILED(hres)) break; hres = push_instr_addr(ctx, OP_case, case_labels[i]); if(FAILED(hres)) break; } } if(FAILED(hres)) { heap_free(case_labels); return hres; } hres = push_instr_uint(ctx, OP_pop, 1); if(FAILED(hres)) { heap_free(case_labels); return hres; } hres = push_instr_addr(ctx, OP_jmp, case_iter ? case_labels[i] : end_label); if(FAILED(hres)) { heap_free(case_labels); return hres; } for(case_iter = stat->case_clausules, i=0; case_iter; case_iter = case_iter->next, i++) { label_set_addr(ctx, case_labels[i]); hres = compile_statement(ctx, NULL, case_iter->stat); if(FAILED(hres)) break; if(!case_iter->next) break; hres = push_instr_addr(ctx, OP_jmp, end_label); if(FAILED(hres)) break; } heap_free(case_labels); if(FAILED(hres)) return hres; label_set_addr(ctx, end_label); return S_OK; }
static HRESULT compile_forto_statement(compile_ctx_t *ctx, forto_statement_t *stat) { statement_ctx_t loop_ctx = {2}; unsigned step_instr, instr; BSTR identifier; HRESULT hres; identifier = alloc_bstr_arg(ctx, stat->identifier); if(!identifier) return E_OUTOFMEMORY; hres = compile_expression(ctx, stat->from_expr); if(FAILED(hres)) return hres; instr = push_instr(ctx, OP_assign_ident); if(!instr) return E_OUTOFMEMORY; instr_ptr(ctx, instr)->arg1.bstr = identifier; instr_ptr(ctx, instr)->arg2.uint = 0; hres = compile_expression(ctx, stat->to_expr); if(FAILED(hres)) return hres; if(!push_instr(ctx, OP_val)) return E_OUTOFMEMORY; if(stat->step_expr) { hres = compile_expression(ctx, stat->step_expr); if(FAILED(hres)) return hres; if(!push_instr(ctx, OP_val)) return E_OUTOFMEMORY; }else { hres = push_instr_int(ctx, OP_short, 1); if(FAILED(hres)) return hres; } loop_ctx.for_end_label = alloc_label(ctx); if(!loop_ctx.for_end_label) return E_OUTOFMEMORY; step_instr = push_instr(ctx, OP_step); if(!step_instr) return E_OUTOFMEMORY; instr_ptr(ctx, step_instr)->arg2.bstr = identifier; instr_ptr(ctx, step_instr)->arg1.uint = loop_ctx.for_end_label; hres = compile_statement(ctx, &loop_ctx, stat->body); if(FAILED(hres)) return hres; instr = push_instr(ctx, OP_incc); if(!instr) return E_OUTOFMEMORY; instr_ptr(ctx, instr)->arg1.bstr = identifier; hres = push_instr_addr(ctx, OP_jmp, step_instr); if(FAILED(hres)) return hres; hres = push_instr_uint(ctx, OP_pop, 2); if(FAILED(hres)) return hres; label_set_addr(ctx, loop_ctx.for_end_label); return S_OK; }
/* ECMA-262 3rd Edition 12.6.4 */ static HRESULT compile_forin_statement(compiler_ctx_t *ctx, forin_statement_t *stat) { statement_ctx_t stat_ctx = {4, FALSE, FALSE}; HRESULT hres; if(stat->variable) { hres = compile_variable_list(ctx, stat->variable); if(FAILED(hres)) return hres; } stat_ctx.break_label = alloc_label(ctx); if(!stat_ctx.break_label) return E_OUTOFMEMORY; stat_ctx.continue_label = alloc_label(ctx); if(!stat_ctx.continue_label) return E_OUTOFMEMORY; hres = compile_expression(ctx, stat->in_expr); if(FAILED(hres)) return hres; if(stat->variable) { hres = push_instr_bstr_uint(ctx, OP_identid, stat->variable->identifier, fdexNameEnsure); if(FAILED(hres)) return hres; }else if(is_memberid_expr(stat->expr->type)) { hres = compile_memberid_expression(ctx, stat->expr, fdexNameEnsure); if(FAILED(hres)) return hres; }else { hres = push_instr_uint(ctx, OP_throw_ref, JS_E_ILLEGAL_ASSIGN); if(FAILED(hres)) return hres; /* FIXME: compile statement anyways when we depend on compiler to check errors */ return S_OK; } hres = push_instr_int(ctx, OP_int, DISPID_STARTENUM); if(FAILED(hres)) return hres; /* FIXME: avoid */ if(!push_instr(ctx, OP_undefined)) return E_OUTOFMEMORY; label_set_addr(ctx, stat_ctx.continue_label); hres = push_instr_uint(ctx, OP_forin, stat_ctx.break_label); if(FAILED(hres)) return E_OUTOFMEMORY; hres = compile_statement(ctx, &stat_ctx, stat->statement); if(FAILED(hres)) return hres; hres = push_instr_uint(ctx, OP_jmp, stat_ctx.continue_label); if(FAILED(hres)) return hres; label_set_addr(ctx, stat_ctx.break_label); return S_OK; }
/* ECMA-262 3rd Edition 12.6.3 */ static HRESULT compile_for_statement(compiler_ctx_t *ctx, for_statement_t *stat) { statement_ctx_t stat_ctx = {0, FALSE, FALSE}; unsigned expr_off; HRESULT hres; if(stat->variable_list) { hres = compile_variable_list(ctx, stat->variable_list); if(FAILED(hres)) return hres; }else if(stat->begin_expr) { BOOL no_ret = FALSE; hres = compile_expression_noret(ctx, stat->begin_expr, &no_ret); if(FAILED(hres)) return hres; if(!no_ret && !push_instr(ctx, OP_pop)) return E_OUTOFMEMORY; } stat_ctx.break_label = alloc_label(ctx); if(!stat_ctx.break_label) return E_OUTOFMEMORY; stat_ctx.continue_label = alloc_label(ctx); if(!stat_ctx.continue_label) return E_OUTOFMEMORY; /* FIXME: avoid */ if(!push_instr(ctx, OP_undefined)) return E_OUTOFMEMORY; expr_off = ctx->code_off; if(stat->expr) { hres = compile_expression(ctx, stat->expr); if(FAILED(hres)) return hres; hres = push_instr_uint(ctx, OP_jmp_z, stat_ctx.break_label); if(FAILED(hres)) return hres; } if(!push_instr(ctx, OP_pop)) return E_OUTOFMEMORY; hres = compile_statement(ctx, &stat_ctx, stat->statement); if(FAILED(hres)) return hres; label_set_addr(ctx, stat_ctx.continue_label); if(stat->end_expr) { BOOL no_ret = FALSE; hres = compile_expression_noret(ctx, stat->end_expr, &no_ret); if(FAILED(hres)) return hres; if(!no_ret && !push_instr(ctx, OP_pop)) return E_OUTOFMEMORY; } hres = push_instr_uint(ctx, OP_jmp, expr_off); if(FAILED(hres)) return hres; label_set_addr(ctx, stat_ctx.break_label); return S_OK; }