http://git-wip-us.apache.org/repos/asf/nifi-minifi-cpp/blob/5977aa27/thirdparty/civetweb-1.10/src/third_party/duktape-1.5.2/src-separate/duk_js_compiler.c ---------------------------------------------------------------------- diff --git a/thirdparty/civetweb-1.10/src/third_party/duktape-1.5.2/src-separate/duk_js_compiler.c b/thirdparty/civetweb-1.10/src/third_party/duktape-1.5.2/src-separate/duk_js_compiler.c deleted file mode 100644 index 3983c7c..0000000 --- a/thirdparty/civetweb-1.10/src/third_party/duktape-1.5.2/src-separate/duk_js_compiler.c +++ /dev/null @@ -1,7865 +0,0 @@ -/* - * Ecmascript compiler. - * - * Parses an input string and generates a function template result. - * Compilation may happen in multiple contexts (global code, eval - * code, function code). - * - * The parser uses a traditional top-down recursive parsing for the - * statement level, and an operator precedence based top-down approach - * for the expression level. The attempt is to minimize the C stack - * depth. Bytecode is generated directly without an intermediate - * representation (tree), at the cost of needing two passes over each - * function. - * - * The top-down recursive parser functions are named "duk__parse_XXX". - * - * Recursion limits are in key functions to prevent arbitrary C recursion: - * function body parsing, statement parsing, and expression parsing. - * - * See doc/compiler.rst for discussion on the design. - * - * A few typing notes: - * - * - duk_regconst_t: unsigned, no marker value for "none" - * - duk_reg_t: signed, < 0 = none - * - PC values: duk_int_t, negative values used as markers - */ - -#include "duk_internal.h" - -/* if highest bit of a register number is set, it refers to a constant instead */ -#define DUK__CONST_MARKER DUK_JS_CONST_MARKER - -/* for array and object literals */ -#define DUK__MAX_ARRAY_INIT_VALUES 20 -#define DUK__MAX_OBJECT_INIT_PAIRS 10 - -/* XXX: hack, remove when const lookup is not O(n) */ -#define DUK__GETCONST_MAX_CONSTS_CHECK 256 - -/* These limits are based on bytecode limits. Max temps is limited - * by duk_hcompiledfunction nargs/nregs fields being 16 bits. - */ -#define DUK__MAX_CONSTS DUK_BC_BC_MAX -#define DUK__MAX_FUNCS DUK_BC_BC_MAX -#define DUK__MAX_TEMPS 0xffffL - -/* Initial bytecode size allocation. */ -#define DUK__BC_INITIAL_INSTS 256 - -#define DUK__RECURSION_INCREASE(comp_ctx,thr) do { \ - DUK_DDD(DUK_DDDPRINT("RECURSION INCREASE: %s:%ld", (const char *) DUK_FILE_MACRO, (long) DUK_LINE_MACRO)); \ - duk__recursion_increase((comp_ctx)); \ - } while (0) - -#define DUK__RECURSION_DECREASE(comp_ctx,thr) do { \ - DUK_DDD(DUK_DDDPRINT("RECURSION DECREASE: %s:%ld", (const char *) DUK_FILE_MACRO, (long) DUK_LINE_MACRO)); \ - duk__recursion_decrease((comp_ctx)); \ - } while (0) - -/* Value stack slot limits: these are quite approximate right now, and - * because they overlap in control flow, some could be eliminated. - */ -#define DUK__COMPILE_ENTRY_SLOTS 8 -#define DUK__FUNCTION_INIT_REQUIRE_SLOTS 16 -#define DUK__FUNCTION_BODY_REQUIRE_SLOTS 16 -#define DUK__PARSE_STATEMENTS_SLOTS 16 -#define DUK__PARSE_EXPR_SLOTS 16 - -/* Temporary structure used to pass a stack allocated region through - * duk_safe_call(). - */ -typedef struct { - duk_small_uint_t flags; - duk_compiler_ctx comp_ctx_alloc; - duk_lexer_point lex_pt_alloc; -} duk__compiler_stkstate; - -/* - * Prototypes - */ - -/* lexing */ -DUK_LOCAL_DECL void duk__advance_helper(duk_compiler_ctx *comp_ctx, duk_small_int_t expect); -DUK_LOCAL_DECL void duk__advance_expect(duk_compiler_ctx *comp_ctx, duk_small_int_t expect); -DUK_LOCAL_DECL void duk__advance(duk_compiler_ctx *ctx); - -/* function helpers */ -DUK_LOCAL_DECL void duk__init_func_valstack_slots(duk_compiler_ctx *comp_ctx); -DUK_LOCAL_DECL void duk__reset_func_for_pass2(duk_compiler_ctx *comp_ctx); -DUK_LOCAL_DECL void duk__init_varmap_and_prologue_for_pass2(duk_compiler_ctx *comp_ctx, duk_reg_t *out_stmt_value_reg); -DUK_LOCAL_DECL void duk__convert_to_func_template(duk_compiler_ctx *comp_ctx, duk_bool_t force_no_namebind); -DUK_LOCAL_DECL duk_int_t duk__cleanup_varmap(duk_compiler_ctx *comp_ctx); - -/* code emission */ -DUK_LOCAL_DECL duk_int_t duk__get_current_pc(duk_compiler_ctx *comp_ctx); -DUK_LOCAL_DECL duk_compiler_instr *duk__get_instr_ptr(duk_compiler_ctx *comp_ctx, duk_int_t pc); -DUK_LOCAL_DECL void duk__emit(duk_compiler_ctx *comp_ctx, duk_instr_t ins); -#if 0 /* unused */ -DUK_LOCAL_DECL void duk__emit_op_only(duk_compiler_ctx *comp_ctx, duk_small_uint_t op); -#endif -DUK_LOCAL_DECL void duk__emit_a_b_c(duk_compiler_ctx *comp_ctx, duk_small_uint_t op_flags, duk_regconst_t a, duk_regconst_t b, duk_regconst_t c); -DUK_LOCAL_DECL void duk__emit_a_b(duk_compiler_ctx *comp_ctx, duk_small_uint_t op_flags, duk_regconst_t a, duk_regconst_t b); -#if 0 /* unused */ -DUK_LOCAL_DECL void duk__emit_a(duk_compiler_ctx *comp_ctx, duk_small_uint_t op_flags, duk_regconst_t a); -#endif -DUK_LOCAL_DECL void duk__emit_a_bc(duk_compiler_ctx *comp_ctx, duk_small_uint_t op_flags, duk_regconst_t a, duk_regconst_t bc); -DUK_LOCAL_DECL void duk__emit_abc(duk_compiler_ctx *comp_ctx, duk_small_uint_t op, duk_regconst_t abc); -DUK_LOCAL_DECL void duk__emit_extraop_b_c(duk_compiler_ctx *comp_ctx, duk_small_uint_t extraop_flags, duk_regconst_t b, duk_regconst_t c); -DUK_LOCAL_DECL void duk__emit_extraop_b(duk_compiler_ctx *comp_ctx, duk_small_uint_t extraop_flags, duk_regconst_t b); -DUK_LOCAL_DECL void duk__emit_extraop_bc(duk_compiler_ctx *comp_ctx, duk_small_uint_t extraop, duk_regconst_t bc); -DUK_LOCAL_DECL void duk__emit_extraop_only(duk_compiler_ctx *comp_ctx, duk_small_uint_t extraop_flags); -DUK_LOCAL_DECL void duk__emit_load_int32(duk_compiler_ctx *comp_ctx, duk_reg_t reg, duk_int32_t val); -DUK_LOCAL_DECL void duk__emit_load_int32_noshuffle(duk_compiler_ctx *comp_ctx, duk_reg_t reg, duk_int32_t val); -DUK_LOCAL_DECL void duk__emit_jump(duk_compiler_ctx *comp_ctx, duk_int_t target_pc); -DUK_LOCAL_DECL duk_int_t duk__emit_jump_empty(duk_compiler_ctx *comp_ctx); -DUK_LOCAL_DECL void duk__insert_jump_entry(duk_compiler_ctx *comp_ctx, duk_int_t jump_pc); -DUK_LOCAL_DECL void duk__patch_jump(duk_compiler_ctx *comp_ctx, duk_int_t jump_pc, duk_int_t target_pc); -DUK_LOCAL_DECL void duk__patch_jump_here(duk_compiler_ctx *comp_ctx, duk_int_t jump_pc); -DUK_LOCAL_DECL void duk__patch_trycatch(duk_compiler_ctx *comp_ctx, duk_int_t ldconst_pc, duk_int_t trycatch_pc, duk_regconst_t reg_catch, duk_regconst_t const_varname, duk_small_uint_t flags); -DUK_LOCAL_DECL void duk__emit_if_false_skip(duk_compiler_ctx *comp_ctx, duk_regconst_t regconst); -DUK_LOCAL_DECL void duk__emit_if_true_skip(duk_compiler_ctx *comp_ctx, duk_regconst_t regconst); -DUK_LOCAL_DECL void duk__emit_invalid(duk_compiler_ctx *comp_ctx); - -/* ivalue/ispec helpers */ -DUK_LOCAL_DECL void duk__copy_ispec(duk_compiler_ctx *comp_ctx, duk_ispec *src, duk_ispec *dst); -DUK_LOCAL_DECL void duk__copy_ivalue(duk_compiler_ctx *comp_ctx, duk_ivalue *src, duk_ivalue *dst); -DUK_LOCAL_DECL duk_bool_t duk__is_whole_get_int32(duk_double_t x, duk_int32_t *ival); -DUK_LOCAL_DECL duk_reg_t duk__alloctemps(duk_compiler_ctx *comp_ctx, duk_small_int_t num); -DUK_LOCAL_DECL duk_reg_t duk__alloctemp(duk_compiler_ctx *comp_ctx); -DUK_LOCAL_DECL void duk__settemp_checkmax(duk_compiler_ctx *comp_ctx, duk_reg_t temp_next); -DUK_LOCAL_DECL duk_regconst_t duk__getconst(duk_compiler_ctx *comp_ctx); -DUK_LOCAL_DECL -duk_regconst_t duk__ispec_toregconst_raw(duk_compiler_ctx *comp_ctx, - duk_ispec *x, - duk_reg_t forced_reg, - duk_small_uint_t flags); -DUK_LOCAL_DECL void duk__ispec_toforcedreg(duk_compiler_ctx *comp_ctx, duk_ispec *x, duk_reg_t forced_reg); -DUK_LOCAL_DECL void duk__ivalue_toplain_raw(duk_compiler_ctx *comp_ctx, duk_ivalue *x, duk_reg_t forced_reg); -DUK_LOCAL_DECL void duk__ivalue_toplain(duk_compiler_ctx *comp_ctx, duk_ivalue *x); -DUK_LOCAL_DECL void duk__ivalue_toplain_ignore(duk_compiler_ctx *comp_ctx, duk_ivalue *x); -DUK_LOCAL_DECL -duk_regconst_t duk__ivalue_toregconst_raw(duk_compiler_ctx *comp_ctx, - duk_ivalue *x, - duk_reg_t forced_reg, - duk_small_uint_t flags); -DUK_LOCAL_DECL duk_reg_t duk__ivalue_toreg(duk_compiler_ctx *comp_ctx, duk_ivalue *x); -#if 0 /* unused */ -DUK_LOCAL_DECL duk_reg_t duk__ivalue_totemp(duk_compiler_ctx *comp_ctx, duk_ivalue *x); -#endif -DUK_LOCAL_DECL void duk__ivalue_toforcedreg(duk_compiler_ctx *comp_ctx, duk_ivalue *x, duk_int_t forced_reg); -DUK_LOCAL_DECL duk_regconst_t duk__ivalue_toregconst(duk_compiler_ctx *comp_ctx, duk_ivalue *x); -DUK_LOCAL_DECL duk_regconst_t duk__ivalue_totempconst(duk_compiler_ctx *comp_ctx, duk_ivalue *x); - -/* identifier handling */ -DUK_LOCAL_DECL duk_reg_t duk__lookup_active_register_binding(duk_compiler_ctx *comp_ctx); -DUK_LOCAL_DECL duk_bool_t duk__lookup_lhs(duk_compiler_ctx *ctx, duk_reg_t *out_reg_varbind, duk_regconst_t *out_rc_varname); - -/* label handling */ -DUK_LOCAL_DECL void duk__add_label(duk_compiler_ctx *comp_ctx, duk_hstring *h_label, duk_int_t pc_label, duk_int_t label_id); -DUK_LOCAL_DECL void duk__update_label_flags(duk_compiler_ctx *comp_ctx, duk_int_t label_id, duk_small_uint_t flags); -DUK_LOCAL_DECL void duk__lookup_active_label(duk_compiler_ctx *comp_ctx, duk_hstring *h_label, duk_bool_t is_break, duk_int_t *out_label_id, duk_int_t *out_label_catch_depth, duk_int_t *out_label_pc, duk_bool_t *out_is_closest); -DUK_LOCAL_DECL void duk__reset_labels_to_length(duk_compiler_ctx *comp_ctx, duk_int_t len); - -/* top-down expression parser */ -DUK_LOCAL_DECL void duk__expr_nud(duk_compiler_ctx *comp_ctx, duk_ivalue *res); -DUK_LOCAL_DECL void duk__expr_led(duk_compiler_ctx *comp_ctx, duk_ivalue *left, duk_ivalue *res); -DUK_LOCAL_DECL duk_small_uint_t duk__expr_lbp(duk_compiler_ctx *comp_ctx); -DUK_LOCAL_DECL duk_bool_t duk__expr_is_empty(duk_compiler_ctx *comp_ctx); - -/* exprtop is the top level variant which resets nud/led counts */ -DUK_LOCAL_DECL void duk__expr(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_small_uint_t rbp_flags); -DUK_LOCAL_DECL void duk__exprtop(duk_compiler_ctx *ctx, duk_ivalue *res, duk_small_uint_t rbp_flags); - -/* convenience helpers */ -#if 0 /* unused */ -DUK_LOCAL_DECL duk_reg_t duk__expr_toreg(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_small_uint_t rbp_flags); -#endif -#if 0 /* unused */ -DUK_LOCAL_DECL duk_reg_t duk__expr_totemp(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_small_uint_t rbp_flags); -#endif -DUK_LOCAL_DECL void duk__expr_toforcedreg(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_small_uint_t rbp_flags, duk_reg_t forced_reg); -DUK_LOCAL_DECL duk_regconst_t duk__expr_toregconst(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_small_uint_t rbp_flags); -#if 0 /* unused */ -DUK_LOCAL_DECL duk_regconst_t duk__expr_totempconst(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_small_uint_t rbp_flags); -#endif -DUK_LOCAL_DECL void duk__expr_toplain(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_small_uint_t rbp_flags); -DUK_LOCAL_DECL void duk__expr_toplain_ignore(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_small_uint_t rbp_flags); -DUK_LOCAL_DECL duk_reg_t duk__exprtop_toreg(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_small_uint_t rbp_flags); -#if 0 /* unused */ -DUK_LOCAL_DECL duk_reg_t duk__exprtop_totemp(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_small_uint_t rbp_flags); -#endif -DUK_LOCAL_DECL void duk__exprtop_toforcedreg(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_small_uint_t rbp_flags, duk_reg_t forced_reg); -DUK_LOCAL_DECL duk_regconst_t duk__exprtop_toregconst(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_small_uint_t rbp_flags); -#if 0 /* unused */ -DUK_LOCAL_DECL void duk__exprtop_toplain_ignore(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_small_uint_t rbp_flags); -#endif - -/* expression parsing helpers */ -DUK_LOCAL_DECL duk_int_t duk__parse_arguments(duk_compiler_ctx *comp_ctx, duk_ivalue *res); -DUK_LOCAL_DECL void duk__nud_array_literal(duk_compiler_ctx *comp_ctx, duk_ivalue *res); -DUK_LOCAL_DECL void duk__nud_object_literal(duk_compiler_ctx *comp_ctx, duk_ivalue *res); -DUK_LOCAL_DECL duk_bool_t duk__nud_object_literal_key_check(duk_compiler_ctx *comp_ctx, duk_small_uint_t new_key_flags); - -/* statement parsing */ -DUK_LOCAL_DECL void duk__parse_var_decl(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_small_uint_t expr_flags, duk_reg_t *out_reg_varbind, duk_regconst_t *out_rc_varname); -DUK_LOCAL_DECL void duk__parse_var_stmt(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_small_uint_t expr_flags); -DUK_LOCAL_DECL void duk__parse_for_stmt(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_int_t pc_label_site); -DUK_LOCAL_DECL void duk__parse_switch_stmt(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_int_t pc_label_site); -DUK_LOCAL_DECL void duk__parse_if_stmt(duk_compiler_ctx *comp_ctx, duk_ivalue *res); -DUK_LOCAL_DECL void duk__parse_do_stmt(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_int_t pc_label_site); -DUK_LOCAL_DECL void duk__parse_while_stmt(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_int_t pc_label_site); -DUK_LOCAL_DECL void duk__parse_break_or_continue_stmt(duk_compiler_ctx *comp_ctx, duk_ivalue *res); -DUK_LOCAL_DECL void duk__parse_return_stmt(duk_compiler_ctx *comp_ctx, duk_ivalue *res); -DUK_LOCAL_DECL void duk__parse_throw_stmt(duk_compiler_ctx *comp_ctx, duk_ivalue *res); -DUK_LOCAL_DECL void duk__parse_try_stmt(duk_compiler_ctx *comp_ctx, duk_ivalue *res); -DUK_LOCAL_DECL void duk__parse_with_stmt(duk_compiler_ctx *comp_ctx, duk_ivalue *res); -DUK_LOCAL_DECL void duk__parse_stmt(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_bool_t allow_source_elem); -DUK_LOCAL_DECL duk_int_t duk__stmt_label_site(duk_compiler_ctx *comp_ctx, duk_int_t label_id); -DUK_LOCAL_DECL void duk__parse_stmts(duk_compiler_ctx *comp_ctx, duk_bool_t allow_source_elem, duk_bool_t expect_eof); - -DUK_LOCAL_DECL void duk__parse_func_body(duk_compiler_ctx *comp_ctx, duk_bool_t expect_eof, duk_bool_t implicit_return_value, duk_small_int_t expect_token); -DUK_LOCAL_DECL void duk__parse_func_formals(duk_compiler_ctx *comp_ctx); -DUK_LOCAL_DECL void duk__parse_func_like_raw(duk_compiler_ctx *comp_ctx, duk_bool_t is_decl, duk_bool_t is_setget); -DUK_LOCAL_DECL duk_int_t duk__parse_func_like_fnum(duk_compiler_ctx *comp_ctx, duk_bool_t is_decl, duk_bool_t is_setget); - -/* - * Parser control values for tokens. The token table is ordered by the - * DUK_TOK_XXX defines. - * - * The binding powers are for lbp() use (i.e. for use in led() context). - * Binding powers are positive for typing convenience, and bits at the - * top should be reserved for flags. Binding power step must be higher - * than 1 so that binding power "lbp - 1" can be used for right associative - * operators. Currently a step of 2 is used (which frees one more bit for - * flags). - */ - -/* XXX: actually single step levels would work just fine, clean up */ - -/* binding power "levels" (see doc/compiler.rst) */ -#define DUK__BP_INVALID 0 /* always terminates led() */ -#define DUK__BP_EOF 2 -#define DUK__BP_CLOSING 4 /* token closes expression, e.g. ')', ']' */ -#define DUK__BP_FOR_EXPR DUK__BP_CLOSING /* bp to use when parsing a top level Expression */ -#define DUK__BP_COMMA 6 -#define DUK__BP_ASSIGNMENT 8 -#define DUK__BP_CONDITIONAL 10 -#define DUK__BP_LOR 12 -#define DUK__BP_LAND 14 -#define DUK__BP_BOR 16 -#define DUK__BP_BXOR 18 -#define DUK__BP_BAND 20 -#define DUK__BP_EQUALITY 22 -#define DUK__BP_RELATIONAL 24 -#define DUK__BP_SHIFT 26 -#define DUK__BP_ADDITIVE 28 -#define DUK__BP_MULTIPLICATIVE 30 -#define DUK__BP_POSTFIX 32 -#define DUK__BP_CALL 34 -#define DUK__BP_MEMBER 36 - -#define DUK__TOKEN_LBP_BP_MASK 0x1f -#define DUK__TOKEN_LBP_FLAG_NO_REGEXP (1 << 5) /* regexp literal must not follow this token */ -#define DUK__TOKEN_LBP_FLAG_TERMINATES (1 << 6) /* terminates expression; e.g. post-increment/-decrement */ -#define DUK__TOKEN_LBP_FLAG_UNUSED (1 << 7) /* spare */ - -#define DUK__TOKEN_LBP_GET_BP(x) ((duk_small_uint_t) (((x) & DUK__TOKEN_LBP_BP_MASK) * 2)) - -#define DUK__MK_LBP(bp) ((bp) >> 1) /* bp is assumed to be even */ -#define DUK__MK_LBP_FLAGS(bp,flags) (((bp) >> 1) | (flags)) - -DUK_LOCAL const duk_uint8_t duk__token_lbp[] = { - DUK__MK_LBP(DUK__BP_EOF), /* DUK_TOK_EOF */ - DUK__MK_LBP_FLAGS(DUK__BP_INVALID, DUK__TOKEN_LBP_FLAG_NO_REGEXP), /* DUK_TOK_IDENTIFIER */ - DUK__MK_LBP(DUK__BP_INVALID), /* DUK_TOK_BREAK */ - DUK__MK_LBP(DUK__BP_INVALID), /* DUK_TOK_CASE */ - DUK__MK_LBP(DUK__BP_INVALID), /* DUK_TOK_CATCH */ - DUK__MK_LBP(DUK__BP_INVALID), /* DUK_TOK_CONTINUE */ - DUK__MK_LBP(DUK__BP_INVALID), /* DUK_TOK_DEBUGGER */ - DUK__MK_LBP(DUK__BP_INVALID), /* DUK_TOK_DEFAULT */ - DUK__MK_LBP(DUK__BP_INVALID), /* DUK_TOK_DELETE */ - DUK__MK_LBP(DUK__BP_INVALID), /* DUK_TOK_DO */ - DUK__MK_LBP(DUK__BP_INVALID), /* DUK_TOK_ELSE */ - DUK__MK_LBP(DUK__BP_INVALID), /* DUK_TOK_FINALLY */ - DUK__MK_LBP(DUK__BP_INVALID), /* DUK_TOK_FOR */ - DUK__MK_LBP(DUK__BP_INVALID), /* DUK_TOK_FUNCTION */ - DUK__MK_LBP(DUK__BP_INVALID), /* DUK_TOK_IF */ - DUK__MK_LBP(DUK__BP_RELATIONAL), /* DUK_TOK_IN */ - DUK__MK_LBP(DUK__BP_RELATIONAL), /* DUK_TOK_INSTANCEOF */ - DUK__MK_LBP(DUK__BP_INVALID), /* DUK_TOK_NEW */ - DUK__MK_LBP(DUK__BP_INVALID), /* DUK_TOK_RETURN */ - DUK__MK_LBP(DUK__BP_INVALID), /* DUK_TOK_SWITCH */ - DUK__MK_LBP_FLAGS(DUK__BP_INVALID, DUK__TOKEN_LBP_FLAG_NO_REGEXP), /* DUK_TOK_THIS */ - DUK__MK_LBP(DUK__BP_INVALID), /* DUK_TOK_THROW */ - DUK__MK_LBP(DUK__BP_INVALID), /* DUK_TOK_TRY */ - DUK__MK_LBP(DUK__BP_INVALID), /* DUK_TOK_TYPEOF */ - DUK__MK_LBP(DUK__BP_INVALID), /* DUK_TOK_VAR */ - DUK__MK_LBP(DUK__BP_INVALID), /* DUK_TOK_CONST */ - DUK__MK_LBP(DUK__BP_INVALID), /* DUK_TOK_VOID */ - DUK__MK_LBP(DUK__BP_INVALID), /* DUK_TOK_WHILE */ - DUK__MK_LBP(DUK__BP_INVALID), /* DUK_TOK_WITH */ - DUK__MK_LBP(DUK__BP_INVALID), /* DUK_TOK_CLASS */ - DUK__MK_LBP(DUK__BP_INVALID), /* DUK_TOK_ENUM */ - DUK__MK_LBP(DUK__BP_INVALID), /* DUK_TOK_EXPORT */ - DUK__MK_LBP(DUK__BP_INVALID), /* DUK_TOK_EXTENDS */ - DUK__MK_LBP(DUK__BP_INVALID), /* DUK_TOK_IMPORT */ - DUK__MK_LBP(DUK__BP_INVALID), /* DUK_TOK_SUPER */ - DUK__MK_LBP_FLAGS(DUK__BP_INVALID, DUK__TOKEN_LBP_FLAG_NO_REGEXP), /* DUK_TOK_NULL */ - DUK__MK_LBP_FLAGS(DUK__BP_INVALID, DUK__TOKEN_LBP_FLAG_NO_REGEXP), /* DUK_TOK_TRUE */ - DUK__MK_LBP_FLAGS(DUK__BP_INVALID, DUK__TOKEN_LBP_FLAG_NO_REGEXP), /* DUK_TOK_FALSE */ - DUK__MK_LBP(DUK__BP_INVALID), /* DUK_TOK_GET */ - DUK__MK_LBP(DUK__BP_INVALID), /* DUK_TOK_SET */ - DUK__MK_LBP(DUK__BP_INVALID), /* DUK_TOK_IMPLEMENTS */ - DUK__MK_LBP(DUK__BP_INVALID), /* DUK_TOK_INTERFACE */ - DUK__MK_LBP(DUK__BP_INVALID), /* DUK_TOK_LET */ - DUK__MK_LBP(DUK__BP_INVALID), /* DUK_TOK_PACKAGE */ - DUK__MK_LBP(DUK__BP_INVALID), /* DUK_TOK_PRIVATE */ - DUK__MK_LBP(DUK__BP_INVALID), /* DUK_TOK_PROTECTED */ - DUK__MK_LBP(DUK__BP_INVALID), /* DUK_TOK_PUBLIC */ - DUK__MK_LBP(DUK__BP_INVALID), /* DUK_TOK_STATIC */ - DUK__MK_LBP(DUK__BP_INVALID), /* DUK_TOK_YIELD */ - DUK__MK_LBP(DUK__BP_INVALID), /* DUK_TOK_LCURLY */ - DUK__MK_LBP_FLAGS(DUK__BP_INVALID, DUK__TOKEN_LBP_FLAG_NO_REGEXP), /* DUK_TOK_RCURLY */ - DUK__MK_LBP(DUK__BP_MEMBER), /* DUK_TOK_LBRACKET */ - DUK__MK_LBP_FLAGS(DUK__BP_CLOSING, DUK__TOKEN_LBP_FLAG_NO_REGEXP), /* DUK_TOK_RBRACKET */ - DUK__MK_LBP(DUK__BP_CALL), /* DUK_TOK_LPAREN */ - DUK__MK_LBP_FLAGS(DUK__BP_CLOSING, DUK__TOKEN_LBP_FLAG_NO_REGEXP), /* DUK_TOK_RPAREN */ - DUK__MK_LBP(DUK__BP_MEMBER), /* DUK_TOK_PERIOD */ - DUK__MK_LBP(DUK__BP_INVALID), /* DUK_TOK_SEMICOLON */ - DUK__MK_LBP(DUK__BP_COMMA), /* DUK_TOK_COMMA */ - DUK__MK_LBP(DUK__BP_RELATIONAL), /* DUK_TOK_LT */ - DUK__MK_LBP(DUK__BP_RELATIONAL), /* DUK_TOK_GT */ - DUK__MK_LBP(DUK__BP_RELATIONAL), /* DUK_TOK_LE */ - DUK__MK_LBP(DUK__BP_RELATIONAL), /* DUK_TOK_GE */ - DUK__MK_LBP(DUK__BP_EQUALITY), /* DUK_TOK_EQ */ - DUK__MK_LBP(DUK__BP_EQUALITY), /* DUK_TOK_NEQ */ - DUK__MK_LBP(DUK__BP_EQUALITY), /* DUK_TOK_SEQ */ - DUK__MK_LBP(DUK__BP_EQUALITY), /* DUK_TOK_SNEQ */ - DUK__MK_LBP(DUK__BP_ADDITIVE), /* DUK_TOK_ADD */ - DUK__MK_LBP(DUK__BP_ADDITIVE), /* DUK_TOK_SUB */ - DUK__MK_LBP(DUK__BP_MULTIPLICATIVE), /* DUK_TOK_MUL */ - DUK__MK_LBP(DUK__BP_MULTIPLICATIVE), /* DUK_TOK_DIV */ - DUK__MK_LBP(DUK__BP_MULTIPLICATIVE), /* DUK_TOK_MOD */ - DUK__MK_LBP(DUK__BP_POSTFIX), /* DUK_TOK_INCREMENT */ - DUK__MK_LBP(DUK__BP_POSTFIX), /* DUK_TOK_DECREMENT */ - DUK__MK_LBP(DUK__BP_SHIFT), /* DUK_TOK_ALSHIFT */ - DUK__MK_LBP(DUK__BP_SHIFT), /* DUK_TOK_ARSHIFT */ - DUK__MK_LBP(DUK__BP_SHIFT), /* DUK_TOK_RSHIFT */ - DUK__MK_LBP(DUK__BP_BAND), /* DUK_TOK_BAND */ - DUK__MK_LBP(DUK__BP_BOR), /* DUK_TOK_BOR */ - DUK__MK_LBP(DUK__BP_BXOR), /* DUK_TOK_BXOR */ - DUK__MK_LBP(DUK__BP_INVALID), /* DUK_TOK_LNOT */ - DUK__MK_LBP(DUK__BP_INVALID), /* DUK_TOK_BNOT */ - DUK__MK_LBP(DUK__BP_LAND), /* DUK_TOK_LAND */ - DUK__MK_LBP(DUK__BP_LOR), /* DUK_TOK_LOR */ - DUK__MK_LBP(DUK__BP_CONDITIONAL), /* DUK_TOK_QUESTION */ - DUK__MK_LBP(DUK__BP_INVALID), /* DUK_TOK_COLON */ - DUK__MK_LBP(DUK__BP_ASSIGNMENT), /* DUK_TOK_EQUALSIGN */ - DUK__MK_LBP(DUK__BP_ASSIGNMENT), /* DUK_TOK_ADD_EQ */ - DUK__MK_LBP(DUK__BP_ASSIGNMENT), /* DUK_TOK_SUB_EQ */ - DUK__MK_LBP(DUK__BP_ASSIGNMENT), /* DUK_TOK_MUL_EQ */ - DUK__MK_LBP(DUK__BP_ASSIGNMENT), /* DUK_TOK_DIV_EQ */ - DUK__MK_LBP(DUK__BP_ASSIGNMENT), /* DUK_TOK_MOD_EQ */ - DUK__MK_LBP(DUK__BP_ASSIGNMENT), /* DUK_TOK_ALSHIFT_EQ */ - DUK__MK_LBP(DUK__BP_ASSIGNMENT), /* DUK_TOK_ARSHIFT_EQ */ - DUK__MK_LBP(DUK__BP_ASSIGNMENT), /* DUK_TOK_RSHIFT_EQ */ - DUK__MK_LBP(DUK__BP_ASSIGNMENT), /* DUK_TOK_BAND_EQ */ - DUK__MK_LBP(DUK__BP_ASSIGNMENT), /* DUK_TOK_BOR_EQ */ - DUK__MK_LBP(DUK__BP_ASSIGNMENT), /* DUK_TOK_BXOR_EQ */ - DUK__MK_LBP_FLAGS(DUK__BP_INVALID, DUK__TOKEN_LBP_FLAG_NO_REGEXP), /* DUK_TOK_NUMBER */ - DUK__MK_LBP_FLAGS(DUK__BP_INVALID, DUK__TOKEN_LBP_FLAG_NO_REGEXP), /* DUK_TOK_STRING */ - DUK__MK_LBP_FLAGS(DUK__BP_INVALID, DUK__TOKEN_LBP_FLAG_NO_REGEXP), /* DUK_TOK_REGEXP */ -}; - -/* - * Misc helpers - */ - -DUK_LOCAL void duk__recursion_increase(duk_compiler_ctx *comp_ctx) { - DUK_ASSERT(comp_ctx != NULL); - DUK_ASSERT(comp_ctx->recursion_depth >= 0); - if (comp_ctx->recursion_depth >= comp_ctx->recursion_limit) { - DUK_ERROR_RANGE(comp_ctx->thr, DUK_STR_COMPILER_RECURSION_LIMIT); - } - comp_ctx->recursion_depth++; -} - -DUK_LOCAL void duk__recursion_decrease(duk_compiler_ctx *comp_ctx) { - DUK_ASSERT(comp_ctx != NULL); - DUK_ASSERT(comp_ctx->recursion_depth > 0); - comp_ctx->recursion_depth--; -} - -DUK_LOCAL duk_bool_t duk__hstring_is_eval_or_arguments(duk_compiler_ctx *comp_ctx, duk_hstring *h) { - DUK_UNREF(comp_ctx); - DUK_ASSERT(h != NULL); - return DUK_HSTRING_HAS_EVAL_OR_ARGUMENTS(h); -} - -DUK_LOCAL duk_bool_t duk__hstring_is_eval_or_arguments_in_strict_mode(duk_compiler_ctx *comp_ctx, duk_hstring *h) { - DUK_ASSERT(h != NULL); - return (comp_ctx->curr_func.is_strict && - DUK_HSTRING_HAS_EVAL_OR_ARGUMENTS(h)); -} - -/* - * Parser duk__advance() token eating functions - */ - -/* XXX: valstack handling is awkward. Add a valstack helper which - * avoids dup():ing; valstack_copy(src, dst)? - */ - -DUK_LOCAL void duk__advance_helper(duk_compiler_ctx *comp_ctx, duk_small_int_t expect) { - duk_hthread *thr = comp_ctx->thr; - duk_context *ctx = (duk_context *) thr; - duk_bool_t regexp; - - DUK_ASSERT(comp_ctx->curr_token.t >= 0 && comp_ctx->curr_token.t <= DUK_TOK_MAXVAL); /* MAXVAL is inclusive */ - - /* - * Use current token to decide whether a RegExp can follow. - * - * We can use either 't' or 't_nores'; the latter would not - * recognize keywords. Some keywords can be followed by a - * RegExp (e.g. "return"), so using 't' is better. This is - * not trivial, see doc/compiler.rst. - */ - - regexp = 1; - if (duk__token_lbp[comp_ctx->curr_token.t] & DUK__TOKEN_LBP_FLAG_NO_REGEXP) { - regexp = 0; - } - if (comp_ctx->curr_func.reject_regexp_in_adv) { - comp_ctx->curr_func.reject_regexp_in_adv = 0; - regexp = 0; - } - - if (expect >= 0 && comp_ctx->curr_token.t != expect) { - DUK_D(DUK_DPRINT("parse error: expect=%ld, got=%ld", - (long) expect, (long) comp_ctx->curr_token.t)); - DUK_ERROR_SYNTAX(thr, DUK_STR_PARSE_ERROR); - } - - /* make current token the previous; need to fiddle with valstack "backing store" */ - DUK_MEMCPY(&comp_ctx->prev_token, &comp_ctx->curr_token, sizeof(duk_token)); - duk_copy(ctx, comp_ctx->tok11_idx, comp_ctx->tok21_idx); - duk_copy(ctx, comp_ctx->tok12_idx, comp_ctx->tok22_idx); - - /* parse new token */ - duk_lexer_parse_js_input_element(&comp_ctx->lex, - &comp_ctx->curr_token, - comp_ctx->curr_func.is_strict, - regexp); - - DUK_DDD(DUK_DDDPRINT("advance: curr: tok=%ld/%ld,%ld,term=%ld,%!T,%!T " - "prev: tok=%ld/%ld,%ld,term=%ld,%!T,%!T", - (long) comp_ctx->curr_token.t, - (long) comp_ctx->curr_token.t_nores, - (long) comp_ctx->curr_token.start_line, - (long) comp_ctx->curr_token.lineterm, - (duk_tval *) duk_get_tval(ctx, comp_ctx->tok11_idx), - (duk_tval *) duk_get_tval(ctx, comp_ctx->tok12_idx), - (long) comp_ctx->prev_token.t, - (long) comp_ctx->prev_token.t_nores, - (long) comp_ctx->prev_token.start_line, - (long) comp_ctx->prev_token.lineterm, - (duk_tval *) duk_get_tval(ctx, comp_ctx->tok21_idx), - (duk_tval *) duk_get_tval(ctx, comp_ctx->tok22_idx))); -} - -/* advance, expecting current token to be a specific token; parse next token in regexp context */ -DUK_LOCAL void duk__advance_expect(duk_compiler_ctx *comp_ctx, duk_small_int_t expect) { - duk__advance_helper(comp_ctx, expect); -} - -/* advance, whatever the current token is; parse next token in regexp context */ -DUK_LOCAL void duk__advance(duk_compiler_ctx *comp_ctx) { - duk__advance_helper(comp_ctx, -1); -} - -/* - * Helpers for duk_compiler_func. - */ - -/* init function state: inits valstack allocations */ -DUK_LOCAL void duk__init_func_valstack_slots(duk_compiler_ctx *comp_ctx) { - duk_compiler_func *func = &comp_ctx->curr_func; - duk_hthread *thr = comp_ctx->thr; - duk_context *ctx = (duk_context *) thr; - duk_idx_t entry_top; - - entry_top = duk_get_top(ctx); - - DUK_MEMZERO(func, sizeof(*func)); /* intentional overlap with earlier memzero */ -#ifdef DUK_USE_EXPLICIT_NULL_INIT - func->h_name = NULL; - func->h_consts = NULL; - func->h_funcs = NULL; - func->h_decls = NULL; - func->h_labelnames = NULL; - func->h_labelinfos = NULL; - func->h_argnames = NULL; - func->h_varmap = NULL; -#endif - - duk_require_stack(ctx, DUK__FUNCTION_INIT_REQUIRE_SLOTS); - - DUK_BW_INIT_PUSHBUF(thr, &func->bw_code, DUK__BC_INITIAL_INSTS * sizeof(duk_compiler_instr)); - /* code_idx = entry_top + 0 */ - - duk_push_array(ctx); - func->consts_idx = entry_top + 1; - func->h_consts = DUK_GET_HOBJECT_POSIDX(ctx, entry_top + 1); - DUK_ASSERT(func->h_consts != NULL); - - duk_push_array(ctx); - func->funcs_idx = entry_top + 2; - func->h_funcs = DUK_GET_HOBJECT_POSIDX(ctx, entry_top + 2); - DUK_ASSERT(func->h_funcs != NULL); - DUK_ASSERT(func->fnum_next == 0); - - duk_push_array(ctx); - func->decls_idx = entry_top + 3; - func->h_decls = DUK_GET_HOBJECT_POSIDX(ctx, entry_top + 3); - DUK_ASSERT(func->h_decls != NULL); - - duk_push_array(ctx); - func->labelnames_idx = entry_top + 4; - func->h_labelnames = DUK_GET_HOBJECT_POSIDX(ctx, entry_top + 4); - DUK_ASSERT(func->h_labelnames != NULL); - - duk_push_dynamic_buffer(ctx, 0); - func->labelinfos_idx = entry_top + 5; - func->h_labelinfos = (duk_hbuffer_dynamic *) duk_get_hbuffer(ctx, entry_top + 5); - DUK_ASSERT(func->h_labelinfos != NULL); - DUK_ASSERT(DUK_HBUFFER_HAS_DYNAMIC(func->h_labelinfos) && !DUK_HBUFFER_HAS_EXTERNAL(func->h_labelinfos)); - - duk_push_array(ctx); - func->argnames_idx = entry_top + 6; - func->h_argnames = DUK_GET_HOBJECT_POSIDX(ctx, entry_top + 6); - DUK_ASSERT(func->h_argnames != NULL); - - duk_push_object_internal(ctx); - func->varmap_idx = entry_top + 7; - func->h_varmap = DUK_GET_HOBJECT_POSIDX(ctx, entry_top + 7); - DUK_ASSERT(func->h_varmap != NULL); -} - -/* reset function state (prepare for pass 2) */ -DUK_LOCAL void duk__reset_func_for_pass2(duk_compiler_ctx *comp_ctx) { - duk_compiler_func *func = &comp_ctx->curr_func; - duk_hthread *thr = comp_ctx->thr; - duk_context *ctx = (duk_context *) thr; - - /* reset bytecode buffer but keep current size; pass 2 will - * require same amount or more. - */ - DUK_BW_RESET_SIZE(thr, &func->bw_code); - - duk_hobject_set_length_zero(thr, func->h_consts); - /* keep func->h_funcs; inner functions are not reparsed to avoid O(depth^2) parsing */ - func->fnum_next = 0; - /* duk_hobject_set_length_zero(thr, func->h_funcs); */ - duk_hobject_set_length_zero(thr, func->h_labelnames); - duk_hbuffer_reset(thr, func->h_labelinfos); - /* keep func->h_argnames; it is fixed for all passes */ - - /* truncated in case pass 3 needed */ - duk_push_object_internal(ctx); - duk_replace(ctx, func->varmap_idx); - func->h_varmap = DUK_GET_HOBJECT_POSIDX(ctx, func->varmap_idx); - DUK_ASSERT(func->h_varmap != NULL); -} - -/* cleanup varmap from any null entries, compact it, etc; returns number - * of final entries after cleanup. - */ -DUK_LOCAL duk_int_t duk__cleanup_varmap(duk_compiler_ctx *comp_ctx) { - duk_hthread *thr = comp_ctx->thr; - duk_context *ctx = (duk_context *) thr; - duk_hobject *h_varmap; - duk_hstring *h_key; - duk_tval *tv; - duk_uint32_t i, e_next; - duk_int_t ret; - - /* [ ... varmap ] */ - - h_varmap = DUK_GET_HOBJECT_NEGIDX(ctx, -1); - DUK_ASSERT(h_varmap != NULL); - - ret = 0; - e_next = DUK_HOBJECT_GET_ENEXT(h_varmap); - for (i = 0; i < e_next; i++) { - h_key = DUK_HOBJECT_E_GET_KEY(thr->heap, h_varmap, i); - if (!h_key) { - continue; - } - - DUK_ASSERT(!DUK_HOBJECT_E_SLOT_IS_ACCESSOR(thr->heap, h_varmap, i)); - - /* The entries can either be register numbers or 'null' values. - * Thus, no need to DECREF them and get side effects. DECREF'ing - * the keys (strings) can cause memory to be freed but no side - * effects as strings don't have finalizers. This is why we can - * rely on the object properties not changing from underneath us. - */ - - tv = DUK_HOBJECT_E_GET_VALUE_TVAL_PTR(thr->heap, h_varmap, i); - if (!DUK_TVAL_IS_NUMBER(tv)) { - DUK_ASSERT(!DUK_TVAL_IS_HEAP_ALLOCATED(tv)); - DUK_HOBJECT_E_SET_KEY(thr->heap, h_varmap, i, NULL); - DUK_HSTRING_DECREF(thr, h_key); - /* when key is NULL, value is garbage so no need to set */ - } else { - ret++; - } - } - - duk_compact(ctx, -1); - - return ret; -} - -/* convert duk_compiler_func into a function template, leaving the result - * on top of stack. - */ -/* XXX: awkward and bloated asm -- use faster internal accesses */ -DUK_LOCAL void duk__convert_to_func_template(duk_compiler_ctx *comp_ctx, duk_bool_t force_no_namebind) { - duk_compiler_func *func = &comp_ctx->curr_func; - duk_hthread *thr = comp_ctx->thr; - duk_context *ctx = (duk_context *) thr; - duk_hcompiledfunction *h_res; - duk_hbuffer_fixed *h_data; - duk_size_t consts_count; - duk_size_t funcs_count; - duk_size_t code_count; - duk_size_t code_size; - duk_size_t data_size; - duk_size_t i; - duk_tval *p_const; - duk_hobject **p_func; - duk_instr_t *p_instr; - duk_compiler_instr *q_instr; - duk_tval *tv; - - DUK_DDD(DUK_DDDPRINT("converting duk_compiler_func to function/template")); - - /* - * Push result object and init its flags - */ - - /* Valstack should suffice here, required on function valstack init */ - - (void) duk_push_compiledfunction(ctx); - h_res = (duk_hcompiledfunction *) DUK_GET_HOBJECT_NEGIDX(ctx, -1); /* XXX: specific getter */ - DUK_ASSERT(h_res != NULL); - - if (func->is_function) { - DUK_DDD(DUK_DDDPRINT("function -> set NEWENV")); - DUK_HOBJECT_SET_NEWENV((duk_hobject *) h_res); - - if (!func->is_arguments_shadowed) { - /* arguments object would be accessible; note that shadowing - * bindings are arguments or function declarations, neither - * of which are deletable, so this is safe. - */ - - if (func->id_access_arguments || func->may_direct_eval) { - DUK_DDD(DUK_DDDPRINT("function may access 'arguments' object directly or " - "indirectly -> set CREATEARGS")); - DUK_HOBJECT_SET_CREATEARGS((duk_hobject *) h_res); - } - } - } else if (func->is_eval && func->is_strict) { - DUK_DDD(DUK_DDDPRINT("strict eval code -> set NEWENV")); - DUK_HOBJECT_SET_NEWENV((duk_hobject *) h_res); - } else { - /* non-strict eval: env is caller's env or global env (direct vs. indirect call) - * global code: env is is global env - */ - DUK_DDD(DUK_DDDPRINT("non-strict eval code or global code -> no NEWENV")); - DUK_ASSERT(!DUK_HOBJECT_HAS_NEWENV((duk_hobject *) h_res)); - } - - if (func->is_function && !func->is_decl && func->h_name != NULL && !force_no_namebind) { - /* Object literal set/get functions have a name (property - * name) but must not have a lexical name binding, see - * test-bug-getset-func-name.js. - */ - DUK_DDD(DUK_DDDPRINT("function expression with a name -> set NAMEBINDING")); - DUK_HOBJECT_SET_NAMEBINDING((duk_hobject *) h_res); - } - - if (func->is_strict) { - DUK_DDD(DUK_DDDPRINT("function is strict -> set STRICT")); - DUK_HOBJECT_SET_STRICT((duk_hobject *) h_res); - } - - if (func->is_notail) { - DUK_DDD(DUK_DDDPRINT("function is notail -> set NOTAIL")); - DUK_HOBJECT_SET_NOTAIL((duk_hobject *) h_res); - } - - /* - * Build function fixed size 'data' buffer, which contains bytecode, - * constants, and inner function references. - * - * During the building phase 'data' is reachable but incomplete. - * Only incref's occur during building (no refzero or GC happens), - * so the building process is atomic. - */ - - consts_count = duk_hobject_get_length(thr, func->h_consts); - funcs_count = duk_hobject_get_length(thr, func->h_funcs) / 3; - code_count = DUK_BW_GET_SIZE(thr, &func->bw_code) / sizeof(duk_compiler_instr); - code_size = code_count * sizeof(duk_instr_t); - - data_size = consts_count * sizeof(duk_tval) + - funcs_count * sizeof(duk_hobject *) + - code_size; - - DUK_DDD(DUK_DDDPRINT("consts_count=%ld, funcs_count=%ld, code_size=%ld -> " - "data_size=%ld*%ld + %ld*%ld + %ld = %ld", - (long) consts_count, (long) funcs_count, (long) code_size, - (long) consts_count, (long) sizeof(duk_tval), - (long) funcs_count, (long) sizeof(duk_hobject *), - (long) code_size, (long) data_size)); - - duk_push_fixed_buffer(ctx, data_size); - h_data = (duk_hbuffer_fixed *) duk_get_hbuffer(ctx, -1); - DUK_ASSERT(h_data != NULL); - - DUK_HCOMPILEDFUNCTION_SET_DATA(thr->heap, h_res, (duk_hbuffer *) h_data); - DUK_HEAPHDR_INCREF(thr, h_data); - - p_const = (duk_tval *) (void *) DUK_HBUFFER_FIXED_GET_DATA_PTR(thr->heap, h_data); - for (i = 0; i < consts_count; i++) { - DUK_ASSERT(i <= DUK_UARRIDX_MAX); /* const limits */ - tv = duk_hobject_find_existing_array_entry_tval_ptr(thr->heap, func->h_consts, (duk_uarridx_t) i); - DUK_ASSERT(tv != NULL); - DUK_TVAL_SET_TVAL(p_const, tv); - p_const++; - DUK_TVAL_INCREF(thr, tv); /* may be a string constant */ - - DUK_DDD(DUK_DDDPRINT("constant: %!T", (duk_tval *) tv)); - } - - p_func = (duk_hobject **) p_const; - DUK_HCOMPILEDFUNCTION_SET_FUNCS(thr->heap, h_res, p_func); - for (i = 0; i < funcs_count; i++) { - duk_hobject *h; - DUK_ASSERT(i * 3 <= DUK_UARRIDX_MAX); /* func limits */ - tv = duk_hobject_find_existing_array_entry_tval_ptr(thr->heap, func->h_funcs, (duk_uarridx_t) (i * 3)); - DUK_ASSERT(tv != NULL); - DUK_ASSERT(DUK_TVAL_IS_OBJECT(tv)); - h = DUK_TVAL_GET_OBJECT(tv); - DUK_ASSERT(h != NULL); - DUK_ASSERT(DUK_HOBJECT_IS_COMPILEDFUNCTION(h)); - *p_func++ = h; - DUK_HOBJECT_INCREF(thr, h); - - DUK_DDD(DUK_DDDPRINT("inner function: %p -> %!iO", - (void *) h, (duk_heaphdr *) h)); - } - - p_instr = (duk_instr_t *) p_func; - DUK_HCOMPILEDFUNCTION_SET_BYTECODE(thr->heap, h_res, p_instr); - - /* copy bytecode instructions one at a time */ - q_instr = (duk_compiler_instr *) (void *) DUK_BW_GET_BASEPTR(thr, &func->bw_code); - for (i = 0; i < code_count; i++) { - p_instr[i] = q_instr[i].ins; - } - /* Note: 'q_instr' is still used below */ - - DUK_ASSERT((duk_uint8_t *) (p_instr + code_count) == DUK_HBUFFER_FIXED_GET_DATA_PTR(thr->heap, h_data) + data_size); - - duk_pop(ctx); /* 'data' (and everything in it) is reachable through h_res now */ - - /* - * Init object properties - * - * Properties should be added in decreasing order of access frequency. - * (Not very critical for function templates.) - */ - - DUK_DDD(DUK_DDDPRINT("init function properties")); - - /* [ ... res ] */ - - /* _Varmap: omitted if function is guaranteed not to do slow path identifier - * accesses or if it would turn out to be empty of actual register mappings - * after a cleanup. When debugging is enabled, we always need the varmap to - * be able to lookup variables at any point. - */ -#if defined(DUK_USE_DEBUGGER_SUPPORT) - if (1) { -#else - if (func->id_access_slow || /* directly uses slow accesses */ - func->may_direct_eval || /* may indirectly slow access through a direct eval */ - funcs_count > 0) { /* has inner functions which may slow access (XXX: this can be optimized by looking at the inner functions) */ -#endif - duk_int_t num_used; - duk_dup(ctx, func->varmap_idx); - num_used = duk__cleanup_varmap(comp_ctx); - DUK_DDD(DUK_DDDPRINT("cleaned up varmap: %!T (num_used=%ld)", - (duk_tval *) duk_get_tval(ctx, -1), (long) num_used)); - - if (num_used > 0) { - duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_INT_VARMAP, DUK_PROPDESC_FLAGS_NONE); - } else { - DUK_DDD(DUK_DDDPRINT("varmap is empty after cleanup -> no need to add")); - duk_pop(ctx); - } - } - - /* _Formals: omitted if function is guaranteed not to need a (non-strict) arguments object */ - if (1) { - /* XXX: Add a proper condition. If formals list is omitted, recheck - * handling for 'length' in duk_js_push_closure(); it currently relies - * on _Formals being set. Removal may need to be conditional to debugging - * being enabled/disabled too. - */ - duk_dup(ctx, func->argnames_idx); - duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_INT_FORMALS, DUK_PROPDESC_FLAGS_NONE); - } - - /* name */ - if (func->h_name) { - duk_push_hstring(ctx, func->h_name); - duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_NAME, DUK_PROPDESC_FLAGS_NONE); - } - - /* _Source */ -#if defined(DUK_USE_NONSTD_FUNC_SOURCE_PROPERTY) - if (0) { - /* XXX: Currently function source code is not stored, as it is not - * required by the standard. Source code should not be stored by - * default (user should enable it explicitly), and the source should - * probably be compressed with a trivial text compressor; average - * compression of 20-30% is quite easy to achieve even with a trivial - * compressor (RLE + backwards lookup). - * - * Debugging needs source code to be useful: sometimes input code is - * not found in files as it may be generated and then eval()'d, given - * by dynamic C code, etc. - * - * Other issues: - * - * - Need tokenizer indices for start and end to substring - * - Always normalize function declaration part? - * - If we keep _Formals, only need to store body - */ - - /* - * For global or eval code this is straightforward. For functions - * created with the Function constructor we only get the source for - * the body and must manufacture the "function ..." part. - * - * For instance, for constructed functions (v8): - * - * > a = new Function("foo", "bar", "print(foo)"); - * [Function] - * > a.toString() - * 'function anonymous(foo,bar) {\nprint(foo)\n}' - * - * Similarly for e.g. getters (v8): - * - * > x = { get a(foo,bar) { print(foo); } } - * { a: [Getter] } - * > Object.getOwnPropertyDescriptor(x, 'a').get.toString() - * 'function a(foo,bar) { print(foo); }' - */ - -#if 0 - duk_push_string(ctx, "XXX"); - duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_INT_SOURCE, DUK_PROPDESC_FLAGS_NONE); -#endif - } -#endif /* DUK_USE_NONSTD_FUNC_SOURCE_PROPERTY */ - - /* _Pc2line */ -#if defined(DUK_USE_PC2LINE) - if (1) { - /* - * Size-optimized pc->line mapping. - */ - - DUK_ASSERT(code_count <= DUK_COMPILER_MAX_BYTECODE_LENGTH); - duk_hobject_pc2line_pack(thr, q_instr, (duk_uint_fast32_t) code_count); /* -> pushes fixed buffer */ - duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_INT_PC2LINE, DUK_PROPDESC_FLAGS_NONE); - - /* XXX: if assertions enabled, walk through all valid PCs - * and check line mapping. - */ - } -#endif /* DUK_USE_PC2LINE */ - - /* fileName */ - if (comp_ctx->h_filename) { - /* - * Source filename (or equivalent), for identifying thrown errors. - */ - - duk_push_hstring(ctx, comp_ctx->h_filename); - duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_FILE_NAME, DUK_PROPDESC_FLAGS_NONE); - } - - /* - * Init remaining result fields - * - * 'nregs' controls how large a register frame is allocated. - * - * 'nargs' controls how many formal arguments are written to registers: - * r0, ... r(nargs-1). The remaining registers are initialized to - * undefined. - */ - - DUK_ASSERT(func->temp_max >= 0); - h_res->nregs = (duk_uint16_t) func->temp_max; - h_res->nargs = (duk_uint16_t) duk_hobject_get_length(thr, func->h_argnames); - DUK_ASSERT(h_res->nregs >= h_res->nargs); /* pass2 allocation handles this */ -#if defined(DUK_USE_DEBUGGER_SUPPORT) - h_res->start_line = (duk_uint32_t) func->min_line; - h_res->end_line = (duk_uint32_t) func->max_line; -#endif - - DUK_DD(DUK_DDPRINT("converted function: %!ixT", - (duk_tval *) duk_get_tval(ctx, -1))); - - /* - * Compact the function template. - */ - - duk_compact(ctx, -1); - - /* - * Debug dumping - */ - -#ifdef DUK_USE_DDDPRINT - { - duk_hcompiledfunction *h; - duk_instr_t *p, *p_start, *p_end; - - h = (duk_hcompiledfunction *) duk_get_hobject(ctx, -1); - p_start = (duk_instr_t *) DUK_HCOMPILEDFUNCTION_GET_CODE_BASE(thr->heap, h); - p_end = (duk_instr_t *) DUK_HCOMPILEDFUNCTION_GET_CODE_END(thr->heap, h); - - p = p_start; - while (p < p_end) { - DUK_DDD(DUK_DDDPRINT("BC %04ld: %!I ; 0x%08lx op=%ld (%!C) a=%ld b=%ld c=%ld", - (long) (p - p_start), - (duk_instr_t) (*p), - (unsigned long) (*p), - (long) DUK_DEC_OP(*p), - (long) DUK_DEC_OP(*p), - (long) DUK_DEC_A(*p), - (long) DUK_DEC_B(*p), - (long) DUK_DEC_C(*p))); - p++; - } - } -#endif -} - -/* - * Code emission helpers - * - * Some emission helpers understand the range of target and source reg/const - * values and automatically emit shuffling code if necessary. This is the - * case when the slot in question (A, B, C) is used in the standard way and - * for opcodes the emission helpers explicitly understand (like DUK_OP_CALL). - * - * The standard way is that: - * - slot A is a target register - * - slot B is a source register/constant - * - slot C is a source register/constant - * - * If a slot is used in a non-standard way the caller must indicate this - * somehow. If a slot is used as a target instead of a source (or vice - * versa), this can be indicated with a flag to trigger proper shuffling - * (e.g. DUK__EMIT_FLAG_B_IS_TARGET). If the value in the slot is not - * register/const related at all, the caller must ensure that the raw value - * fits into the corresponding slot so as to not trigger shuffling. The - * caller must set a "no shuffle" flag to ensure compilation fails if - * shuffling were to be triggered because of an internal error. - * - * For slots B and C the raw slot size is 9 bits but one bit is reserved for - * the reg/const indicator. To use the full 9-bit range for a raw value, - * shuffling must be disabled with the DUK__EMIT_FLAG_NO_SHUFFLE_{B,C} flag. - * Shuffling is only done for A, B, and C slots, not the larger BC or ABC slots. - * - * There is call handling specific understanding in the A-B-C emitter to - * convert call setup and call instructions into indirect ones if necessary. - */ - -/* Code emission flags, passed in the 'opcode' field. Opcode + flags - * fit into 16 bits for now, so use duk_small_uint.t. - */ -#define DUK__EMIT_FLAG_NO_SHUFFLE_A (1 << 8) -#define DUK__EMIT_FLAG_NO_SHUFFLE_B (1 << 9) -#define DUK__EMIT_FLAG_NO_SHUFFLE_C (1 << 10) -#define DUK__EMIT_FLAG_A_IS_SOURCE (1 << 11) /* slot A is a source (default: target) */ -#define DUK__EMIT_FLAG_B_IS_TARGET (1 << 12) /* slot B is a target (default: source) */ -#define DUK__EMIT_FLAG_C_IS_TARGET (1 << 13) /* slot C is a target (default: source) */ -#define DUK__EMIT_FLAG_B_IS_TARGETSOURCE (1 << 14) /* slot B is both a target and a source (used by extraops like DUK_EXTRAOP_INSTOF */ -#define DUK__EMIT_FLAG_RESERVE_JUMPSLOT (1 << 15) /* reserve a jumpslot after instr before target spilling, used for NEXTENUM */ - -/* XXX: clarify on when and where DUK__CONST_MARKER is allowed */ -/* XXX: opcode specific assertions on when consts are allowed */ - -/* XXX: macro smaller than call? */ -DUK_LOCAL duk_int_t duk__get_current_pc(duk_compiler_ctx *comp_ctx) { - duk_compiler_func *func; - func = &comp_ctx->curr_func; - return (duk_int_t) (DUK_BW_GET_SIZE(comp_ctx->thr, &func->bw_code) / sizeof(duk_compiler_instr)); -} - -DUK_LOCAL duk_compiler_instr *duk__get_instr_ptr(duk_compiler_ctx *comp_ctx, duk_int_t pc) { - DUK_ASSERT(pc >= 0); - DUK_ASSERT((duk_size_t) pc < (duk_size_t) (DUK_BW_GET_SIZE(comp_ctx->thr, &comp_ctx->curr_func.bw_code) / sizeof(duk_compiler_instr))); - return ((duk_compiler_instr *) (void *) DUK_BW_GET_BASEPTR(comp_ctx->thr, &comp_ctx->curr_func.bw_code)) + pc; -} - -/* emit instruction; could return PC but that's not needed in the majority - * of cases. - */ -DUK_LOCAL void duk__emit(duk_compiler_ctx *comp_ctx, duk_instr_t ins) { -#if defined(DUK_USE_PC2LINE) - duk_int_t line; -#endif - duk_compiler_instr *instr; - - DUK_DDD(DUK_DDDPRINT("duk__emit: 0x%08lx curr_token.start_line=%ld prev_token.start_line=%ld pc=%ld --> %!I", - (unsigned long) ins, - (long) comp_ctx->curr_token.start_line, - (long) comp_ctx->prev_token.start_line, - (long) duk__get_current_pc(comp_ctx), - (duk_instr_t) ins)); - - instr = (duk_compiler_instr *) (void *) DUK_BW_ENSURE_GETPTR(comp_ctx->thr, &comp_ctx->curr_func.bw_code, sizeof(duk_compiler_instr)); - DUK_BW_ADD_PTR(comp_ctx->thr, &comp_ctx->curr_func.bw_code, sizeof(duk_compiler_instr)); - -#if defined(DUK_USE_PC2LINE) - /* The line number tracking is a bit inconsistent right now, which - * affects debugger accuracy. Mostly call sites emit opcodes when - * they have parsed a token (say a terminating semicolon) and called - * duk__advance(). In this case the line number of the previous - * token is the most accurate one (except in prologue where - * prev_token.start_line is 0). This is probably not 100% correct - * right now. - */ - /* approximation, close enough */ - line = comp_ctx->prev_token.start_line; - if (line == 0) { - line = comp_ctx->curr_token.start_line; - } -#endif - - instr->ins = ins; -#if defined(DUK_USE_PC2LINE) - instr->line = line; -#endif -#if defined(DUK_USE_DEBUGGER_SUPPORT) - if (line < comp_ctx->curr_func.min_line) { - comp_ctx->curr_func.min_line = line; - } - if (line > comp_ctx->curr_func.max_line) { - comp_ctx->curr_func.max_line = line; - } -#endif - - /* Limit checks for bytecode byte size and line number. */ - if (DUK_UNLIKELY(DUK_BW_GET_SIZE(comp_ctx->thr, &comp_ctx->curr_func.bw_code) > DUK_USE_ESBC_MAX_BYTES)) { - goto fail_bc_limit; - } -#if defined(DUK_USE_PC2LINE) && defined(DUK_USE_ESBC_LIMITS) -#if defined(DUK_USE_BUFLEN16) - /* Buffer length is bounded to 0xffff automatically, avoid compile warning. */ - if (DUK_UNLIKELY(line > DUK_USE_ESBC_MAX_LINENUMBER)) { - goto fail_bc_limit; - } -#else - if (DUK_UNLIKELY(line > DUK_USE_ESBC_MAX_LINENUMBER)) { - goto fail_bc_limit; - } -#endif -#endif - - return; - - fail_bc_limit: - DUK_ERROR_RANGE(comp_ctx->thr, DUK_STR_BYTECODE_LIMIT); -} - -/* Update function min/max line from current token. Needed to improve - * function line range information for debugging, so that e.g. opening - * curly brace is covered by line range even when no opcodes are emitted - * for the line containing the brace. - */ -DUK_LOCAL void duk__update_lineinfo_currtoken(duk_compiler_ctx *comp_ctx) { -#if defined(DUK_USE_DEBUGGER_SUPPORT) - duk_int_t line; - - line = comp_ctx->curr_token.start_line; - if (line == 0) { - return; - } - if (line < comp_ctx->curr_func.min_line) { - comp_ctx->curr_func.min_line = line; - } - if (line > comp_ctx->curr_func.max_line) { - comp_ctx->curr_func.max_line = line; - } -#else - DUK_UNREF(comp_ctx); -#endif -} - -#if 0 /* unused */ -DUK_LOCAL void duk__emit_op_only(duk_compiler_ctx *comp_ctx, duk_small_uint_t op) { - duk__emit(comp_ctx, DUK_ENC_OP_ABC(op, 0)); -} -#endif - -/* Important main primitive. */ -DUK_LOCAL void duk__emit_a_b_c(duk_compiler_ctx *comp_ctx, duk_small_uint_t op_flags, duk_regconst_t a, duk_regconst_t b, duk_regconst_t c) { - duk_instr_t ins = 0; - duk_int_t a_out = -1; - duk_int_t b_out = -1; - duk_int_t c_out = -1; - duk_int_t tmp; - - DUK_DDD(DUK_DDDPRINT("emit: op_flags=%04lx, a=%ld, b=%ld, c=%ld", - (unsigned long) op_flags, (long) a, (long) b, (long) c)); - - /* We could rely on max temp/const checks: if they don't exceed BC - * limit, nothing here can either (just asserts would be enough). - * Currently we check for the limits, which provides additional - * protection against creating invalid bytecode due to compiler - * bugs. - */ - - DUK_ASSERT_DISABLE((op_flags & 0xff) >= DUK_BC_OP_MIN); /* unsigned */ - DUK_ASSERT((op_flags & 0xff) <= DUK_BC_OP_MAX); - - /* Input shuffling happens before the actual operation, while output - * shuffling happens afterwards. Output shuffling decisions are still - * made at the same time to reduce branch clutter; output shuffle decisions - * are recorded into X_out variables. - */ - - /* Slot A */ - -#if defined(DUK_USE_SHUFFLE_TORTURE) - if (a <= DUK_BC_A_MAX && (op_flags & DUK__EMIT_FLAG_NO_SHUFFLE_A)) { -#else - if (a <= DUK_BC_A_MAX) { -#endif - ; - } else if (op_flags & DUK__EMIT_FLAG_NO_SHUFFLE_A) { - DUK_D(DUK_DPRINT("out of regs: 'a' (reg) needs shuffling but shuffle prohibited, a: %ld", (long) a)); - goto error_outofregs; - } else if (a <= DUK_BC_BC_MAX) { - comp_ctx->curr_func.needs_shuffle = 1; - tmp = comp_ctx->curr_func.shuffle1; - if (op_flags & DUK__EMIT_FLAG_A_IS_SOURCE) { - duk__emit(comp_ctx, DUK_ENC_OP_A_BC(DUK_OP_LDREG, tmp, a)); - } else { - duk_small_int_t op = op_flags & 0xff; - if (op == DUK_OP_CSVAR || op == DUK_OP_CSREG || op == DUK_OP_CSPROP) { - /* Special handling for call setup instructions. The target - * is expressed indirectly, but there is no output shuffling. - */ - DUK_ASSERT((op_flags & DUK__EMIT_FLAG_A_IS_SOURCE) == 0); - duk__emit_load_int32_noshuffle(comp_ctx, tmp, a); - DUK_ASSERT(DUK_OP_CSVARI == DUK_OP_CSVAR + 1); - DUK_ASSERT(DUK_OP_CSREGI == DUK_OP_CSREG + 1); - DUK_ASSERT(DUK_OP_CSPROPI == DUK_OP_CSPROP + 1); - op_flags++; /* indirect opcode follows direct */ - } else { - /* Output shuffle needed after main operation */ - a_out = a; - } - } - a = tmp; - } else { - DUK_D(DUK_DPRINT("out of regs: 'a' (reg) needs shuffling but does not fit into BC, a: %ld", (long) a)); - goto error_outofregs; - } - - /* Slot B */ - - if (b & DUK__CONST_MARKER) { - DUK_ASSERT((op_flags & DUK__EMIT_FLAG_NO_SHUFFLE_B) == 0); - DUK_ASSERT((op_flags & DUK__EMIT_FLAG_B_IS_TARGET) == 0); - DUK_ASSERT((op_flags & 0xff) != DUK_OP_CALL); - DUK_ASSERT((op_flags & 0xff) != DUK_OP_NEW); - b = b & ~DUK__CONST_MARKER; -#if defined(DUK_USE_SHUFFLE_TORTURE) - if (0) { -#else - if (b <= 0xff) { -#endif - ins |= DUK_ENC_OP_A_B_C(0, 0, 0x100, 0); /* const flag for B */ - } else if (b <= DUK_BC_BC_MAX) { - comp_ctx->curr_func.needs_shuffle = 1; - tmp = comp_ctx->curr_func.shuffle2; - duk__emit(comp_ctx, DUK_ENC_OP_A_BC(DUK_OP_LDCONST, tmp, b)); - b = tmp; - } else { - DUK_D(DUK_DPRINT("out of regs: 'b' (const) needs shuffling but does not fit into BC, b: %ld", (long) b)); - goto error_outofregs; - } - } else { -#if defined(DUK_USE_SHUFFLE_TORTURE) - if (b <= 0xff && (op_flags & DUK__EMIT_FLAG_NO_SHUFFLE_B)) { -#else - if (b <= 0xff) { -#endif - ; - } else if (op_flags & DUK__EMIT_FLAG_NO_SHUFFLE_B) { - if (b > DUK_BC_B_MAX) { - /* Note: 0xff != DUK_BC_B_MAX */ - DUK_D(DUK_DPRINT("out of regs: 'b' (reg) needs shuffling but shuffle prohibited, b: %ld", (long) b)); - goto error_outofregs; - } - } else if (b <= DUK_BC_BC_MAX) { - comp_ctx->curr_func.needs_shuffle = 1; - tmp = comp_ctx->curr_func.shuffle2; - if (op_flags & DUK__EMIT_FLAG_B_IS_TARGET) { - /* Output shuffle needed after main operation */ - b_out = b; - } - if (!(op_flags & DUK__EMIT_FLAG_B_IS_TARGET) || (op_flags & DUK__EMIT_FLAG_B_IS_TARGETSOURCE)) { - duk_small_int_t op = op_flags & 0xff; - if (op == DUK_OP_CALL || op == DUK_OP_NEW || - op == DUK_OP_MPUTOBJ || op == DUK_OP_MPUTARR) { - /* Special handling for CALL/NEW/MPUTOBJ/MPUTARR shuffling. - * For each, slot B identifies the first register of a range - * of registers, so normal shuffling won't work. Instead, - * an indirect version of the opcode is used. - */ - DUK_ASSERT((op_flags & DUK__EMIT_FLAG_B_IS_TARGET) == 0); - duk__emit_load_int32_noshuffle(comp_ctx, tmp, b); - DUK_ASSERT(DUK_OP_CALLI == DUK_OP_CALL + 1); - DUK_ASSERT(DUK_OP_NEWI == DUK_OP_NEW + 1); - DUK_ASSERT(DUK_OP_MPUTOBJI == DUK_OP_MPUTOBJ + 1); - DUK_ASSERT(DUK_OP_MPUTARRI == DUK_OP_MPUTARR + 1); - op_flags++; /* indirect opcode follows direct */ - } else { - duk__emit(comp_ctx, DUK_ENC_OP_A_BC(DUK_OP_LDREG, tmp, b)); - } - } - b = tmp; - } else { - DUK_D(DUK_DPRINT("out of regs: 'b' (reg) needs shuffling but does not fit into BC, b: %ld", (long) b)); - goto error_outofregs; - } - } - - /* Slot C */ - - if (c & DUK__CONST_MARKER) { - DUK_ASSERT((op_flags & DUK__EMIT_FLAG_NO_SHUFFLE_C) == 0); - DUK_ASSERT((op_flags & DUK__EMIT_FLAG_C_IS_TARGET) == 0); - c = c & ~DUK__CONST_MARKER; -#if defined(DUK_USE_SHUFFLE_TORTURE) - if (0) { -#else - if (c <= 0xff) { -#endif - ins |= DUK_ENC_OP_A_B_C(0, 0, 0, 0x100); /* const flag for C */ - } else if (c <= DUK_BC_BC_MAX) { - comp_ctx->curr_func.needs_shuffle = 1; - tmp = comp_ctx->curr_func.shuffle3; - duk__emit(comp_ctx, DUK_ENC_OP_A_BC(DUK_OP_LDCONST, tmp, c)); - c = tmp; - } else { - DUK_D(DUK_DPRINT("out of regs: 'c' (const) needs shuffling but does not fit into BC, c: %ld", (long) c)); - goto error_outofregs; - } - } else { -#if defined(DUK_USE_SHUFFLE_TORTURE) - if (c <= 0xff && (op_flags & DUK__EMIT_FLAG_NO_SHUFFLE_C)) { -#else - if (c <= 0xff) { -#endif - ; - } else if (op_flags & DUK__EMIT_FLAG_NO_SHUFFLE_C) { - if (c > DUK_BC_C_MAX) { - /* Note: 0xff != DUK_BC_C_MAX */ - DUK_D(DUK_DPRINT("out of regs: 'c' (reg) needs shuffling but shuffle prohibited, c: %ld", (long) c)); - goto error_outofregs; - } - } else if (c <= DUK_BC_BC_MAX) { - comp_ctx->curr_func.needs_shuffle = 1; - tmp = comp_ctx->curr_func.shuffle3; - if (op_flags & DUK__EMIT_FLAG_C_IS_TARGET) { - /* Output shuffle needed after main operation */ - c_out = c; - } else { - duk_small_int_t op = op_flags & 0xff; - if (op == DUK_OP_EXTRA && - (a == DUK_EXTRAOP_INITGET || a == DUK_EXTRAOP_INITSET)) { - /* Special shuffling for INITGET/INITSET, where slot C - * identifies a register pair and cannot be shuffled - * normally. Use an indirect variant instead. - */ - DUK_ASSERT((op_flags & DUK__EMIT_FLAG_C_IS_TARGET) == 0); - duk__emit_load_int32_noshuffle(comp_ctx, tmp, c); - DUK_ASSERT(DUK_EXTRAOP_INITGETI == DUK_EXTRAOP_INITGET + 1); - DUK_ASSERT(DUK_EXTRAOP_INITSETI == DUK_EXTRAOP_INITSET + 1); - a++; /* indirect opcode follows direct */ - } else { - duk__emit(comp_ctx, DUK_ENC_OP_A_BC(DUK_OP_LDREG, tmp, c)); - } - } - c = tmp; - } else { - DUK_D(DUK_DPRINT("out of regs: 'c' (reg) needs shuffling but does not fit into BC, c: %ld", (long) c)); - goto error_outofregs; - } - } - - /* Main operation */ - - DUK_ASSERT_DISABLE(a >= DUK_BC_A_MIN); /* unsigned */ - DUK_ASSERT(a <= DUK_BC_A_MAX); - DUK_ASSERT_DISABLE(b >= DUK_BC_B_MIN); /* unsigned */ - DUK_ASSERT(b <= DUK_BC_B_MAX); - DUK_ASSERT_DISABLE(c >= DUK_BC_C_MIN); /* unsigned */ - DUK_ASSERT(c <= DUK_BC_C_MAX); - - ins |= DUK_ENC_OP_A_B_C(op_flags & 0xff, a, b, c); - duk__emit(comp_ctx, ins); - - /* NEXTENUM needs a jump slot right after the main instruction. - * When the JUMP is taken, output spilling is not needed so this - * workaround is possible. The jump slot PC is exceptionally - * plumbed through comp_ctx to minimize call sites. - */ - if (op_flags & DUK__EMIT_FLAG_RESERVE_JUMPSLOT) { - comp_ctx->emit_jumpslot_pc = duk__get_current_pc(comp_ctx); - duk__emit_abc(comp_ctx, DUK_OP_JUMP, 0); - } - - /* Output shuffling: only one output register is realistically possible. - * - * (Zero would normally be an OK marker value: if the target register - * was zero, it would never be shuffled. But with DUK_USE_SHUFFLE_TORTURE - * this is no longer true, so use -1 as a marker instead.) - */ - - if (a_out >= 0) { - DUK_ASSERT(b_out < 0); - DUK_ASSERT(c_out < 0); - duk__emit(comp_ctx, DUK_ENC_OP_A_BC(DUK_OP_STREG, a, a_out)); - } else if (b_out >= 0) { - DUK_ASSERT(a_out < 0); - DUK_ASSERT(c_out < 0); - duk__emit(comp_ctx, DUK_ENC_OP_A_BC(DUK_OP_STREG, b, b_out)); - } else if (c_out >= 0) { - DUK_ASSERT(b_out < 0); - DUK_ASSERT(c_out < 0); - duk__emit(comp_ctx, DUK_ENC_OP_A_BC(DUK_OP_STREG, c, c_out)); - } - - return; - - error_outofregs: - DUK_ERROR_RANGE(comp_ctx->thr, DUK_STR_REG_LIMIT); -} - -DUK_LOCAL void duk__emit_a_b(duk_compiler_ctx *comp_ctx, duk_small_uint_t op_flags, duk_regconst_t a, duk_regconst_t b) { - duk__emit_a_b_c(comp_ctx, op_flags | DUK__EMIT_FLAG_NO_SHUFFLE_C, a, b, 0); -} - -#if 0 /* unused */ -DUK_LOCAL void duk__emit_a(duk_compiler_ctx *comp_ctx, int op_flags, int a) { - duk__emit_a_b_c(comp_ctx, op_flags | DUK__EMIT_FLAG_NO_SHUFFLE_B | DUK__EMIT_FLAG_NO_SHUFFLE_C, a, 0, 0); -} -#endif - -DUK_LOCAL void duk__emit_a_bc(duk_compiler_ctx *comp_ctx, duk_small_uint_t op_flags, duk_regconst_t a, duk_regconst_t bc) { - duk_instr_t ins; - duk_int_t tmp; - - /* allow caller to give a const number with the DUK__CONST_MARKER */ - bc = bc & (~DUK__CONST_MARKER); - - DUK_ASSERT_DISABLE((op_flags & 0xff) >= DUK_BC_OP_MIN); /* unsigned */ - DUK_ASSERT((op_flags & 0xff) <= DUK_BC_OP_MAX); - DUK_ASSERT_DISABLE(bc >= DUK_BC_BC_MIN); /* unsigned */ - DUK_ASSERT(bc <= DUK_BC_BC_MAX); - DUK_ASSERT((bc & DUK__CONST_MARKER) == 0); - - if (bc <= DUK_BC_BC_MAX) { - ; - } else { - /* No BC shuffling now. */ - goto error_outofregs; - } - -#if defined(DUK_USE_SHUFFLE_TORTURE) - if (a <= DUK_BC_A_MAX && (op_flags & DUK__EMIT_FLAG_NO_SHUFFLE_A)) { -#else - if (a <= DUK_BC_A_MAX) { -#endif - ins = DUK_ENC_OP_A_BC(op_flags & 0xff, a, bc); - duk__emit(comp_ctx, ins); - } else if (op_flags & DUK__EMIT_FLAG_NO_SHUFFLE_A) { - goto error_outofregs; - } else if (a <= DUK_BC_BC_MAX) { - comp_ctx->curr_func.needs_shuffle = 1; - tmp = comp_ctx->curr_func.shuffle1; - ins = DUK_ENC_OP_A_BC(op_flags & 0xff, tmp, bc); - if (op_flags & DUK__EMIT_FLAG_A_IS_SOURCE) { - duk__emit(comp_ctx, DUK_ENC_OP_A_BC(DUK_OP_LDREG, tmp, a)); - duk__emit(comp_ctx, ins); - } else { - duk__emit(comp_ctx, ins); - duk__emit(comp_ctx, DUK_ENC_OP_A_BC(DUK_OP_STREG, tmp, a)); - } - } else { - goto error_outofregs; - } - return; - - error_outofregs: - DUK_ERROR_RANGE(comp_ctx->thr, DUK_STR_REG_LIMIT); -} - -DUK_LOCAL void duk__emit_abc(duk_compiler_ctx *comp_ctx, duk_small_uint_t op, duk_regconst_t abc) { - duk_instr_t ins; - - DUK_ASSERT_DISABLE(op >= DUK_BC_OP_MIN); /* unsigned */ - DUK_ASSERT(op <= DUK_BC_OP_MAX); - DUK_ASSERT_DISABLE(abc >= DUK_BC_ABC_MIN); /* unsigned */ - DUK_ASSERT(abc <= DUK_BC_ABC_MAX); - DUK_ASSERT((abc & DUK__CONST_MARKER) == 0); - - if (abc <= DUK_BC_ABC_MAX) { - ; - } else { - goto error_outofregs; - } - ins = DUK_ENC_OP_ABC(op, abc); - DUK_DDD(DUK_DDDPRINT("duk__emit_abc: 0x%08lx line=%ld pc=%ld op=%ld (%!C) abc=%ld (%!I)", - (unsigned long) ins, (long) comp_ctx->curr_token.start_line, - (long) duk__get_current_pc(comp_ctx), (long) op, (long) op, - (long) abc, (duk_instr_t) ins)); - duk__emit(comp_ctx, ins); - return; - - error_outofregs: - DUK_ERROR_RANGE(comp_ctx->thr, DUK_STR_REG_LIMIT); -} - -DUK_LOCAL void duk__emit_extraop_b_c(duk_compiler_ctx *comp_ctx, duk_small_uint_t extraop_flags, duk_regconst_t b, duk_regconst_t c) { - DUK_ASSERT_DISABLE((extraop_flags & 0xff) >= DUK_BC_EXTRAOP_MIN); /* unsigned */ - DUK_ASSERT((extraop_flags & 0xff) <= DUK_BC_EXTRAOP_MAX); - /* Setting "no shuffle A" is covered by the assert, but it's needed - * with DUK_USE_SHUFFLE_TORTURE. - */ - duk__emit_a_b_c(comp_ctx, - DUK_OP_EXTRA | DUK__EMIT_FLAG_NO_SHUFFLE_A | (extraop_flags & ~0xff), /* transfer flags */ - extraop_flags & 0xff, - b, - c); -} - -DUK_LOCAL void duk__emit_extraop_b(duk_compiler_ctx *comp_ctx, duk_small_uint_t extraop_flags, duk_regconst_t b) { - DUK_ASSERT_DISABLE((extraop_flags & 0xff) >= DUK_BC_EXTRAOP_MIN); /* unsigned */ - DUK_ASSERT((extraop_flags & 0xff) <= DUK_BC_EXTRAOP_MAX); - /* Setting "no shuffle A" is covered by the assert, but it's needed - * with DUK_USE_SHUFFLE_TORTURE. - */ - duk__emit_a_b_c(comp_ctx, - DUK_OP_EXTRA | DUK__EMIT_FLAG_NO_SHUFFLE_A | (extraop_flags & ~0xff), /* transfer flags */ - extraop_flags & 0xff, - b, - 0); -} - -DUK_LOCAL void duk__emit_extraop_bc(duk_compiler_ctx *comp_ctx, duk_small_uint_t extraop, duk_regconst_t bc) { - DUK_ASSERT_DISABLE(extraop >= DUK_BC_EXTRAOP_MIN); /* unsigned */ - DUK_ASSERT(extraop <= DUK_BC_EXTRAOP_MAX); - /* Setting "no shuffle A" is covered by the assert, but it's needed - * with DUK_USE_SHUFFLE_TORTURE. - */ - duk__emit_a_bc(comp_ctx, - DUK_OP_EXTRA | DUK__EMIT_FLAG_NO_SHUFFLE_A, - extraop, - bc); -} - -DUK_LOCAL void duk__emit_extraop_only(duk_compiler_ctx *comp_ctx, duk_small_uint_t extraop_flags) { - DUK_ASSERT_DISABLE((extraop_flags & 0xff) >= DUK_BC_EXTRAOP_MIN); /* unsigned */ - DUK_ASSERT((extraop_flags & 0xff) <= DUK_BC_EXTRAOP_MAX); - /* Setting "no shuffle A" is covered by the assert, but it's needed - * with DUK_USE_SHUFFLE_TORTURE. - */ - duk__emit_a_b_c(comp_ctx, - DUK_OP_EXTRA | DUK__EMIT_FLAG_NO_SHUFFLE_A | DUK__EMIT_FLAG_NO_SHUFFLE_B | - DUK__EMIT_FLAG_NO_SHUFFLE_C | (extraop_flags & ~0xff), /* transfer flags */ - extraop_flags & 0xff, - 0, - 0); -} - -DUK_LOCAL void duk__emit_load_int32_raw(duk_compiler_ctx *comp_ctx, duk_reg_t reg, duk_int32_t val, duk_small_uint_t op_flags) { - /* XXX: Shuffling support could be implemented here so that LDINT+LDINTX - * would only shuffle once (instead of twice). The current code works - * though, and has a smaller compiler footprint. - */ - - if ((val >= (duk_int32_t) DUK_BC_BC_MIN - (duk_int32_t) DUK_BC_LDINT_BIAS) && - (val <= (duk_int32_t) DUK_BC_BC_MAX - (duk_int32_t) DUK_BC_LDINT_BIAS)) { - DUK_DDD(DUK_DDDPRINT("emit LDINT to reg %ld for %ld", (long) reg, (long) val)); - duk__emit_a_bc(comp_ctx, DUK_OP_LDINT | op_flags, reg, (duk_regconst_t) (val + (duk_int32_t) DUK_BC_LDINT_BIAS)); - } else { - duk_int32_t hi = val >> DUK_BC_LDINTX_SHIFT; - duk_int32_t lo = val & ((((duk_int32_t) 1) << DUK_BC_LDINTX_SHIFT) - 1); - DUK_ASSERT(lo >= 0); - DUK_DDD(DUK_DDDPRINT("emit LDINT+LDINTX to reg %ld for %ld -> hi %ld, lo %ld", - (long) reg, (long) val, (long) hi, (long) lo)); - duk__emit_a_bc(comp_ctx, DUK_OP_LDINT | op_flags, reg, (duk_regconst_t) (hi + (duk_int32_t) DUK_BC_LDINT_BIAS)); - duk__emit_a_bc(comp_ctx, DUK_OP_LDINTX | op_flags, reg, (duk_regconst_t) lo); - } -} - -DUK_LOCAL void duk__emit_load_int32(duk_compiler_ctx *comp_ctx, duk_reg_t reg, duk_int32_t val) { - duk__emit_load_int32_raw(comp_ctx, reg, val, 0 /*op_flags*/); -} - -#if defined(DUK_USE_SHUFFLE_TORTURE) -/* Used by duk__emit*() calls so that we don't shuffle the loadints that - * are needed to handle indirect opcodes. - */ -DUK_LOCAL void duk__emit_load_int32_noshuffle(duk_compiler_ctx *comp_ctx, duk_reg_t reg, duk_int32_t val) { - duk__emit_load_int32_raw(comp_ctx, reg, val, DUK__EMIT_FLAG_NO_SHUFFLE_A /*op_flags*/); -} -#else -DUK_LOCAL void duk__emit_load_int32_noshuffle(duk_compiler_ctx *comp_ctx, duk_reg_t reg, duk_int32_t val) { - /* When torture not enabled, can just use the same helper because - * 'reg' won't get spilled. - */ - DUK_ASSERT(reg <= DUK_BC_A_MAX); - duk__emit_load_int32(comp_ctx, reg, val); -} -#endif - -DUK_LOCAL void duk__emit_jump(duk_compiler_ctx *comp_ctx, duk_int_t target_pc) { - duk_int_t curr_pc; - duk_int_t offset; - - curr_pc = (duk_int_t) (DUK_BW_GET_SIZE(comp_ctx->thr, &comp_ctx->curr_func.bw_code) / sizeof(duk_compiler_instr)); - offset = (duk_int_t) target_pc - (duk_int_t) curr_pc - 1; - DUK_ASSERT(offset + DUK_BC_JUMP_BIAS >= DUK_BC_ABC_MIN); - DUK_ASSERT(offset + DUK_BC_JUMP_BIAS <= DUK_BC_ABC_MAX); - duk__emit_abc(comp_ctx, DUK_OP_JUMP, (duk_regconst_t) (offset + DUK_BC_JUMP_BIAS)); -} - -DUK_LOCAL duk_int_t duk__emit_jump_empty(duk_compiler_ctx *comp_ctx) { - duk_int_t ret; - - ret = duk__get_current_pc(comp_ctx); /* useful for patching jumps later */ - duk__emit_abc(comp_ctx, DUK_OP_JUMP, 0); - return ret; -} - -/* Insert an empty jump in the middle of code emitted earlier. This is - * currently needed for compiling for-in. - */ -DUK_LOCAL void duk__insert_jump_entry(duk_compiler_ctx *comp_ctx, duk_int_t jump_pc) { -#if defined(DUK_USE_PC2LINE) - duk_int_t line; -#endif - duk_compiler_instr *instr; - duk_size_t offset; - - offset = jump_pc * sizeof(duk_compiler_instr), - instr = (duk_compiler_instr *) (void *) - DUK_BW_INSERT_ENSURE_AREA(comp_ctx->thr, - &comp_ctx->curr_func.bw_code, - offset, - sizeof(duk_compiler_instr)); - -#if defined(DUK_USE_PC2LINE) - line = comp_ctx->curr_token.start_line; /* approximation, close enough */ -#endif - instr->ins = DUK_ENC_OP_ABC(DUK_OP_JUMP, 0); -#if defined(DUK_USE_PC2LINE) - instr->line = line; -#endif - - DUK_BW_ADD_PTR(comp_ctx->thr, &comp_ctx->curr_func.bw_code, sizeof(duk_compiler_instr)); - if (DUK_UNLIKELY(DUK_BW_GET_SIZE(comp_ctx->thr, &comp_ctx->curr_func.bw_code) > DUK_USE_ESBC_MAX_BYTES)) { - goto fail_bc_limit; - } - return; - - fail_bc_limit: - DUK_ERROR_RANGE(comp_ctx->thr, DUK_STR_BYTECODE_LIMIT); -} - -/* Does not assume that jump_pc contains a DUK_OP_JUMP previously; this is intentional - * to allow e.g. an INVALID opcode be overwritten with a JUMP (label management uses this). - */ -DUK_LOCAL void duk__patch_jump(duk_compiler_ctx *comp_ctx, duk_int_t jump_pc, duk_int_t target_pc) { - duk_compiler_instr *instr; - duk_int_t offset; - - /* allow negative PCs, behave as a no-op */ - if (jump_pc < 0) { - DUK_DDD(DUK_DDDPRINT("duk__patch_jump(): nop call, jump_pc=%ld (<0), target_pc=%ld", - (long) jump_pc, (long) target_pc)); - return; - } - DUK_ASSERT(jump_pc >= 0); - - /* XXX: range assert */ - instr = duk__get_instr_ptr(comp_ctx, jump_pc); - DUK_ASSERT(instr != NULL); - - /* XXX: range assert */ - offset = target_pc - jump_pc - 1; - - instr->ins = DUK_ENC_OP_ABC(DUK_OP_JUMP, offset + DUK_BC_JUMP_BIAS); - DUK_DDD(DUK_DDDPRINT("duk__patch_jump(): jump_pc=%ld, target_pc=%ld, offset=%ld", - (long) jump_pc, (long) target_pc, (long) offset)); -} - -DUK_LOCAL void duk__patch_jump_here(duk_compiler_ctx *comp_ctx, duk_int_t jump_pc) { - duk__patch_jump(comp_ctx, jump_pc, duk__get_current_pc(comp_ctx)); -} - -DUK_LOCAL void duk__patch_trycatch(duk_compiler_ctx *comp_ctx, duk_int_t ldconst_pc, duk_int_t trycatch_pc, duk_regconst_t reg_catch, duk_regconst_t const_varname, duk_small_uint_t flags) { - duk_compiler_instr *instr; - - DUK_ASSERT((reg_catch & DUK__CONST_MARKER) == 0); - - instr = duk__get_instr_ptr(comp_ctx, ldconst_pc); - DUK_ASSERT(DUK_DEC_OP(instr->ins) == DUK_OP_LDCONST); - DUK_ASSERT(instr != NULL); - if (const_varname & DUK__CONST_MARKER) { - /* Have a catch variable. */ - const_varname = const_varname & (~DUK__CONST_MARKER); - if (reg_catch > DUK_BC_BC_MAX || const_varname > DUK_BC_BC_MAX) { - /* Catch attempts to use out-of-range reg/const. Without this - * check Duktape 0.12.0 could generate invalid code which caused - * an assert failure on execution. This error is triggered e.g. - * for functions with a lot of constants and a try-catch statement. - * Shuffling or opcode semantics change is needed to fix the issue. - * See: test-bug-trycatch-many-constants.js. - */ - DUK_D(DUK_DPRINT("failed to patch trycatch: flags=%ld, reg_catch=%ld, const_varname=%ld (0x%08lx)", - (long) flags, (long) reg_catch, (long) const_varname, (long) const_varname)); - DUK_ERROR_RANGE(comp_ctx->thr, DUK_STR_REG_LIMIT); - } - instr->ins |= DUK_ENC_OP_A_BC(0, 0, const_varname); - } else { - /* No catch variable, e.g. a try-finally; replace LDCONST with - * NOP to avoid a bogus LDCONST. - */ - instr->ins = DUK_ENC_OP_A(DUK_OP_EXTRA, DUK_EXTRAOP_NOP); - } - - instr = duk__get_instr_ptr(comp_ctx, trycatch_pc); - DUK_ASSERT(instr != NULL); - DUK_ASSERT_DISABLE(flags >= DUK_BC_A_MIN); - DUK_ASSERT(flags <= DUK_BC_A_MAX); - instr->ins = DUK_ENC_OP_A_BC(DUK_OP_TRYCATCH, flags, reg_catch); -} - -DUK_LOCAL void duk__emit_if_false_skip(duk_compiler_ctx *comp_ctx, duk_regconst_t regconst) { - duk__emit_a_b_c(comp_ctx, - DUK_OP_IF | DUK__EMIT_FLAG_NO_SHUFFLE_A | DUK__EMIT_FLAG_NO_SHUFFLE_C, - 0 /*false*/, - regconst, - 0 /*unused*/); -} - -DUK_LOCAL void duk__emit_if_true_skip(duk_compiler_ctx *comp_ctx, duk_regconst_t regconst) { - duk__emit_a_b_c(comp_ctx, - DUK_OP_IF | DUK__EMIT_FLAG_NO_SHUFFLE_A | DUK__EMIT_FLAG_NO_SHUFFLE_C, - 1 /*true*/, - regconst, - 0 /*unused*/); -} - -DUK_LOCAL void duk__emit_invalid(duk_compiler_ctx *comp_ctx) { - duk__emit_extraop_bc(comp_ctx, DUK_EXTRAOP_INVALID, 0); -} - -/* - * Peephole optimizer for finished bytecode. - * - * Does not remove opcodes; currently only straightens out unconditional - * jump chains which are generated by several control structures. - */ - -DUK_LOCAL void duk__peephole_optimize_bytecode(duk_compiler_ctx *comp_ctx) { - duk_compiler_instr *bc; - duk_small_uint_t iter; - duk_int_t i, n; - duk_int_t count_opt; - - bc = (duk_compiler_instr *) (void *) DUK_BW_GET_BASEPTR(comp_ctx->thr, &comp_ctx->curr_func.bw_code); -#if defined(DUK_USE_BUFLEN16) - /* No need to assert, buffer size maximum is 0xffff. */ -#else - DUK_ASSERT((duk_size_t) DUK_BW_GET_SIZE(comp_ctx->thr, &comp_ctx->curr_func.bw_code) / sizeof(duk_compiler_instr) <= (duk_size_t) DUK_INT_MAX); /* bytecode limits */ -#endif - n = (duk_int_t) (DUK_BW_GET_SIZE(comp_ctx->thr, &comp_ctx->curr_func.bw_code) / sizeof(duk_compiler_instr)); - - for (iter = 0; iter < DUK_COMPILER_PEEPHOLE_MAXITER; iter++) { - count_opt = 0; - - for (i = 0; i < n; i++) { - duk_instr_t ins; - duk_int_t target_pc1; - duk_int_t target_pc2; - - ins = bc[i].ins; - if (DUK_DEC_OP(ins) != DUK_OP_JUMP) { - continue; - } - - target_pc1 = i + 1 + DUK_DEC_ABC(ins) - DUK_BC_JUMP_BIAS; - DUK_DDD(DUK_DDDPRINT("consider jump at pc %ld; target_pc=%ld", (long) i, (long) target_pc1)); - DUK_ASSERT(target_pc1 >= 0); - DUK_ASSERT(target_pc1 < n); - - /* Note: if target_pc1 == i, we'll optimize a jump to itself. - * This does not need to be checked for explicitly; the case - * is rare and max iter breaks us out. - */ - - ins = bc[target_pc1].ins; - if (DUK_DEC_OP(ins) != DUK_OP_JUMP) { - continue; - } - - target_pc2 = target_pc1 + 1 + DUK_DEC_ABC(ins) - DUK_BC_JUMP_BIAS; - - DUK_DDD(DUK_DDDPRINT("optimizing jump at pc %ld; old target is %ld -> new target is %ld", - (long) i, (long) target_pc1, (long) target_pc2)); - - bc[i].ins = DUK_ENC_OP_ABC(DUK_OP_JUMP, target_pc2 - (i + 1) + DUK_BC_JUMP_BIAS); - - count_opt++; - } - - DUK_DD(DUK_DDPRINT("optimized %ld jumps on peephole round %ld", (long) count_opt, (long) (iter + 1))); - - if (count_opt == 0) { - break; - } - } -} - -/* - * Intermediate value helpers - */ - -#define DUK__ISREG(comp_ctx,x) (((x) & DUK__CONST_MARKER) == 0) -#define DUK__ISCONST(comp_ctx,x) (((x) & DUK__CONST_MARKER) != 0) -#define DUK__ISTEMP(comp_ctx,x) (DUK__ISREG((comp_ctx), (x)) && (duk_regconst_t) (x) >= (duk_regconst_t) ((comp_ctx)->curr_func.temp_first)) -#define DUK__GETTEMP(comp_ctx) ((comp_ctx)->curr_func.temp_next) -#define DUK__SETTEMP(comp_ctx,x) ((comp_ctx)->curr_func.temp_next = (x)) /* dangerous: must only lower (temp_max not updated) */ -#define DUK__SETTEMP_CHECKMAX(comp_ctx,x) duk__settemp_checkmax((comp_ctx),(x)) -#define DUK__ALLOCTEMP(comp_ctx) duk__alloctemp((comp_ctx)) -#define DUK__ALLOCTEMPS(comp_ctx,count) duk__alloctemps((comp_ctx),(count)) - -/* Flags for intermediate value coercions. A flag for using a forced reg - * is not needed, the forced_reg argument suffices and generates better - * code (it is checked as it is used). - */ -#define DUK__IVAL_FLAG_ALLOW_CONST (1 << 0) /* allow a constant to be returned */ -#define DUK__IVAL_FLAG_REQUIRE_TEMP (1 << 1) /* require a (mutable) temporary as a result (or a const if allowed) */ -#define DUK__IVAL_FLAG_REQUIRE_SHORT (1 << 2) /* require a short (8-bit) reg/const which fits into bytecode B/C slot */ - -/* XXX: some code might benefit from DUK__SETTEMP_IFTEMP(ctx,x) */ - -#if 0 /* enable manually for dumping */ -#define DUK__DUMP_ISPEC(compctx,ispec) do { duk__dump_ispec((compctx), (ispec)); } while (0) -#define DUK__DUMP_IVALUE(compctx,ivalue) do { duk__dump_ivalue((compctx), (ivalue)); } while (0) - -DUK_LOCAL void duk__dump_ispec(duk_compiler_ctx *comp_ctx, duk_ispec *x) { - DUK_D(DUK_DPRINT("ispec dump: t=%ld regconst=0x%08lx, valstack_idx=%ld, value=%!T", - (long) x->t, (unsigned long) x->regconst, (long) x->valstack_idx, - duk_get_tval((duk_context *) comp_ctx->thr, x->valstack_idx))); -} -DUK_LOCAL void duk__dump_ivalue(duk_compiler_ctx *comp_ctx, duk_ivalue *x) { - DUK_D(DUK_DPRINT("ivalue dump: t=%ld op=%ld " - "x1={t=%ld regconst=0x%08lx valstack_idx=%ld value=%!T} " - "x2={t=%ld regconst=0x%08lx valstack_idx=%ld value=%!T}", - (long) x->t, (long) x->op, - (long) x->x1.t, (unsigned long) x->x1.regconst, (long) x->x1.valstack_idx, - duk_get_tval((duk_context *) comp_ctx->thr, x->x1.valstack_idx), - (long) x->x2.t, (unsigned long) x->x2.regconst, (long) x->x2.valstack_idx, - duk_get_tval((duk_context *) comp_ctx->thr, x->x2.valstack_idx))); -} -#else -#define DUK__DUMP_ISPEC(comp_ctx,x) do {} while (0) -#define DUK__DUMP_IVALUE(comp_ctx,x) do {} while (0) -#endif - -DUK_LOCAL void duk__copy_ispec(duk_compiler_ctx *comp_ctx, duk_ispec *src, duk_ispec *dst) { - duk_context *ctx = (duk_context *) comp_ctx->thr; - - dst->t = src->t; - dst->regconst = src->regconst; - duk_copy(ctx, src->valstack_idx, dst->valstack_idx); -} - -DUK_LOCAL void duk__copy_ivalue(duk_compiler_ctx *comp_ctx, duk_ivalue *src, duk_ivalue *dst) { - duk_context *ctx = (duk_context *) comp_ctx->thr; - - dst->t = src->t; - dst->op = src->op; - dst->x1.t = src->x1.t; - dst->x1.regconst = src->x1.regconst; - dst->x2.t = src->x2.t; - dst->x2.regconst = src->x2.regconst; - duk_copy(ctx, src->x1.valstack_idx, dst->x1.valstack_idx); - duk_copy(ctx, src->x2.valstack_idx, dst->x2.valstack_idx); -} - -/* XXX: to util */ -DUK_LOCAL duk_bool_t duk__is_whole_get_int32(duk_double_t x, duk_int32_t *ival) { - duk_small_int_t c; - duk_int32_t t; - - c = DUK_FPCLASSIFY(x); - if (c == DUK_FP_NORMAL || (c == DUK_FP_ZERO && !DUK_SIGNBIT(x))) { - /* Don't allow negative zero as it will cause trouble with - * LDINT+LDINTX. But positive zero is OK. - */ - t = (duk_int32_t) x; - if ((duk_double_t) t == x) { - *ival = t; - return 1; - } - } - - return 0; -} - -DUK_LOCAL duk_reg_t duk__alloctemps(duk_compiler_ctx *comp_ctx, duk_small_int_t num) { - duk_reg_t res; - - res = comp_ctx->curr_func.temp_next; - comp_ctx->curr_func.temp_next += num; - - if (comp_ctx->curr_func.temp_next > DUK__MAX_TEMPS) { /* == DUK__MAX_TEMPS is OK */ - DUK_ERROR_RANGE(comp_ctx->thr, DUK_STR_TEMP_LIMIT); - } - - /* maintain highest 'used' temporary, needed to figure out nregs of function */ - if (comp_ctx->curr_func.temp_next > comp_ctx->curr_func.temp_max) { - comp_ctx->curr_func.temp_max = comp_ctx->curr_func.temp_next; - } - - return res; -} - -DUK_LOCAL duk_reg_t duk__alloctemp(duk_compiler_ctx *comp_ctx) { - return duk__alloctemps(comp_ctx, 1); -} - -DUK_LOCAL void duk__settemp_checkmax(duk_compiler_ctx *comp_ctx, duk_reg_t temp_next) { - comp_ctx->curr_func.temp_next = temp_next; - if (temp_next > comp_ctx->curr_func.temp_max) { - comp_ctx->curr_func.temp_max = temp_next; - } -} - -/* get const for value at valstack top */ -DUK_LOCAL duk_regconst_t duk__getconst(duk_compiler_ctx *comp_ctx) { - duk_hthread *thr = comp_ctx->thr; - duk_context *ctx = (duk_context *) thr; - duk_compiler_func *f = &comp_ctx->curr_func; - duk_tval *tv1; - duk_int_t i, n, n_check; - - n = (duk_int_t) duk_get_length(ctx, f->consts_idx); - - tv1 = DUK_GET_TVAL_NEGIDX(ctx, -1); - DUK_ASSERT(tv1 != NULL); - -#if defined(DUK_USE_FASTINT) - /* Explicit check for fastint downgrade. */ - DUK_TVAL_CHKFAST_INPLACE(tv1); -#endif - - /* Sanity workaround for handling functions with a large number of - * constants at least somewhat reasonably. Otherwise checking whether - * we already have the constant would grow very slow (as it is O(N^2)). - */ - n_check = (n > DUK__GETCONST_MAX_CONSTS_CHECK ? DUK__GETCONST_MAX_CONSTS_CHECK : n); - for (i = 0; i < n_check; i++) { - duk_tval *tv2 = DUK_HOBJECT_A_GET_VALUE_PTR(thr->heap, f->h_consts, i); - - /* Strict equality is NOT enough, because we cannot use the same - * constant for e.g. +0 and -0. - */ - if (duk_js_samevalue(tv1, tv2)) { - DUK_DDD(DUK_DDDPRINT("reused existing constant for %!T -> const index %ld", - (duk_tval *) tv1, (long) i)); - duk_pop(ctx); - return (duk_regconst_t) (i | DUK__CONST_MARKER); - } - } - - if (n > DUK__MAX_CONSTS) { - DUK_ERROR_RANGE(comp_ctx->thr, DUK_STR_CONST_LIMIT); - } - - DUK_DDD(DUK_DDDPRINT("allocating new constant for %!T -> const index %ld", - (duk_tval *) tv1, (long) n)); - (void) duk_put_prop_index(ctx, f->consts_idx, n); /* invalidates tv1, tv2 */ - return (duk_regconst_t) (n | DUK__CONST_MARKER); -} - -/* Get the value represented by an duk_ispec to a register or constant. - * The caller can control the result by indicating whether or not: - * - * (1) a constant is allowed (sometimes the caller needs the result to - * be in a register) - * - * (2) a temporary register is required (usually when caller requires - * the register to be safely mutable; normally either a bound - * register or a temporary register are both OK) - * - * (3) a forced register target needs to be used - * - * Bytecode may be emitted to generate the necessary value. The return - * value is either a register or a constant. - */ - -DUK_LOCAL -duk_regconst_t duk__ispec_toregconst_raw(duk_compiler_ctx *comp_ctx, - duk_ispec *x, - duk_reg_t forced_reg, - duk_small_uint_t flags) { - duk_hthread *thr = comp_ctx->thr; - duk_context *ctx = (duk_context *) thr; - - DUK_DDD(DUK_DDDPRINT("duk__ispec_toregconst_raw(): x={%ld:%ld:%!T}, " - "forced_reg=%ld, flags 0x%08lx: allow_const=%ld require_temp=%ld require_short=%ld", - (long) x->t, - (long) x->regconst, - (duk_tval *) duk_get_tval(ctx, x->valstack_idx), - (long) forced_reg, - (unsigned long) flags, - (long) ((flags & DUK__IVAL_FLAG_ALLOW_CONST) ? 1 : 0), - (long) ((flags & DUK__IVAL_FLAG_REQUIRE_TEMP) ? 1 : 0), - (long) ((flags & DUK__IVAL_FLAG_REQUIRE_SHORT) ? 1 : 0))); - - switch (x->t) { - case DUK_ISPEC_VALUE: { - duk_tval *tv; - - tv = DUK_GET_TVAL_POSIDX(ctx, x->valstack_idx); - DUK_ASSERT(tv != NULL); - - switch (DUK_TVAL_GET_TAG(tv)) { - case DUK_TAG_UNDEFINED: { - /* Note: although there is no 'undefined' literal, undefined - * values can occur during compilation as a result of e.g. - * the 'void' operator. - */ - duk_reg_t dest = (forced_reg >= 0 ? forced_reg : DUK__ALLOCTEMP(comp_ctx)); - duk__emit_extraop_bc(comp_ctx, DUK_EXTRAOP_LDUNDEF, (duk_regconst_t) dest); - return (duk_regconst_t) dest; - } - case DUK_TAG_NULL: { - duk_reg_t dest = (forced_reg >= 0 ? forced_reg : DUK__ALLOCTEMP(comp_ctx)); - duk__emit_extraop_bc(comp_ctx, DUK_EXTRAOP_LDNULL, (duk_regconst_t) dest); - return (duk_regconst_t) dest; - } - case DUK_TAG_BOOLEAN: { - duk_reg_t dest = (forced_reg >= 0 ? forced_reg : DUK__ALLOCTEMP(comp_ctx)); - duk__emit_extraop_bc(comp_ctx, - (DUK_TVAL_GET_BOOLEAN(tv) ? DUK_EXTRAOP_LDTRUE : DUK_EXTRAOP_LDFALSE), - (duk_regconst_t) dest); - return (duk_regconst_t) dest; - } - case DUK_TAG_POINTER: { - DUK_UNREACHABLE(); - break; - } - case DUK_TAG_STRING: { - duk_hstring *h; - duk_reg_t dest; - duk_regconst_t constidx; - - h = DUK_TVAL_GET_STRING(tv); - DUK_UNREF(h); - DUK_ASSERT(h != NULL); - -#if 0 /* XXX: to be implemented? */ - /* Use special opcodes to load short strings */ - if (DUK_HSTRING_GET_BYTELEN(h) <= 2) { - /* Encode into a single opcode (18 bits can encode 1-2 bytes + length indicator) */ - } else if (DUK_HSTRING_GET_BYTELEN(h) <= 6) { - /* Encode into a double constant (53 bits can encode 6*8 = 48 bits + 3-bit length */ - } -#endif - duk_dup(ctx, x->valstack_idx); - constidx = duk__getconst(comp_ctx); - - if (flags & DUK__IVAL_FLAG_ALLOW_CONST) { - return constidx; - } - - dest = (forced_reg >= 0 ? forced_reg : DUK__ALLOCTEMP(comp_ctx)); - duk__emit_a_bc(comp_ctx, DUK_OP_LDCONST, (duk_regconst_t) dest, constidx); - return (duk_regconst_t) dest; - } - case DUK_TAG_OBJECT: { - DUK_UNREACHABLE(); - break; - } - case DUK_TAG_BUFFER: { - DUK_UNREACHABLE(); - break; - } - case DUK_TAG_LIGHTFUNC: { - DUK_UNREACHABLE(); - break; - } -#if defined(DUK_USE_FASTINT) - case DUK_TAG_FASTINT: -#endif - default: { - /* number */ - duk_reg_t dest; - duk_regconst_t constidx; - duk_double_t dval; - duk_int32_t ival; - - DUK_ASSERT(!DUK_TVAL_IS_UNUSED(tv)); - DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv)); - dval = DUK_TVAL_GET_NUMBER(tv); - - if (!(flags & DUK__IVAL_FLAG_ALLOW_CONST)) { - /* A number can be loaded either through a constant, using - * LDINT, or using LDINT+LDINTX. LDINT is always a size win, - * LDINT+LDINTX is not if the constant is used multiple times. - * Currently always prefer LDINT+LDINTX over a double constant. - */ - - if (duk__is_whole_get_int32(dval, &ival)) { - dest = (forced_reg >= 0 ? forced_reg : DUK__ALLOCTEMP(comp_ctx)); - duk__emit_load_int32(comp_ctx, dest, ival); - return (duk_regconst_t) dest; - } - } - - duk_dup(ctx, x->valstack_idx); - constidx = duk__getconst(comp_ctx); - - if (flags & DUK__IVAL_FLAG_ALLOW_CONST) { - return constidx; - } else { - dest = (forced_reg >= 0 ? forced_reg : DUK__ALLOCTEMP(comp_ctx)); - duk__emit_a_bc(comp_ctx, DUK_OP_LDCONST, (duk_regconst_t) dest, constidx); - return (duk_regconst_t) dest; - } - } - } /* end switch */ - } - case DUK_ISPEC_REGCONST: { - if (forced_reg >= 0) { - if (x->regconst & DUK__CONST_MARKER) { - duk__emit_a_bc(comp_ctx, DUK_OP_LDCONST, forced_reg, x->regconst); - } else if (x->regconst != (duk_regconst_t) forced_reg) { - duk__emit_a_bc(comp_ctx, DUK_OP_LDREG, forced_reg, x->regconst); - } else { - ; /* already in correct reg */ - } - return (duk_regconst_t) forced_reg; - } - - DUK_ASSERT(forced_reg < 0); - if (x->regconst & DUK__CONST_MARKER) { - if (!(flags & DUK__IVAL_FLAG_ALLOW_CONST)) { - duk_reg_t dest = DUK__ALLOCTEMP(comp_ctx); - duk__emit_a_bc(comp_ctx, DUK_OP_LDCONST, (duk_regconst_t) dest, x->regconst); - return (duk_regconst_t) dest; - } - return x->regconst; - } - - DUK_ASSERT(forced_reg < 0 && !(x->regconst & DUK__CONST_MARKER)); - if ((flags & DUK__IVAL_FLAG_REQUIRE_TEMP) && !DUK__ISTEMP(comp_ctx, x->regconst)) { - duk_reg_t dest = DUK__ALLOCTEMP(comp_ctx); - d
<TRUNCATED>