static HRESULT compile_member_expression(compile_ctx_t *ctx, member_expression_t *expr, BOOL ret_val) { unsigned arg_cnt = 0; HRESULT hres; if(ret_val && !expr->args) { expression_t *const_expr; const_expr = lookup_const_decls(ctx, expr->identifier, TRUE); if(const_expr) return compile_expression(ctx, const_expr); } hres = compile_args(ctx, expr->args, &arg_cnt); if(FAILED(hres)) return hres; if(expr->obj_expr) { hres = compile_expression(ctx, expr->obj_expr); if(FAILED(hres)) return hres; hres = push_instr_bstr_uint(ctx, ret_val ? OP_mcall : OP_mcallv, expr->identifier, arg_cnt); }else { hres = push_instr_bstr_uint(ctx, ret_val ? OP_icall : OP_icallv, expr->identifier, arg_cnt); } return hres; }
static HRESULT compile_assignment(compile_ctx_t *ctx, member_expression_t *member_expr, expression_t *value_expr, BOOL is_set) { unsigned args_cnt; vbsop_t op; HRESULT hres; if(member_expr->obj_expr) { hres = compile_expression(ctx, member_expr->obj_expr); if(FAILED(hres)) return hres; op = is_set ? OP_set_member : OP_assign_member; }else { op = is_set ? OP_set_ident : OP_assign_ident; } hres = compile_expression(ctx, value_expr); if(FAILED(hres)) return hres; hres = compile_args(ctx, member_expr->args, &args_cnt); if(FAILED(hres)) return hres; return push_instr_bstr_uint(ctx, op, member_expr->identifier, args_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_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; }
/* 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; }
static HRESULT compile_assign_statement(compile_ctx_t *ctx, assign_statement_t *stat, BOOL is_set) { HRESULT hres; hres = compile_expression(ctx, stat->value_expr); if(FAILED(hres)) return hres; if(stat->member_expr->args) { FIXME("arguments support not implemented\n"); return E_NOTIMPL; } if(stat->member_expr->obj_expr) { hres = compile_expression(ctx, stat->member_expr->obj_expr); if(FAILED(hres)) return hres; hres = push_instr_bstr(ctx, is_set ? OP_set_member : OP_assign_member, stat->member_expr->identifier); }else { hres = push_instr_bstr(ctx, is_set ? OP_set_ident : OP_assign_ident, stat->member_expr->identifier); } return hres; }
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; }
/* 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_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) ? S_OK : E_OUTOFMEMORY; }
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) == -1 ? E_OUTOFMEMORY : S_OK; }
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); }
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 value_t run_expression_until_condition(value_t ambience, value_t fragment, value_t expr) { runtime_t *runtime = get_ambience_runtime(ambience); TRY_DEF(code_block, compile_expression(runtime, expr, fragment, scope_get_bottom())); return run_code_block_until_condition(ambience, code_block); }
static HRESULT compile_const_statement(compile_ctx_t *ctx, const_statement_t *stat) { const_decl_t *decl, *next_decl = stat->decls; do { decl = next_decl; if(lookup_const_decls(ctx, decl->name, FALSE) || lookup_args_name(ctx, decl->name) || lookup_dim_decls(ctx, decl->name)) { FIXME("%s redefined\n", debugstr_w(decl->name)); return E_FAIL; } if(ctx->func->type == FUNC_GLOBAL) { HRESULT hres; hres = compile_expression(ctx, decl->value_expr); if(FAILED(hres)) return hres; hres = push_instr_bstr(ctx, OP_const, decl->name); if(FAILED(hres)) return hres; } next_decl = decl->next; decl->next = ctx->const_decls; ctx->const_decls = decl; } while(next_decl); 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; }
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_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; }
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 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) ? S_OK : E_OUTOFMEMORY; }
/* 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; }
static HRESULT compile_new_expression(compiler_ctx_t *ctx, call_expression_t *expr) { unsigned arg_cnt = 0; argument_t *arg; HRESULT hres; hres = compile_expression(ctx, expr->expression); if(FAILED(hres)) return hres; for(arg = expr->argument_list; arg; arg = arg->next) { hres = compile_expression(ctx, arg->expr); if(FAILED(hres)) return hres; arg_cnt++; } return push_instr_int(ctx, OP_new, arg_cnt); }
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) == -1 ? E_OUTOFMEMORY : S_OK; }
/* ECMA-262 3rd Edition 11.2.1 */ static HRESULT compile_member_expression(compiler_ctx_t *ctx, member_expression_t *expr) { HRESULT hres; hres = compile_expression(ctx, expr->expression); if(FAILED(hres)) return hres; return push_instr_bstr(ctx, OP_member, expr->identifier); }
/* ECMA-262 3rd Edition 11.11 */ static HRESULT compile_logical_expression(compiler_ctx_t *ctx, binary_expression_t *expr, jsop_t op) { unsigned instr; HRESULT hres; hres = compile_expression(ctx, expr->expression1); if(FAILED(hres)) return hres; instr = push_instr(ctx, op); if(!instr) return E_OUTOFMEMORY; hres = compile_expression(ctx, expr->expression2); if(FAILED(hres)) return hres; instr_ptr(ctx, instr)->arg1.uint = ctx->code_off; return S_OK; }
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; }
// Adds a namespace binding based on the given declaration ast in the given // fragment's namespace. static value_t apply_namespace_declaration(value_t ambience, value_t decl, value_t fragment) { CHECK_FAMILY(ofAmbience, ambience); CHECK_FAMILY(ofNamespaceDeclarationAst, decl); CHECK_FAMILY(ofModuleFragment, fragment); runtime_t *runtime = get_ambience_runtime(ambience); value_t value_syntax = get_namespace_declaration_ast_value(decl); TRY_DEF(code_block, compile_expression(runtime, value_syntax, fragment, scope_get_bottom())); TRY_DEF(value, run_code_block_until_condition(ambience, code_block)); value_t nspace = get_module_fragment_namespace(fragment); value_t path = get_namespace_declaration_ast_path(decl); TRY(set_namespace_binding_at(runtime, nspace, path, value)); return success(); }
static HRESULT compile_call_expression(compiler_ctx_t *ctx, call_expression_t *expr, BOOL *no_ret) { unsigned arg_cnt = 0; argument_t *arg; unsigned instr; jsop_t op; HRESULT hres; if(is_memberid_expr(expr->expression->type)) { op = OP_call_member; hres = compile_memberid_expression(ctx, expr->expression, 0); }else { op = OP_call; hres = compile_expression(ctx, expr->expression); } if(FAILED(hres)) return hres; for(arg = expr->argument_list; arg; arg = arg->next) { hres = compile_expression(ctx, arg->expr); if(FAILED(hres)) return hres; arg_cnt++; } instr = push_instr(ctx, op); if(!instr) return E_OUTOFMEMORY; instr_ptr(ctx, instr)->arg1.uint = arg_cnt; instr_ptr(ctx, instr)->arg2.lng = no_ret == NULL; if(no_ret) *no_ret = TRUE; return S_OK; }
/* ECMA-262 3rd Edition 12.9 */ static HRESULT compile_return_statement(compiler_ctx_t *ctx, expression_statement_t *stat) { HRESULT hres; hres = pop_to_stat(ctx, NULL); if(FAILED(hres)) return hres; if(stat->expr) { hres = compile_expression(ctx, stat->expr); if(FAILED(hres)) return hres; } return push_instr(ctx, OP_ret) ? S_OK : E_OUTOFMEMORY; }
static HRESULT compile_args(compile_ctx_t *ctx, expression_t *args, unsigned *ret) { unsigned arg_cnt = 0; HRESULT hres; while(args) { hres = compile_expression(ctx, args); if(FAILED(hres)) return hres; arg_cnt++; args = args->next; } *ret = arg_cnt; return S_OK; }
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); }