static HRESULT compile_literal(compiler_ctx_t *ctx, literal_t *literal) { switch(literal->type) { case LT_BOOL: return push_instr_int(ctx, OP_bool, literal->u.bval); case LT_DOUBLE: return push_instr_double(ctx, OP_double, literal->u.dval); case LT_INT: return push_instr_int(ctx, OP_int, literal->u.lval); case LT_NULL: return push_instr(ctx, OP_null) ? S_OK : E_OUTOFMEMORY; case LT_STRING: return push_instr_str(ctx, OP_str, literal->u.wstr); case LT_REGEXP: { unsigned instr; WCHAR *str; str = compiler_alloc(ctx->code, (literal->u.regexp.str_len+1)*sizeof(WCHAR)); if(!str) return E_OUTOFMEMORY; memcpy(str, literal->u.regexp.str, literal->u.regexp.str_len*sizeof(WCHAR)); str[literal->u.regexp.str_len] = 0; instr = push_instr(ctx, OP_regexp); if(!instr) return E_OUTOFMEMORY; instr_ptr(ctx, instr)->arg1.str = str; instr_ptr(ctx, instr)->arg2.lng = literal->u.regexp.flags; return S_OK; } default: assert(0); } }
static HRESULT compile_if_statement(compile_ctx_t *ctx, if_statement_t *stat) { unsigned cnd_jmp, endif_label = -1; elseif_decl_t *elseif_decl; HRESULT hres; hres = compile_expression(ctx, stat->expr); if(FAILED(hres)) return hres; cnd_jmp = push_instr(ctx, OP_jmp_false); if(cnd_jmp == -1) return E_OUTOFMEMORY; hres = compile_statement(ctx, stat->if_stat); if(FAILED(hres)) return hres; if(stat->else_stat || stat->elseifs) { endif_label = alloc_label(ctx); if(endif_label == -1) return E_OUTOFMEMORY; hres = push_instr_addr(ctx, OP_jmp, endif_label); if(FAILED(hres)) return hres; } for(elseif_decl = stat->elseifs; elseif_decl; elseif_decl = elseif_decl->next) { instr_ptr(ctx, cnd_jmp)->arg1.uint = ctx->instr_cnt; hres = compile_expression(ctx, elseif_decl->expr); if(FAILED(hres)) return hres; cnd_jmp = push_instr(ctx, OP_jmp_false); if(cnd_jmp == -1) return E_OUTOFMEMORY; hres = compile_statement(ctx, elseif_decl->stat); if(FAILED(hres)) return hres; hres = push_instr_addr(ctx, OP_jmp, endif_label); if(FAILED(hres)) return hres; } instr_ptr(ctx, cnd_jmp)->arg1.uint = ctx->instr_cnt; if(stat->else_stat) { hres = compile_statement(ctx, stat->else_stat); if(FAILED(hres)) return hres; } if(endif_label != -1) label_set_addr(ctx, endif_label); return S_OK; }
static HRESULT compile_object_literal(compiler_ctx_t *ctx, property_value_expression_t *expr) { prop_val_t *iter; unsigned instr; BSTR name; HRESULT hres; if(!push_instr(ctx, OP_new_obj)) return E_OUTOFMEMORY; for(iter = expr->property_list; iter; iter = iter->next) { hres = literal_as_bstr(ctx, iter->name, &name); if(FAILED(hres)) return hres; hres = compile_expression(ctx, iter->value); if(FAILED(hres)) return hres; instr = push_instr(ctx, OP_obj_prop); if(!instr) return E_OUTOFMEMORY; instr_ptr(ctx, instr)->arg1.bstr = name; } return S_OK; }
/* ECMA-262 3rd Edition 11.12 */ static HRESULT compile_conditional_expression(compiler_ctx_t *ctx, conditional_expression_t *expr) { unsigned jmp_false, jmp_end; HRESULT hres; hres = compile_expression(ctx, expr->expression); if(FAILED(hres)) return hres; jmp_false = push_instr(ctx, OP_cnd_z); if(!jmp_false) return E_OUTOFMEMORY; hres = compile_expression(ctx, expr->true_expression); if(FAILED(hres)) return hres; jmp_end = push_instr(ctx, OP_jmp); if(!jmp_end) return E_OUTOFMEMORY; instr_ptr(ctx, jmp_false)->arg1.uint = ctx->code_off; if(!push_instr(ctx, OP_pop)) return E_OUTOFMEMORY; hres = compile_expression(ctx, expr->false_expression); if(FAILED(hres)) return hres; instr_ptr(ctx, jmp_end)->arg1.uint = ctx->code_off; return S_OK; }
HRESULT compile_subscript_stat(parser_ctx_t *parser, statement_t *stat, BOOL from_eval, unsigned *ret_off) { unsigned off; HRESULT hres; TRACE("\n"); hres = init_compiler(parser); if(FAILED(hres)) return hres; off = parser->compiler->code_off; if(stat->next) hres = compile_block_statement(parser->compiler, stat); else hres = compile_statement(parser->compiler, NULL, stat); if(FAILED(hres)) return hres; resolve_labels(parser->compiler, off); if(!from_eval && !push_instr(parser->compiler, OP_pop)) return E_OUTOFMEMORY; if(!push_instr(parser->compiler, OP_ret)) return E_OUTOFMEMORY; if(TRACE_ON(jscript_disas)) dump_code(parser->compiler, off); *ret_off = off; return S_OK; }
/* 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_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.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_delete_expression(compiler_ctx_t *ctx, unary_expression_t *expr) { HRESULT hres; switch(expr->expression->type) { case EXPR_ARRAY: { binary_expression_t *array_expr = (binary_expression_t*)expr->expression; hres = compile_expression(ctx, array_expr->expression1); if(FAILED(hres)) return hres; hres = compile_expression(ctx, array_expr->expression2); if(FAILED(hres)) return hres; if(!push_instr(ctx, OP_delete)) return E_OUTOFMEMORY; break; } case EXPR_MEMBER: { member_expression_t *member_expr = (member_expression_t*)expr->expression; 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; if(!push_instr(ctx, OP_delete)) return E_OUTOFMEMORY; break; } case EXPR_IDENT: return push_instr_bstr(ctx, OP_delete_ident, ((identifier_expression_t*)expr->expression)->identifier); default: { const WCHAR fixmeW[] = {'F','I','X','M','E',0}; WARN("invalid delete, unimplemented exception message\n"); hres = compile_expression(ctx, expr->expression); if(FAILED(hres)) return hres; return push_instr_uint_str(ctx, OP_throw_type, JS_E_INVALID_DELETE, fixmeW); } } return S_OK; }
static HRESULT compile_foreach_statement(compile_ctx_t *ctx, foreach_statement_t *stat) { statement_ctx_t loop_ctx = {1}; unsigned loop_start; HRESULT hres; hres = compile_expression(ctx, stat->group_expr); if(FAILED(hres)) return hres; if(!push_instr(ctx, OP_newenum)) return E_OUTOFMEMORY; loop_start = ctx->instr_cnt; if(!(loop_ctx.for_end_label = alloc_label(ctx))) return E_OUTOFMEMORY; hres = push_instr_uint_bstr(ctx, OP_enumnext, loop_ctx.for_end_label, stat->identifier); if(FAILED(hres)) return hres; hres = compile_statement(ctx, &loop_ctx, stat->body); if(FAILED(hres)) return hres; hres = push_instr_addr(ctx, OP_jmp, loop_start); if(FAILED(hres)) return hres; label_set_addr(ctx, loop_ctx.for_end_label); return S_OK; }
HRESULT compile_subscript(parser_ctx_t *parser, expression_t *expr, BOOL do_ret, unsigned *ret_off) { BOOL no_ret = FALSE; HRESULT hres; if(!parser->code) { parser->code = heap_alloc_zero(sizeof(bytecode_t)); if(!parser->code) return E_OUTOFMEMORY; jsheap_init(&parser->code->heap); } if(!parser->compiler) { parser->compiler = heap_alloc_zero(sizeof(compiler_ctx_t)); if(!parser->compiler) return E_OUTOFMEMORY; parser->compiler->parser = parser; parser->compiler->code = parser->code; } *ret_off = parser->compiler->code_off; hres = compile_expression_noret(parser->compiler, expr, do_ret ? NULL : &no_ret); if(FAILED(hres)) return hres; return push_instr(parser->compiler, OP_ret) == -1 ? E_OUTOFMEMORY : S_OK; }
/* ECMA-262 3rd Edition 12.13 */ static HRESULT compile_throw_statement(compiler_ctx_t *ctx, expression_statement_t *stat) { HRESULT hres; hres = compile_expression(ctx, stat->expr); if(FAILED(hres)) return hres; return push_instr(ctx, OP_throw) ? S_OK : E_OUTOFMEMORY; }
/* ECMA-262 3rd Edition 12.2 */ static HRESULT compile_var_statement(compiler_ctx_t *ctx, var_statement_t *stat) { HRESULT hres; hres = compile_variable_list(ctx, stat->variable_list); if(FAILED(hres)) return hres; return push_instr(ctx, OP_undefined) ? S_OK : E_OUTOFMEMORY; }
static HRESULT compile_unary_expression(compiler_ctx_t *ctx, unary_expression_t *expr, jsop_t op) { HRESULT hres; hres = compile_expression(ctx, expr->expression); if(FAILED(hres)) return hres; return push_instr(ctx, op) ? S_OK : E_OUTOFMEMORY; }
static HRESULT compile_unary_expression(compile_ctx_t *ctx, unary_expression_t *expr, vbsop_t op) { HRESULT hres; hres = compile_expression(ctx, expr->subexpr); if(FAILED(hres)) return hres; return push_instr(ctx, op) == -1 ? E_OUTOFMEMORY : S_OK; }
static HRESULT push_instr_uint(compiler_ctx_t *ctx, jsop_t op, unsigned arg) { unsigned instr; instr = push_instr(ctx, op); if(!instr) return E_OUTOFMEMORY; instr_ptr(ctx, instr)->arg1.uint = arg; return S_OK; }
static HRESULT compile_interp_fallback(compiler_ctx_t *ctx, expression_t *expr) { unsigned instr; instr = push_instr(ctx, OP_tree); if(instr == -1) return E_OUTOFMEMORY; instr_ptr(ctx, instr)->arg1.expr = expr; return S_OK; }
static HRESULT push_instr_int(compiler_ctx_t *ctx, jsop_t op, LONG arg) { unsigned instr; instr = push_instr(ctx, op); if(instr == -1) return E_OUTOFMEMORY; instr_ptr(ctx, instr)->arg1.lng = arg; return S_OK; }
static HRESULT push_instr_addr(compile_ctx_t *ctx, vbsop_t op, unsigned arg) { unsigned ret; ret = push_instr(ctx, op); if(ret == -1) return E_OUTOFMEMORY; instr_ptr(ctx, ret)->arg1.uint = arg; return S_OK; }
static HRESULT push_instr_int(compile_ctx_t *ctx, vbsop_t op, LONG arg) { unsigned ret; ret = push_instr(ctx, op); if(!ret) return E_OUTOFMEMORY; instr_ptr(ctx, ret)->arg1.lng = arg; return S_OK; }
static HRESULT pop_to_stat(compiler_ctx_t *ctx, statement_ctx_t *stat_ctx) { unsigned stack_pop = 0; statement_ctx_t *iter; for(iter = ctx->stat_ctx; iter != stat_ctx; iter = iter->next) { if(iter->using_scope && !push_instr(ctx, OP_pop_scope)) return E_OUTOFMEMORY; if(iter->using_except && !push_instr(ctx, OP_pop_except)) return E_OUTOFMEMORY; stack_pop += iter->stack_use; } /* FIXME: optimize */ while(stack_pop--) { if(!push_instr(ctx, OP_pop)) return E_OUTOFMEMORY; } return S_OK; }
/* ECMA-262 3rd Edition 12.10 */ static HRESULT compile_with_statement(compiler_ctx_t *ctx, with_statement_t *stat) { statement_ctx_t stat_ctx = {0, TRUE, FALSE}; HRESULT hres; hres = compile_expression(ctx, stat->expr); if(FAILED(hres)) return hres; if(!push_instr(ctx, OP_push_scope)) return E_OUTOFMEMORY; hres = compile_statement(ctx, &stat_ctx, stat->statement); if(FAILED(hres)) return hres; if(!push_instr(ctx, OP_pop_scope)) return E_OUTOFMEMORY; return S_OK; }
/* ECMA-262 3rd Edition 11.14 */ static HRESULT compile_comma_expression(compiler_ctx_t *ctx, binary_expression_t *expr) { HRESULT hres; hres = compile_expression(ctx, expr->expression1); if(FAILED(hres)) return hres; if(!push_instr(ctx, OP_pop)) return E_OUTOFMEMORY; return compile_expression(ctx, expr->expression2); }
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_binary_expression(compile_ctx_t *ctx, binary_expression_t *expr, vbsop_t op) { HRESULT hres; hres = compile_expression(ctx, expr->left); if(FAILED(hres)) return hres; hres = compile_expression(ctx, expr->right); if(FAILED(hres)) return hres; return push_instr(ctx, op) ? S_OK : E_OUTOFMEMORY; }
/* ECMA-262 3rd Edition 12.1 */ static HRESULT compile_block_statement(compiler_ctx_t *ctx, statement_t *iter) { HRESULT hres; /* FIXME: do it only if needed */ if(!iter) return push_instr(ctx, OP_undefined) ? S_OK : E_OUTOFMEMORY; while(1) { hres = compile_statement(ctx, NULL, iter); if(FAILED(hres)) return hres; iter = iter->next; if(!iter) break; if(!push_instr(ctx, OP_pop)) return E_OUTOFMEMORY; } return S_OK; }
static HRESULT compile_binary_expression(compiler_ctx_t *ctx, binary_expression_t *expr, jsop_t op) { HRESULT hres; hres = compile_expression(ctx, expr->expression1); if(FAILED(hres)) return hres; hres = compile_expression(ctx, expr->expression2); if(FAILED(hres)) return hres; return push_instr(ctx, op) == -1 ? E_OUTOFMEMORY : S_OK; }
/* ECMA-262 3rd Edition 12.5 */ static HRESULT compile_if_statement(compiler_ctx_t *ctx, if_statement_t *stat) { unsigned jmp_else, jmp_end; HRESULT hres; hres = compile_expression(ctx, stat->expr); if(FAILED(hres)) return hres; jmp_else = push_instr(ctx, OP_jmp_z); if(!jmp_else) return E_OUTOFMEMORY; hres = compile_statement(ctx, NULL, stat->if_stat); if(FAILED(hres)) return hres; jmp_end = push_instr(ctx, OP_jmp); if(!jmp_end) return E_OUTOFMEMORY; instr_ptr(ctx, jmp_else)->arg1.uint = ctx->code_off; if(stat->else_stat) { hres = compile_statement(ctx, NULL, stat->else_stat); if(FAILED(hres)) return hres; }else { /* FIXME: We could sometimes avoid it */ if(!push_instr(ctx, OP_undefined)) return E_OUTOFMEMORY; } instr_ptr(ctx, jmp_end)->arg1.uint = ctx->code_off; return S_OK; }
/* ECMA-262 3rd Edition 12.4 */ static HRESULT compile_expression_statement(compiler_ctx_t *ctx, expression_statement_t *stat) { BOOL no_ret = FALSE; HRESULT hres; hres = compile_expression_noret(ctx, stat->expr, &no_ret); if(FAILED(hres)) return hres; /* FIXME: that's a big potential optimization */ if(no_ret && !push_instr(ctx, OP_undefined)) return E_OUTOFMEMORY; return S_OK; }
static HRESULT compile_function_expression(compiler_ctx_t *ctx, function_expression_t *expr) { unsigned instr; /* FIXME: not exactly right */ if(expr->identifier) return push_instr_bstr(ctx, OP_ident, expr->identifier); instr = push_instr(ctx, OP_func); if(!instr) return E_OUTOFMEMORY; instr_ptr(ctx, instr)->arg1.func = expr; return S_OK; }