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; }
expr_val * compile_function(function_node * func, scope * current_scope, str_list * lines) { char * orig_name = func->name; char * name; // Declaring new scope scope * n_scope = new_scope(current_scope); expr_val * args = compile_statement(func->args, n_scope, lines); expr_val * return_type = compile_statement(func->return_type, n_scope, lines); // Does func with that name already exist if (get_variable_from_scope(current_scope, orig_name)) { int len = strlen(orig_name) + 46; char * msg = (char *) malloc(len); snprintf(msg, len, "Functions name already exists %s", orig_name); report_error(msg, 0); } else if(strcmp(orig_name, "main") == 0) { // @todo: remove this hack create_variable_in_scope(current_scope, orig_name, return_type->type, FUNC, &var_num); name = orig_name; } else { name = create_variable_in_scope(current_scope, orig_name, return_type->type, FUNC, &var_num); } int len = strlen(args->val) + strlen(return_type->val) + strlen(name) + 5; char * output = (char *) malloc(len); snprintf(output, len, "%s %s%s", return_type->val, name, args->val); insert_array(lines, output); compile_block(func->block, n_scope, lines, 1); return new_expr_val("(function)", NO_TYPE); }
expr_val * get_func_argument(base_node * name_node, base_node * type_node, scope * scope) { char *orig_name, * name, *type; if (name_node->type != ID_NODE) { report_error("Function argument must have a valid name", name_node->type); } orig_name = ((id_node*) name_node)->val; expr_val * type_expr_val = compile_statement(type_node, scope, NULL); type = type_expr_val->val; // Does arg with that name already exist if (get_variable_from_scope(scope, orig_name)) { int len = strlen(orig_name) + 46; char * msg = (char *) malloc(len); snprintf(msg, len, "Function arguments must have different names %s", orig_name); report_error(msg, 0); } else { name = create_variable_in_scope(scope, orig_name, type_expr_val->type, FUNC_ARG, &var_num); } int len = strlen(name) + strlen(type) + 2; char * output = (char*) malloc(len); snprintf(output, len, "%s %s", type, name); return new_expr_val(output, type_expr_val->type); }
expr_val * compile_call_args(argument_node * args, scope * current_scope, str_list * lines) { // Argument string char * output = (char*) malloc(0); //expr_val int i; for (i = 0 ; i < args->arguments->used ; i++) { base_node * arg_node = args->arguments->array[i]; expr_val * arg = compile_statement(arg_node, current_scope, lines); printf("call arg: %s\n", arg->val); // Appending to argument string int new_length = strlen(output) + strlen(arg->val) + 4; output = (char *) realloc(output, new_length); if (i == 0) { // if first argument do not add , snprintf(output, new_length, "%s%s", output, arg->val); } else { snprintf(output, new_length, "%s, %s", output, arg->val); } } // Adding parentheses around function int len = strlen(output) + 4; char * output_with_parentheses = (char *) malloc(len); snprintf(output_with_parentheses, len, "(%s)", output); return new_expr_val(output_with_parentheses, NO_TYPE); }
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; }
static HRESULT compile_dowhile_statement(compile_ctx_t *ctx, while_statement_t *stat) { statement_ctx_t loop_ctx = {0}; unsigned start_addr; vbsop_t jmp_op; HRESULT hres; start_addr = ctx->instr_cnt; if(!(loop_ctx.while_end_label = alloc_label(ctx))) return E_OUTOFMEMORY; hres = compile_statement(ctx, &loop_ctx, stat->body); if(FAILED(hres)) return hres; if(stat->expr) { hres = compile_expression(ctx, stat->expr); if(FAILED(hres)) return hres; jmp_op = stat->stat.type == STAT_DOUNTIL ? OP_jmp_false : OP_jmp_true; }else { jmp_op = OP_jmp; } hres = push_instr_addr(ctx, jmp_op, start_addr); if(FAILED(hres)) return hres; label_set_addr(ctx, loop_ctx.while_end_label); 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; }
static HRESULT compile_dowhile_statement(compile_ctx_t *ctx, while_statement_t *stat) { unsigned start_addr, prev_label; HRESULT hres; start_addr = ctx->instr_cnt; prev_label = ctx->while_end_label; if((ctx->while_end_label = alloc_label(ctx)) == -1) return E_OUTOFMEMORY; hres = compile_statement(ctx, stat->body); if(FAILED(hres)) return hres; hres = compile_expression(ctx, stat->expr); if(FAILED(hres)) return hres; hres = push_instr_addr(ctx, stat->stat.type == STAT_DOUNTIL ? OP_jmp_false : OP_jmp_true, start_addr); if(FAILED(hres)) return hres; label_set_addr(ctx, ctx->while_end_label); ctx->while_end_label = prev_label; return S_OK; }
/* 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; }
expr_val * compile_type_assignment(binary_op_node * node, scope * scope, str_list * lines) { expr_val * type = compile_statement(node->right, scope, lines); // Convert to id id_node * left_id = (id_node *) node->left; expr_val * name = get_variable(left_id->val, type->type, scope, lines); return name; }
expr_val * compile_assignment(binary_op_node * ass, scope * scope, str_list * lines) { expr_val * name = compile_statement(ass->left, scope, lines); expr_val * val = compile_statement(ass->right, scope, lines); if (!determine_type(ass->op, name->type, val->type)) return NULL; char * name_value = name->val; char * right_value = val->val; int len = strlen(name_value) + strlen(right_value) + 5; char * output = (char *) malloc(len); snprintf(output, len, "%s = %s;", name_value, right_value); insert_array(lines, output); return new_expr_val(name_value, val->type); }
expr_val * compile_return(return_node * ret, scope * scope, str_list * lines) { expr_val * expression = compile_statement(ret->expr, scope, lines); int len = strlen(expression->val) + 9; char * output = (char *) malloc(len); snprintf(output, len, "return %s;", expression->val); insert_array(lines, output); return expression; }
expr_val * compile_block(block_node * block, scope * scope, str_list * lines, int add_braces) { int i; node_list * statements = block->stmts; if (add_braces) insert_array(lines, "{"); for (i = 0 ; i < statements->used ; i++) { compile_statement(statements->array[i], scope, lines); } if (add_braces) insert_array(lines, "}"); return new_expr_val("(block)", NO_TYPE); }
expr_val * compile_call(call_node * call, scope * current_scope, str_list * lines) { expr_val * name = compile_statement(call->expr, current_scope, lines); expr_val * args = compile_statement(call->args, current_scope, lines); // Determine function type variable * func = get_variable_from_scope_gen_name(current_scope, name->val); expr_val * var = get_new_var(func->type, current_scope, lines); // Extracting values char * name_val = name->val; char * args_val = args->val; char * var_val = var->val; int len = strlen(var_val) + strlen(name_val) + strlen(args_val) + 8; char * output = (char *) malloc(len); snprintf(output, len, "%s = %s%s;", var_val, name_val, args_val); insert_array(lines, output); return new_expr_val(var_val, var->type); }
expr_val * compile_comparison_assignment(binary_op_node * node, scope * scope, str_list * lines) { types op = node->op; expr_val * left = compile_statement(node->left, scope, lines); expr_val * right = compile_statement(node->right, scope, lines); char * left_val = left->val; char * right_val = right->val; expr_val * var = get_new_var(INT_TYPE, scope, lines); char * op_str; switch (op) { case EQUAL: op_str = "=="; break; case LESS: op_str = "<"; break; case LESS_EQUAL: op_str = "<="; break; case GREATER: op_str = ">"; break; case GREATER_EQUAL: op_str = ">="; break; default: break; } int out_len = strlen(left_val) + strlen(right_val) + strlen(op_str) + 11; char * output = (char *) malloc(out_len); snprintf(output, out_len, "%s = %s %s %s;", var->val, left_val, op_str, right_val); insert_array(lines, output); return var; }
/* 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; }
expr_val * compile_addition(binary_op_node * node, scope * scope, str_list * lines) { int op = node->op; expr_val * left = compile_statement(node->left, scope, lines); expr_val * right = compile_statement(node->right, scope, lines); // Determine if types are compatible expr_type type; if(!(type = determine_type(op, left->type, right->type))) return NULL; expr_val * var = get_new_var(type, scope, lines); // Extracting values char * var_val = var->val; char * left_val = left->val; char * right_val = right->val; int len = strlen(var_val) + strlen(left_val) + strlen(right_val) + 8; char * output = (char *) malloc(len); snprintf(output, len, "%s = %s + %s;", var_val, left_val, right_val); insert_array(lines, output); return new_expr_val(var_val, INT_TYPE); }
/* 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; }
static HRESULT compile_while_statement(compile_ctx_t *ctx, while_statement_t *stat) { statement_ctx_t stat_ctx = {0}, *loop_ctx; unsigned start_addr; unsigned jmp_end; HRESULT hres; start_addr = ctx->instr_cnt; hres = compile_expression(ctx, stat->expr); if(FAILED(hres)) return hres; jmp_end = push_instr(ctx, stat->stat.type == STAT_UNTIL ? OP_jmp_true : OP_jmp_false); if(!jmp_end) return E_OUTOFMEMORY; if(stat->stat.type == STAT_WHILE) { loop_ctx = NULL; }else { if(!(stat_ctx.while_end_label = alloc_label(ctx))) return E_OUTOFMEMORY; loop_ctx = &stat_ctx; } hres = compile_statement(ctx, loop_ctx, stat->body); if(FAILED(hres)) return hres; hres = push_instr_addr(ctx, OP_jmp, start_addr); if(FAILED(hres)) return hres; instr_ptr(ctx, jmp_end)->arg1.uint = ctx->instr_cnt; if(loop_ctx) label_set_addr(ctx, stat_ctx.while_end_label); return S_OK; }
/* 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_while_statement(compile_ctx_t *ctx, while_statement_t *stat) { unsigned start_addr, prev_label; unsigned jmp_end; HRESULT hres; start_addr = ctx->instr_cnt; hres = compile_expression(ctx, stat->expr); if(FAILED(hres)) return hres; jmp_end = push_instr(ctx, stat->stat.type == STAT_UNTIL ? OP_jmp_true : OP_jmp_false); if(jmp_end == -1) return E_OUTOFMEMORY; prev_label = ctx->while_end_label; if(stat->stat.type != STAT_WHILE && (ctx->while_end_label = alloc_label(ctx)) == -1) return E_OUTOFMEMORY; hres = compile_statement(ctx, stat->body); if(FAILED(hres)) return hres; hres = push_instr_addr(ctx, OP_jmp, start_addr); if(FAILED(hres)) return hres; instr_ptr(ctx, jmp_end)->arg1.uint = ctx->instr_cnt; if(stat->stat.type != STAT_WHILE) { label_set_addr(ctx, ctx->while_end_label); ctx->while_end_label = prev_label; } return S_OK; }
expr_val * compile_if(if_node * stmt, scope * scope, str_list * lines) { expr_val * expr = compile_statement(stmt->expr, scope, lines); // Random val to generate afterif int val = var_num++; int var_len = sizeof(char)*(int)log10(val) + 1; // Before block int before_len = strlen(expr->val) + 21 + var_len; char * before = (char *)malloc(before_len); snprintf(before, before_len, "if(!%s) goto afterif%d;", expr->val, val); insert_array(lines, before); expr_val * block = compile_block(stmt->block, scope, lines, 0); int after_len = var_len + 10; char * after = (char *)malloc(after_len); snprintf(after, after_len, "afterif%d:;", val); insert_array(lines, after); return NULL; }
static HRESULT compile_func(compile_ctx_t *ctx, statement_t *stat, function_t *func) { HRESULT hres; func->code_off = ctx->instr_cnt; ctx->while_end_label = -1; ctx->sub_end_label = -1; ctx->func_end_label = -1; ctx->prop_end_label = -1; switch(func->type) { case FUNC_FUNCTION: ctx->func_end_label = alloc_label(ctx); if(ctx->func_end_label == -1) return E_OUTOFMEMORY; /* FIXME ! */ break; case FUNC_SUB: ctx->sub_end_label = alloc_label(ctx); if(ctx->sub_end_label == -1) return E_OUTOFMEMORY; break; case FUNC_PROPGET: case FUNC_PROPLET: case FUNC_PROPSET: case FUNC_DEFGET: ctx->prop_end_label = alloc_label(ctx); if(ctx->prop_end_label == -1) return E_OUTOFMEMORY; break; case FUNC_GLOBAL: break; } ctx->func = func; ctx->dim_decls = NULL; hres = compile_statement(ctx, stat); ctx->func = NULL; if(FAILED(hres)) return hres; assert(ctx->while_end_label == -1); if(ctx->sub_end_label != -1) label_set_addr(ctx, ctx->sub_end_label); if(ctx->func_end_label != -1) label_set_addr(ctx, ctx->func_end_label); if(ctx->prop_end_label != -1) label_set_addr(ctx, ctx->prop_end_label); if(push_instr(ctx, OP_ret) == -1) return E_OUTOFMEMORY; resolve_labels(ctx, func->code_off); if(func->var_cnt) { dim_decl_t *dim_decl; if(func->type == FUNC_GLOBAL) { dynamic_var_t *new_var; func->var_cnt = 0; for(dim_decl = ctx->dim_decls; dim_decl; dim_decl = dim_decl->next) { new_var = compiler_alloc(ctx->code, sizeof(*new_var)); if(!new_var) return E_OUTOFMEMORY; new_var->name = compiler_alloc_string(ctx->code, dim_decl->name); if(!new_var->name) return E_OUTOFMEMORY; V_VT(&new_var->v) = VT_EMPTY; new_var->next = ctx->global_vars; ctx->global_vars = new_var; } }else { unsigned i; func->vars = compiler_alloc(ctx->code, func->var_cnt * sizeof(var_desc_t)); if(!func->vars) return E_OUTOFMEMORY; for(dim_decl = ctx->dim_decls, i=0; dim_decl; dim_decl = dim_decl->next, i++) { func->vars[i].name = compiler_alloc_string(ctx->code, dim_decl->name); if(!func->vars[i].name) return E_OUTOFMEMORY; } assert(i == func->var_cnt); } } 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; }
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.13 */ static HRESULT compile_switch_statement(compiler_ctx_t *ctx, switch_statement_t *stat) { statement_ctx_t stat_ctx = {0, FALSE, FALSE}; unsigned case_cnt = 0, *case_jmps, i, default_jmp; BOOL have_default = FALSE; statement_t *stat_iter; case_clausule_t *iter; HRESULT hres; hres = compile_expression(ctx, stat->expr); if(FAILED(hres)) return hres; stat_ctx.break_label = alloc_label(ctx); if(!stat_ctx.break_label) return E_OUTOFMEMORY; for(iter = stat->case_list; iter; iter = iter->next) { if(iter->expr) case_cnt++; } case_jmps = heap_alloc(case_cnt * sizeof(*case_jmps)); if(!case_jmps) return E_OUTOFMEMORY; i = 0; for(iter = stat->case_list; iter; iter = iter->next) { if(!iter->expr) { have_default = TRUE; continue; } hres = compile_expression(ctx, iter->expr); if(FAILED(hres)) break; case_jmps[i] = push_instr(ctx, OP_case); if(!case_jmps[i]) { hres = E_OUTOFMEMORY; break; } i++; } if(SUCCEEDED(hres)) { if(push_instr(ctx, OP_pop)) { default_jmp = push_instr(ctx, OP_jmp); if(!default_jmp) hres = E_OUTOFMEMORY; }else { hres = E_OUTOFMEMORY; } } if(FAILED(hres)) { heap_free(case_jmps); return hres; } i = 0; for(iter = stat->case_list; iter; iter = iter->next) { while(iter->next && iter->next->stat == iter->stat) { instr_ptr(ctx, iter->expr ? case_jmps[i++] : default_jmp)->arg1.uint = ctx->code_off; iter = iter->next; } instr_ptr(ctx, iter->expr ? case_jmps[i++] : default_jmp)->arg1.uint = ctx->code_off; for(stat_iter = iter->stat; stat_iter && (!iter->next || iter->next->stat != stat_iter); stat_iter = stat_iter->next) { hres = compile_statement(ctx, &stat_ctx, stat_iter); if(FAILED(hres)) break; if(stat_iter->next && !push_instr(ctx, OP_pop)) { hres = E_OUTOFMEMORY; break; } } if(FAILED(hres)) break; } heap_free(case_jmps); if(FAILED(hres)) return hres; assert(i == case_cnt); if(!have_default) instr_ptr(ctx, default_jmp)->arg1.uint = ctx->code_off; label_set_addr(ctx, stat_ctx.break_label); return S_OK; }
/* ECMA-262 3rd Edition 12.14 */ static HRESULT compile_try_statement(compiler_ctx_t *ctx, try_statement_t *stat) { statement_ctx_t try_ctx = {0, FALSE, TRUE}, catch_ctx = {0, TRUE, FALSE}; statement_ctx_t finally_ctx = {2, FALSE, FALSE}; unsigned push_except; BSTR ident; HRESULT hres; push_except = push_instr(ctx, OP_push_except); if(!push_except) 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; if(!stat->catch_block) try_ctx.stack_use = 2; hres = compile_statement(ctx, &try_ctx, stat->try_statement); if(FAILED(hres)) return hres; if(!push_instr(ctx, OP_pop_except)) return E_OUTOFMEMORY; if(stat->catch_block) { unsigned jmp_finally; jmp_finally = push_instr(ctx, OP_jmp); if(!jmp_finally) return E_OUTOFMEMORY; instr_ptr(ctx, push_except)->arg1.uint = ctx->code_off; hres = compile_statement(ctx, &catch_ctx, stat->catch_block->statement); if(FAILED(hres)) return hres; if(!push_instr(ctx, OP_pop_scope)) 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)) return E_OUTOFMEMORY; hres = compile_statement(ctx, stat->catch_block ? NULL : &finally_ctx, stat->finally_statement); if(FAILED(hres)) return hres; if(!stat->catch_block && !push_instr(ctx, OP_end_finally)) return E_OUTOFMEMORY; } return S_OK; }