]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | /* |
2 | * Ecmascript compiler. | |
3 | * | |
4 | * Parses an input string and generates a function template result. | |
5 | * Compilation may happen in multiple contexts (global code, eval | |
6 | * code, function code). | |
7 | * | |
8 | * The parser uses a traditional top-down recursive parsing for the | |
9 | * statement level, and an operator precedence based top-down approach | |
10 | * for the expression level. The attempt is to minimize the C stack | |
11 | * depth. Bytecode is generated directly without an intermediate | |
12 | * representation (tree), at the cost of needing two passes over each | |
13 | * function. | |
14 | * | |
15 | * The top-down recursive parser functions are named "duk__parse_XXX". | |
16 | * | |
17 | * Recursion limits are in key functions to prevent arbitrary C recursion: | |
18 | * function body parsing, statement parsing, and expression parsing. | |
19 | * | |
20 | * See doc/compiler.rst for discussion on the design. | |
21 | * | |
22 | * A few typing notes: | |
23 | * | |
24 | * - duk_regconst_t: unsigned, no marker value for "none" | |
25 | * - duk_reg_t: signed, < 0 = none | |
26 | * - PC values: duk_int_t, negative values used as markers | |
27 | */ | |
28 | ||
29 | #include "duk_internal.h" | |
30 | ||
31 | /* if highest bit of a register number is set, it refers to a constant instead */ | |
32 | #define DUK__CONST_MARKER DUK_JS_CONST_MARKER | |
33 | ||
34 | /* for array and object literals */ | |
35 | #define DUK__MAX_ARRAY_INIT_VALUES 20 | |
36 | #define DUK__MAX_OBJECT_INIT_PAIRS 10 | |
37 | ||
38 | /* XXX: hack, remove when const lookup is not O(n) */ | |
39 | #define DUK__GETCONST_MAX_CONSTS_CHECK 256 | |
40 | ||
41 | /* These limits are based on bytecode limits. Max temps is limited | |
42 | * by duk_hcompiledfunction nargs/nregs fields being 16 bits. | |
43 | */ | |
44 | #define DUK__MAX_CONSTS DUK_BC_BC_MAX | |
45 | #define DUK__MAX_FUNCS DUK_BC_BC_MAX | |
46 | #define DUK__MAX_TEMPS 0xffffL | |
47 | ||
48 | /* Initial bytecode size allocation. */ | |
49 | #define DUK__BC_INITIAL_INSTS 256 | |
50 | ||
51 | #define DUK__RECURSION_INCREASE(comp_ctx,thr) do { \ | |
52 | DUK_DDD(DUK_DDDPRINT("RECURSION INCREASE: %s:%ld", (const char *) DUK_FILE_MACRO, (long) DUK_LINE_MACRO)); \ | |
53 | duk__recursion_increase((comp_ctx)); \ | |
54 | } while (0) | |
55 | ||
56 | #define DUK__RECURSION_DECREASE(comp_ctx,thr) do { \ | |
57 | DUK_DDD(DUK_DDDPRINT("RECURSION DECREASE: %s:%ld", (const char *) DUK_FILE_MACRO, (long) DUK_LINE_MACRO)); \ | |
58 | duk__recursion_decrease((comp_ctx)); \ | |
59 | } while (0) | |
60 | ||
61 | /* Value stack slot limits: these are quite approximate right now, and | |
62 | * because they overlap in control flow, some could be eliminated. | |
63 | */ | |
64 | #define DUK__COMPILE_ENTRY_SLOTS 8 | |
65 | #define DUK__FUNCTION_INIT_REQUIRE_SLOTS 16 | |
66 | #define DUK__FUNCTION_BODY_REQUIRE_SLOTS 16 | |
67 | #define DUK__PARSE_STATEMENTS_SLOTS 16 | |
68 | #define DUK__PARSE_EXPR_SLOTS 16 | |
69 | ||
70 | /* Temporary structure used to pass a stack allocated region through | |
71 | * duk_safe_call(). | |
72 | */ | |
73 | typedef struct { | |
74 | duk_small_uint_t flags; | |
75 | duk_compiler_ctx comp_ctx_alloc; | |
76 | duk_lexer_point lex_pt_alloc; | |
77 | } duk__compiler_stkstate; | |
78 | ||
79 | /* | |
80 | * Prototypes | |
81 | */ | |
82 | ||
83 | /* lexing */ | |
84 | DUK_LOCAL_DECL void duk__advance_helper(duk_compiler_ctx *comp_ctx, duk_small_int_t expect); | |
85 | DUK_LOCAL_DECL void duk__advance_expect(duk_compiler_ctx *comp_ctx, duk_small_int_t expect); | |
86 | DUK_LOCAL_DECL void duk__advance(duk_compiler_ctx *ctx); | |
87 | ||
88 | /* function helpers */ | |
89 | DUK_LOCAL_DECL void duk__init_func_valstack_slots(duk_compiler_ctx *comp_ctx); | |
90 | DUK_LOCAL_DECL void duk__reset_func_for_pass2(duk_compiler_ctx *comp_ctx); | |
91 | DUK_LOCAL_DECL void duk__init_varmap_and_prologue_for_pass2(duk_compiler_ctx *comp_ctx, duk_reg_t *out_stmt_value_reg); | |
92 | DUK_LOCAL_DECL void duk__convert_to_func_template(duk_compiler_ctx *comp_ctx, duk_bool_t force_no_namebind); | |
93 | DUK_LOCAL_DECL duk_int_t duk__cleanup_varmap(duk_compiler_ctx *comp_ctx); | |
94 | ||
95 | /* code emission */ | |
96 | DUK_LOCAL_DECL duk_int_t duk__get_current_pc(duk_compiler_ctx *comp_ctx); | |
97 | DUK_LOCAL_DECL duk_compiler_instr *duk__get_instr_ptr(duk_compiler_ctx *comp_ctx, duk_int_t pc); | |
98 | DUK_LOCAL_DECL void duk__emit(duk_compiler_ctx *comp_ctx, duk_instr_t ins); | |
99 | #if 0 /* unused */ | |
100 | DUK_LOCAL_DECL void duk__emit_op_only(duk_compiler_ctx *comp_ctx, duk_small_uint_t op); | |
101 | #endif | |
102 | 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); | |
103 | 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); | |
104 | #if 0 /* unused */ | |
105 | DUK_LOCAL_DECL void duk__emit_a(duk_compiler_ctx *comp_ctx, duk_small_uint_t op_flags, duk_regconst_t a); | |
106 | #endif | |
107 | 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); | |
108 | DUK_LOCAL_DECL void duk__emit_abc(duk_compiler_ctx *comp_ctx, duk_small_uint_t op, duk_regconst_t abc); | |
109 | 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); | |
110 | DUK_LOCAL_DECL void duk__emit_extraop_b(duk_compiler_ctx *comp_ctx, duk_small_uint_t extraop_flags, duk_regconst_t b); | |
111 | DUK_LOCAL_DECL void duk__emit_extraop_bc(duk_compiler_ctx *comp_ctx, duk_small_uint_t extraop, duk_regconst_t bc); | |
112 | DUK_LOCAL_DECL void duk__emit_extraop_only(duk_compiler_ctx *comp_ctx, duk_small_uint_t extraop_flags); | |
113 | DUK_LOCAL_DECL void duk__emit_load_int32(duk_compiler_ctx *comp_ctx, duk_reg_t reg, duk_int32_t val); | |
114 | DUK_LOCAL_DECL void duk__emit_load_int32_noshuffle(duk_compiler_ctx *comp_ctx, duk_reg_t reg, duk_int32_t val); | |
115 | DUK_LOCAL_DECL void duk__emit_jump(duk_compiler_ctx *comp_ctx, duk_int_t target_pc); | |
116 | DUK_LOCAL_DECL duk_int_t duk__emit_jump_empty(duk_compiler_ctx *comp_ctx); | |
117 | DUK_LOCAL_DECL void duk__insert_jump_entry(duk_compiler_ctx *comp_ctx, duk_int_t jump_pc); | |
118 | DUK_LOCAL_DECL void duk__patch_jump(duk_compiler_ctx *comp_ctx, duk_int_t jump_pc, duk_int_t target_pc); | |
119 | DUK_LOCAL_DECL void duk__patch_jump_here(duk_compiler_ctx *comp_ctx, duk_int_t jump_pc); | |
120 | 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); | |
121 | DUK_LOCAL_DECL void duk__emit_if_false_skip(duk_compiler_ctx *comp_ctx, duk_regconst_t regconst); | |
122 | DUK_LOCAL_DECL void duk__emit_if_true_skip(duk_compiler_ctx *comp_ctx, duk_regconst_t regconst); | |
123 | DUK_LOCAL_DECL void duk__emit_invalid(duk_compiler_ctx *comp_ctx); | |
124 | ||
125 | /* ivalue/ispec helpers */ | |
126 | DUK_LOCAL_DECL void duk__copy_ispec(duk_compiler_ctx *comp_ctx, duk_ispec *src, duk_ispec *dst); | |
127 | DUK_LOCAL_DECL void duk__copy_ivalue(duk_compiler_ctx *comp_ctx, duk_ivalue *src, duk_ivalue *dst); | |
128 | DUK_LOCAL_DECL duk_bool_t duk__is_whole_get_int32(duk_double_t x, duk_int32_t *ival); | |
129 | DUK_LOCAL_DECL duk_reg_t duk__alloctemps(duk_compiler_ctx *comp_ctx, duk_small_int_t num); | |
130 | DUK_LOCAL_DECL duk_reg_t duk__alloctemp(duk_compiler_ctx *comp_ctx); | |
131 | DUK_LOCAL_DECL void duk__settemp_checkmax(duk_compiler_ctx *comp_ctx, duk_reg_t temp_next); | |
132 | DUK_LOCAL_DECL duk_regconst_t duk__getconst(duk_compiler_ctx *comp_ctx); | |
133 | DUK_LOCAL_DECL | |
134 | duk_regconst_t duk__ispec_toregconst_raw(duk_compiler_ctx *comp_ctx, | |
135 | duk_ispec *x, | |
136 | duk_reg_t forced_reg, | |
137 | duk_small_uint_t flags); | |
138 | DUK_LOCAL_DECL void duk__ispec_toforcedreg(duk_compiler_ctx *comp_ctx, duk_ispec *x, duk_reg_t forced_reg); | |
139 | DUK_LOCAL_DECL void duk__ivalue_toplain_raw(duk_compiler_ctx *comp_ctx, duk_ivalue *x, duk_reg_t forced_reg); | |
140 | DUK_LOCAL_DECL void duk__ivalue_toplain(duk_compiler_ctx *comp_ctx, duk_ivalue *x); | |
141 | DUK_LOCAL_DECL void duk__ivalue_toplain_ignore(duk_compiler_ctx *comp_ctx, duk_ivalue *x); | |
142 | DUK_LOCAL_DECL | |
143 | duk_regconst_t duk__ivalue_toregconst_raw(duk_compiler_ctx *comp_ctx, | |
144 | duk_ivalue *x, | |
145 | duk_reg_t forced_reg, | |
146 | duk_small_uint_t flags); | |
147 | DUK_LOCAL_DECL duk_reg_t duk__ivalue_toreg(duk_compiler_ctx *comp_ctx, duk_ivalue *x); | |
148 | #if 0 /* unused */ | |
11fdf7f2 | 149 | DUK_LOCAL_DECL duk_reg_t duk__ivalue_totemp(duk_compiler_ctx *comp_ctx, duk_ivalue *x); |
7c673cae FG |
150 | #endif |
151 | DUK_LOCAL_DECL void duk__ivalue_toforcedreg(duk_compiler_ctx *comp_ctx, duk_ivalue *x, duk_int_t forced_reg); | |
152 | DUK_LOCAL_DECL duk_regconst_t duk__ivalue_toregconst(duk_compiler_ctx *comp_ctx, duk_ivalue *x); | |
11fdf7f2 | 153 | DUK_LOCAL_DECL duk_regconst_t duk__ivalue_totempconst(duk_compiler_ctx *comp_ctx, duk_ivalue *x); |
7c673cae FG |
154 | |
155 | /* identifier handling */ | |
156 | DUK_LOCAL_DECL duk_reg_t duk__lookup_active_register_binding(duk_compiler_ctx *comp_ctx); | |
157 | 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); | |
158 | ||
159 | /* label handling */ | |
160 | 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); | |
161 | DUK_LOCAL_DECL void duk__update_label_flags(duk_compiler_ctx *comp_ctx, duk_int_t label_id, duk_small_uint_t flags); | |
162 | 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); | |
163 | DUK_LOCAL_DECL void duk__reset_labels_to_length(duk_compiler_ctx *comp_ctx, duk_int_t len); | |
164 | ||
165 | /* top-down expression parser */ | |
166 | DUK_LOCAL_DECL void duk__expr_nud(duk_compiler_ctx *comp_ctx, duk_ivalue *res); | |
167 | DUK_LOCAL_DECL void duk__expr_led(duk_compiler_ctx *comp_ctx, duk_ivalue *left, duk_ivalue *res); | |
168 | DUK_LOCAL_DECL duk_small_uint_t duk__expr_lbp(duk_compiler_ctx *comp_ctx); | |
169 | DUK_LOCAL_DECL duk_bool_t duk__expr_is_empty(duk_compiler_ctx *comp_ctx); | |
170 | ||
171 | /* exprtop is the top level variant which resets nud/led counts */ | |
172 | DUK_LOCAL_DECL void duk__expr(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_small_uint_t rbp_flags); | |
173 | DUK_LOCAL_DECL void duk__exprtop(duk_compiler_ctx *ctx, duk_ivalue *res, duk_small_uint_t rbp_flags); | |
174 | ||
175 | /* convenience helpers */ | |
11fdf7f2 | 176 | #if 0 /* unused */ |
7c673cae | 177 | DUK_LOCAL_DECL duk_reg_t duk__expr_toreg(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_small_uint_t rbp_flags); |
11fdf7f2 | 178 | #endif |
7c673cae | 179 | #if 0 /* unused */ |
11fdf7f2 | 180 | DUK_LOCAL_DECL duk_reg_t duk__expr_totemp(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_small_uint_t rbp_flags); |
7c673cae FG |
181 | #endif |
182 | 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); | |
183 | DUK_LOCAL_DECL duk_regconst_t duk__expr_toregconst(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_small_uint_t rbp_flags); | |
11fdf7f2 TL |
184 | #if 0 /* unused */ |
185 | DUK_LOCAL_DECL duk_regconst_t duk__expr_totempconst(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_small_uint_t rbp_flags); | |
186 | #endif | |
7c673cae FG |
187 | DUK_LOCAL_DECL void duk__expr_toplain(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_small_uint_t rbp_flags); |
188 | DUK_LOCAL_DECL void duk__expr_toplain_ignore(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_small_uint_t rbp_flags); | |
189 | DUK_LOCAL_DECL duk_reg_t duk__exprtop_toreg(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_small_uint_t rbp_flags); | |
190 | #if 0 /* unused */ | |
11fdf7f2 | 191 | DUK_LOCAL_DECL duk_reg_t duk__exprtop_totemp(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_small_uint_t rbp_flags); |
7c673cae FG |
192 | #endif |
193 | 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); | |
194 | DUK_LOCAL_DECL duk_regconst_t duk__exprtop_toregconst(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_small_uint_t rbp_flags); | |
195 | #if 0 /* unused */ | |
196 | DUK_LOCAL_DECL void duk__exprtop_toplain_ignore(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_small_uint_t rbp_flags); | |
197 | #endif | |
198 | ||
199 | /* expression parsing helpers */ | |
200 | DUK_LOCAL_DECL duk_int_t duk__parse_arguments(duk_compiler_ctx *comp_ctx, duk_ivalue *res); | |
201 | DUK_LOCAL_DECL void duk__nud_array_literal(duk_compiler_ctx *comp_ctx, duk_ivalue *res); | |
202 | DUK_LOCAL_DECL void duk__nud_object_literal(duk_compiler_ctx *comp_ctx, duk_ivalue *res); | |
203 | DUK_LOCAL_DECL duk_bool_t duk__nud_object_literal_key_check(duk_compiler_ctx *comp_ctx, duk_small_uint_t new_key_flags); | |
204 | ||
205 | /* statement parsing */ | |
206 | 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); | |
11fdf7f2 | 207 | DUK_LOCAL_DECL void duk__parse_var_stmt(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_small_uint_t expr_flags); |
7c673cae FG |
208 | DUK_LOCAL_DECL void duk__parse_for_stmt(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_int_t pc_label_site); |
209 | DUK_LOCAL_DECL void duk__parse_switch_stmt(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_int_t pc_label_site); | |
210 | DUK_LOCAL_DECL void duk__parse_if_stmt(duk_compiler_ctx *comp_ctx, duk_ivalue *res); | |
211 | DUK_LOCAL_DECL void duk__parse_do_stmt(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_int_t pc_label_site); | |
212 | DUK_LOCAL_DECL void duk__parse_while_stmt(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_int_t pc_label_site); | |
213 | DUK_LOCAL_DECL void duk__parse_break_or_continue_stmt(duk_compiler_ctx *comp_ctx, duk_ivalue *res); | |
214 | DUK_LOCAL_DECL void duk__parse_return_stmt(duk_compiler_ctx *comp_ctx, duk_ivalue *res); | |
215 | DUK_LOCAL_DECL void duk__parse_throw_stmt(duk_compiler_ctx *comp_ctx, duk_ivalue *res); | |
216 | DUK_LOCAL_DECL void duk__parse_try_stmt(duk_compiler_ctx *comp_ctx, duk_ivalue *res); | |
217 | DUK_LOCAL_DECL void duk__parse_with_stmt(duk_compiler_ctx *comp_ctx, duk_ivalue *res); | |
218 | DUK_LOCAL_DECL void duk__parse_stmt(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_bool_t allow_source_elem); | |
219 | DUK_LOCAL_DECL duk_int_t duk__stmt_label_site(duk_compiler_ctx *comp_ctx, duk_int_t label_id); | |
220 | DUK_LOCAL_DECL void duk__parse_stmts(duk_compiler_ctx *comp_ctx, duk_bool_t allow_source_elem, duk_bool_t expect_eof); | |
221 | ||
222 | 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); | |
223 | DUK_LOCAL_DECL void duk__parse_func_formals(duk_compiler_ctx *comp_ctx); | |
224 | DUK_LOCAL_DECL void duk__parse_func_like_raw(duk_compiler_ctx *comp_ctx, duk_bool_t is_decl, duk_bool_t is_setget); | |
225 | 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); | |
226 | ||
227 | /* | |
228 | * Parser control values for tokens. The token table is ordered by the | |
229 | * DUK_TOK_XXX defines. | |
230 | * | |
231 | * The binding powers are for lbp() use (i.e. for use in led() context). | |
232 | * Binding powers are positive for typing convenience, and bits at the | |
233 | * top should be reserved for flags. Binding power step must be higher | |
234 | * than 1 so that binding power "lbp - 1" can be used for right associative | |
235 | * operators. Currently a step of 2 is used (which frees one more bit for | |
236 | * flags). | |
237 | */ | |
238 | ||
239 | /* XXX: actually single step levels would work just fine, clean up */ | |
240 | ||
241 | /* binding power "levels" (see doc/compiler.rst) */ | |
242 | #define DUK__BP_INVALID 0 /* always terminates led() */ | |
243 | #define DUK__BP_EOF 2 | |
244 | #define DUK__BP_CLOSING 4 /* token closes expression, e.g. ')', ']' */ | |
245 | #define DUK__BP_FOR_EXPR DUK__BP_CLOSING /* bp to use when parsing a top level Expression */ | |
246 | #define DUK__BP_COMMA 6 | |
247 | #define DUK__BP_ASSIGNMENT 8 | |
248 | #define DUK__BP_CONDITIONAL 10 | |
249 | #define DUK__BP_LOR 12 | |
250 | #define DUK__BP_LAND 14 | |
251 | #define DUK__BP_BOR 16 | |
252 | #define DUK__BP_BXOR 18 | |
253 | #define DUK__BP_BAND 20 | |
254 | #define DUK__BP_EQUALITY 22 | |
255 | #define DUK__BP_RELATIONAL 24 | |
256 | #define DUK__BP_SHIFT 26 | |
257 | #define DUK__BP_ADDITIVE 28 | |
258 | #define DUK__BP_MULTIPLICATIVE 30 | |
259 | #define DUK__BP_POSTFIX 32 | |
260 | #define DUK__BP_CALL 34 | |
261 | #define DUK__BP_MEMBER 36 | |
262 | ||
263 | #define DUK__TOKEN_LBP_BP_MASK 0x1f | |
264 | #define DUK__TOKEN_LBP_FLAG_NO_REGEXP (1 << 5) /* regexp literal must not follow this token */ | |
265 | #define DUK__TOKEN_LBP_FLAG_TERMINATES (1 << 6) /* terminates expression; e.g. post-increment/-decrement */ | |
266 | #define DUK__TOKEN_LBP_FLAG_UNUSED (1 << 7) /* spare */ | |
267 | ||
268 | #define DUK__TOKEN_LBP_GET_BP(x) ((duk_small_uint_t) (((x) & DUK__TOKEN_LBP_BP_MASK) * 2)) | |
269 | ||
270 | #define DUK__MK_LBP(bp) ((bp) >> 1) /* bp is assumed to be even */ | |
271 | #define DUK__MK_LBP_FLAGS(bp,flags) (((bp) >> 1) | (flags)) | |
272 | ||
273 | DUK_LOCAL const duk_uint8_t duk__token_lbp[] = { | |
274 | DUK__MK_LBP(DUK__BP_EOF), /* DUK_TOK_EOF */ | |
275 | DUK__MK_LBP_FLAGS(DUK__BP_INVALID, DUK__TOKEN_LBP_FLAG_NO_REGEXP), /* DUK_TOK_IDENTIFIER */ | |
276 | DUK__MK_LBP(DUK__BP_INVALID), /* DUK_TOK_BREAK */ | |
277 | DUK__MK_LBP(DUK__BP_INVALID), /* DUK_TOK_CASE */ | |
278 | DUK__MK_LBP(DUK__BP_INVALID), /* DUK_TOK_CATCH */ | |
279 | DUK__MK_LBP(DUK__BP_INVALID), /* DUK_TOK_CONTINUE */ | |
280 | DUK__MK_LBP(DUK__BP_INVALID), /* DUK_TOK_DEBUGGER */ | |
281 | DUK__MK_LBP(DUK__BP_INVALID), /* DUK_TOK_DEFAULT */ | |
282 | DUK__MK_LBP(DUK__BP_INVALID), /* DUK_TOK_DELETE */ | |
283 | DUK__MK_LBP(DUK__BP_INVALID), /* DUK_TOK_DO */ | |
284 | DUK__MK_LBP(DUK__BP_INVALID), /* DUK_TOK_ELSE */ | |
285 | DUK__MK_LBP(DUK__BP_INVALID), /* DUK_TOK_FINALLY */ | |
286 | DUK__MK_LBP(DUK__BP_INVALID), /* DUK_TOK_FOR */ | |
287 | DUK__MK_LBP(DUK__BP_INVALID), /* DUK_TOK_FUNCTION */ | |
288 | DUK__MK_LBP(DUK__BP_INVALID), /* DUK_TOK_IF */ | |
289 | DUK__MK_LBP(DUK__BP_RELATIONAL), /* DUK_TOK_IN */ | |
290 | DUK__MK_LBP(DUK__BP_RELATIONAL), /* DUK_TOK_INSTANCEOF */ | |
291 | DUK__MK_LBP(DUK__BP_INVALID), /* DUK_TOK_NEW */ | |
292 | DUK__MK_LBP(DUK__BP_INVALID), /* DUK_TOK_RETURN */ | |
293 | DUK__MK_LBP(DUK__BP_INVALID), /* DUK_TOK_SWITCH */ | |
294 | DUK__MK_LBP_FLAGS(DUK__BP_INVALID, DUK__TOKEN_LBP_FLAG_NO_REGEXP), /* DUK_TOK_THIS */ | |
295 | DUK__MK_LBP(DUK__BP_INVALID), /* DUK_TOK_THROW */ | |
296 | DUK__MK_LBP(DUK__BP_INVALID), /* DUK_TOK_TRY */ | |
297 | DUK__MK_LBP(DUK__BP_INVALID), /* DUK_TOK_TYPEOF */ | |
298 | DUK__MK_LBP(DUK__BP_INVALID), /* DUK_TOK_VAR */ | |
11fdf7f2 | 299 | DUK__MK_LBP(DUK__BP_INVALID), /* DUK_TOK_CONST */ |
7c673cae FG |
300 | DUK__MK_LBP(DUK__BP_INVALID), /* DUK_TOK_VOID */ |
301 | DUK__MK_LBP(DUK__BP_INVALID), /* DUK_TOK_WHILE */ | |
302 | DUK__MK_LBP(DUK__BP_INVALID), /* DUK_TOK_WITH */ | |
303 | DUK__MK_LBP(DUK__BP_INVALID), /* DUK_TOK_CLASS */ | |
7c673cae FG |
304 | DUK__MK_LBP(DUK__BP_INVALID), /* DUK_TOK_ENUM */ |
305 | DUK__MK_LBP(DUK__BP_INVALID), /* DUK_TOK_EXPORT */ | |
306 | DUK__MK_LBP(DUK__BP_INVALID), /* DUK_TOK_EXTENDS */ | |
307 | DUK__MK_LBP(DUK__BP_INVALID), /* DUK_TOK_IMPORT */ | |
308 | DUK__MK_LBP(DUK__BP_INVALID), /* DUK_TOK_SUPER */ | |
309 | DUK__MK_LBP_FLAGS(DUK__BP_INVALID, DUK__TOKEN_LBP_FLAG_NO_REGEXP), /* DUK_TOK_NULL */ | |
310 | DUK__MK_LBP_FLAGS(DUK__BP_INVALID, DUK__TOKEN_LBP_FLAG_NO_REGEXP), /* DUK_TOK_TRUE */ | |
311 | DUK__MK_LBP_FLAGS(DUK__BP_INVALID, DUK__TOKEN_LBP_FLAG_NO_REGEXP), /* DUK_TOK_FALSE */ | |
312 | DUK__MK_LBP(DUK__BP_INVALID), /* DUK_TOK_GET */ | |
313 | DUK__MK_LBP(DUK__BP_INVALID), /* DUK_TOK_SET */ | |
314 | DUK__MK_LBP(DUK__BP_INVALID), /* DUK_TOK_IMPLEMENTS */ | |
315 | DUK__MK_LBP(DUK__BP_INVALID), /* DUK_TOK_INTERFACE */ | |
316 | DUK__MK_LBP(DUK__BP_INVALID), /* DUK_TOK_LET */ | |
317 | DUK__MK_LBP(DUK__BP_INVALID), /* DUK_TOK_PACKAGE */ | |
318 | DUK__MK_LBP(DUK__BP_INVALID), /* DUK_TOK_PRIVATE */ | |
319 | DUK__MK_LBP(DUK__BP_INVALID), /* DUK_TOK_PROTECTED */ | |
320 | DUK__MK_LBP(DUK__BP_INVALID), /* DUK_TOK_PUBLIC */ | |
321 | DUK__MK_LBP(DUK__BP_INVALID), /* DUK_TOK_STATIC */ | |
322 | DUK__MK_LBP(DUK__BP_INVALID), /* DUK_TOK_YIELD */ | |
323 | DUK__MK_LBP(DUK__BP_INVALID), /* DUK_TOK_LCURLY */ | |
324 | DUK__MK_LBP_FLAGS(DUK__BP_INVALID, DUK__TOKEN_LBP_FLAG_NO_REGEXP), /* DUK_TOK_RCURLY */ | |
325 | DUK__MK_LBP(DUK__BP_MEMBER), /* DUK_TOK_LBRACKET */ | |
326 | DUK__MK_LBP_FLAGS(DUK__BP_CLOSING, DUK__TOKEN_LBP_FLAG_NO_REGEXP), /* DUK_TOK_RBRACKET */ | |
327 | DUK__MK_LBP(DUK__BP_CALL), /* DUK_TOK_LPAREN */ | |
328 | DUK__MK_LBP_FLAGS(DUK__BP_CLOSING, DUK__TOKEN_LBP_FLAG_NO_REGEXP), /* DUK_TOK_RPAREN */ | |
329 | DUK__MK_LBP(DUK__BP_MEMBER), /* DUK_TOK_PERIOD */ | |
330 | DUK__MK_LBP(DUK__BP_INVALID), /* DUK_TOK_SEMICOLON */ | |
331 | DUK__MK_LBP(DUK__BP_COMMA), /* DUK_TOK_COMMA */ | |
332 | DUK__MK_LBP(DUK__BP_RELATIONAL), /* DUK_TOK_LT */ | |
333 | DUK__MK_LBP(DUK__BP_RELATIONAL), /* DUK_TOK_GT */ | |
334 | DUK__MK_LBP(DUK__BP_RELATIONAL), /* DUK_TOK_LE */ | |
335 | DUK__MK_LBP(DUK__BP_RELATIONAL), /* DUK_TOK_GE */ | |
336 | DUK__MK_LBP(DUK__BP_EQUALITY), /* DUK_TOK_EQ */ | |
337 | DUK__MK_LBP(DUK__BP_EQUALITY), /* DUK_TOK_NEQ */ | |
338 | DUK__MK_LBP(DUK__BP_EQUALITY), /* DUK_TOK_SEQ */ | |
339 | DUK__MK_LBP(DUK__BP_EQUALITY), /* DUK_TOK_SNEQ */ | |
340 | DUK__MK_LBP(DUK__BP_ADDITIVE), /* DUK_TOK_ADD */ | |
341 | DUK__MK_LBP(DUK__BP_ADDITIVE), /* DUK_TOK_SUB */ | |
342 | DUK__MK_LBP(DUK__BP_MULTIPLICATIVE), /* DUK_TOK_MUL */ | |
343 | DUK__MK_LBP(DUK__BP_MULTIPLICATIVE), /* DUK_TOK_DIV */ | |
344 | DUK__MK_LBP(DUK__BP_MULTIPLICATIVE), /* DUK_TOK_MOD */ | |
345 | DUK__MK_LBP(DUK__BP_POSTFIX), /* DUK_TOK_INCREMENT */ | |
346 | DUK__MK_LBP(DUK__BP_POSTFIX), /* DUK_TOK_DECREMENT */ | |
347 | DUK__MK_LBP(DUK__BP_SHIFT), /* DUK_TOK_ALSHIFT */ | |
348 | DUK__MK_LBP(DUK__BP_SHIFT), /* DUK_TOK_ARSHIFT */ | |
349 | DUK__MK_LBP(DUK__BP_SHIFT), /* DUK_TOK_RSHIFT */ | |
350 | DUK__MK_LBP(DUK__BP_BAND), /* DUK_TOK_BAND */ | |
351 | DUK__MK_LBP(DUK__BP_BOR), /* DUK_TOK_BOR */ | |
352 | DUK__MK_LBP(DUK__BP_BXOR), /* DUK_TOK_BXOR */ | |
353 | DUK__MK_LBP(DUK__BP_INVALID), /* DUK_TOK_LNOT */ | |
354 | DUK__MK_LBP(DUK__BP_INVALID), /* DUK_TOK_BNOT */ | |
355 | DUK__MK_LBP(DUK__BP_LAND), /* DUK_TOK_LAND */ | |
356 | DUK__MK_LBP(DUK__BP_LOR), /* DUK_TOK_LOR */ | |
357 | DUK__MK_LBP(DUK__BP_CONDITIONAL), /* DUK_TOK_QUESTION */ | |
358 | DUK__MK_LBP(DUK__BP_INVALID), /* DUK_TOK_COLON */ | |
359 | DUK__MK_LBP(DUK__BP_ASSIGNMENT), /* DUK_TOK_EQUALSIGN */ | |
360 | DUK__MK_LBP(DUK__BP_ASSIGNMENT), /* DUK_TOK_ADD_EQ */ | |
361 | DUK__MK_LBP(DUK__BP_ASSIGNMENT), /* DUK_TOK_SUB_EQ */ | |
362 | DUK__MK_LBP(DUK__BP_ASSIGNMENT), /* DUK_TOK_MUL_EQ */ | |
363 | DUK__MK_LBP(DUK__BP_ASSIGNMENT), /* DUK_TOK_DIV_EQ */ | |
364 | DUK__MK_LBP(DUK__BP_ASSIGNMENT), /* DUK_TOK_MOD_EQ */ | |
365 | DUK__MK_LBP(DUK__BP_ASSIGNMENT), /* DUK_TOK_ALSHIFT_EQ */ | |
366 | DUK__MK_LBP(DUK__BP_ASSIGNMENT), /* DUK_TOK_ARSHIFT_EQ */ | |
367 | DUK__MK_LBP(DUK__BP_ASSIGNMENT), /* DUK_TOK_RSHIFT_EQ */ | |
368 | DUK__MK_LBP(DUK__BP_ASSIGNMENT), /* DUK_TOK_BAND_EQ */ | |
369 | DUK__MK_LBP(DUK__BP_ASSIGNMENT), /* DUK_TOK_BOR_EQ */ | |
370 | DUK__MK_LBP(DUK__BP_ASSIGNMENT), /* DUK_TOK_BXOR_EQ */ | |
371 | DUK__MK_LBP_FLAGS(DUK__BP_INVALID, DUK__TOKEN_LBP_FLAG_NO_REGEXP), /* DUK_TOK_NUMBER */ | |
372 | DUK__MK_LBP_FLAGS(DUK__BP_INVALID, DUK__TOKEN_LBP_FLAG_NO_REGEXP), /* DUK_TOK_STRING */ | |
373 | DUK__MK_LBP_FLAGS(DUK__BP_INVALID, DUK__TOKEN_LBP_FLAG_NO_REGEXP), /* DUK_TOK_REGEXP */ | |
374 | }; | |
375 | ||
376 | /* | |
377 | * Misc helpers | |
378 | */ | |
379 | ||
380 | DUK_LOCAL void duk__recursion_increase(duk_compiler_ctx *comp_ctx) { | |
381 | DUK_ASSERT(comp_ctx != NULL); | |
382 | DUK_ASSERT(comp_ctx->recursion_depth >= 0); | |
383 | if (comp_ctx->recursion_depth >= comp_ctx->recursion_limit) { | |
11fdf7f2 | 384 | DUK_ERROR_RANGE(comp_ctx->thr, DUK_STR_COMPILER_RECURSION_LIMIT); |
7c673cae FG |
385 | } |
386 | comp_ctx->recursion_depth++; | |
387 | } | |
388 | ||
389 | DUK_LOCAL void duk__recursion_decrease(duk_compiler_ctx *comp_ctx) { | |
390 | DUK_ASSERT(comp_ctx != NULL); | |
391 | DUK_ASSERT(comp_ctx->recursion_depth > 0); | |
392 | comp_ctx->recursion_depth--; | |
393 | } | |
394 | ||
395 | DUK_LOCAL duk_bool_t duk__hstring_is_eval_or_arguments(duk_compiler_ctx *comp_ctx, duk_hstring *h) { | |
396 | DUK_UNREF(comp_ctx); | |
397 | DUK_ASSERT(h != NULL); | |
398 | return DUK_HSTRING_HAS_EVAL_OR_ARGUMENTS(h); | |
399 | } | |
400 | ||
401 | DUK_LOCAL duk_bool_t duk__hstring_is_eval_or_arguments_in_strict_mode(duk_compiler_ctx *comp_ctx, duk_hstring *h) { | |
402 | DUK_ASSERT(h != NULL); | |
403 | return (comp_ctx->curr_func.is_strict && | |
404 | DUK_HSTRING_HAS_EVAL_OR_ARGUMENTS(h)); | |
405 | } | |
406 | ||
407 | /* | |
408 | * Parser duk__advance() token eating functions | |
409 | */ | |
410 | ||
411 | /* XXX: valstack handling is awkward. Add a valstack helper which | |
412 | * avoids dup():ing; valstack_copy(src, dst)? | |
413 | */ | |
414 | ||
415 | DUK_LOCAL void duk__advance_helper(duk_compiler_ctx *comp_ctx, duk_small_int_t expect) { | |
416 | duk_hthread *thr = comp_ctx->thr; | |
417 | duk_context *ctx = (duk_context *) thr; | |
418 | duk_bool_t regexp; | |
419 | ||
420 | DUK_ASSERT(comp_ctx->curr_token.t >= 0 && comp_ctx->curr_token.t <= DUK_TOK_MAXVAL); /* MAXVAL is inclusive */ | |
421 | ||
422 | /* | |
423 | * Use current token to decide whether a RegExp can follow. | |
424 | * | |
425 | * We can use either 't' or 't_nores'; the latter would not | |
426 | * recognize keywords. Some keywords can be followed by a | |
427 | * RegExp (e.g. "return"), so using 't' is better. This is | |
428 | * not trivial, see doc/compiler.rst. | |
429 | */ | |
430 | ||
431 | regexp = 1; | |
432 | if (duk__token_lbp[comp_ctx->curr_token.t] & DUK__TOKEN_LBP_FLAG_NO_REGEXP) { | |
433 | regexp = 0; | |
434 | } | |
435 | if (comp_ctx->curr_func.reject_regexp_in_adv) { | |
436 | comp_ctx->curr_func.reject_regexp_in_adv = 0; | |
437 | regexp = 0; | |
438 | } | |
439 | ||
440 | if (expect >= 0 && comp_ctx->curr_token.t != expect) { | |
441 | DUK_D(DUK_DPRINT("parse error: expect=%ld, got=%ld", | |
442 | (long) expect, (long) comp_ctx->curr_token.t)); | |
11fdf7f2 | 443 | DUK_ERROR_SYNTAX(thr, DUK_STR_PARSE_ERROR); |
7c673cae FG |
444 | } |
445 | ||
446 | /* make current token the previous; need to fiddle with valstack "backing store" */ | |
447 | DUK_MEMCPY(&comp_ctx->prev_token, &comp_ctx->curr_token, sizeof(duk_token)); | |
448 | duk_copy(ctx, comp_ctx->tok11_idx, comp_ctx->tok21_idx); | |
449 | duk_copy(ctx, comp_ctx->tok12_idx, comp_ctx->tok22_idx); | |
450 | ||
451 | /* parse new token */ | |
452 | duk_lexer_parse_js_input_element(&comp_ctx->lex, | |
453 | &comp_ctx->curr_token, | |
454 | comp_ctx->curr_func.is_strict, | |
455 | regexp); | |
456 | ||
457 | DUK_DDD(DUK_DDDPRINT("advance: curr: tok=%ld/%ld,%ld,term=%ld,%!T,%!T " | |
458 | "prev: tok=%ld/%ld,%ld,term=%ld,%!T,%!T", | |
459 | (long) comp_ctx->curr_token.t, | |
460 | (long) comp_ctx->curr_token.t_nores, | |
461 | (long) comp_ctx->curr_token.start_line, | |
462 | (long) comp_ctx->curr_token.lineterm, | |
463 | (duk_tval *) duk_get_tval(ctx, comp_ctx->tok11_idx), | |
464 | (duk_tval *) duk_get_tval(ctx, comp_ctx->tok12_idx), | |
465 | (long) comp_ctx->prev_token.t, | |
466 | (long) comp_ctx->prev_token.t_nores, | |
467 | (long) comp_ctx->prev_token.start_line, | |
468 | (long) comp_ctx->prev_token.lineterm, | |
469 | (duk_tval *) duk_get_tval(ctx, comp_ctx->tok21_idx), | |
470 | (duk_tval *) duk_get_tval(ctx, comp_ctx->tok22_idx))); | |
471 | } | |
472 | ||
473 | /* advance, expecting current token to be a specific token; parse next token in regexp context */ | |
474 | DUK_LOCAL void duk__advance_expect(duk_compiler_ctx *comp_ctx, duk_small_int_t expect) { | |
475 | duk__advance_helper(comp_ctx, expect); | |
476 | } | |
477 | ||
478 | /* advance, whatever the current token is; parse next token in regexp context */ | |
479 | DUK_LOCAL void duk__advance(duk_compiler_ctx *comp_ctx) { | |
480 | duk__advance_helper(comp_ctx, -1); | |
481 | } | |
482 | ||
483 | /* | |
484 | * Helpers for duk_compiler_func. | |
485 | */ | |
486 | ||
487 | /* init function state: inits valstack allocations */ | |
488 | DUK_LOCAL void duk__init_func_valstack_slots(duk_compiler_ctx *comp_ctx) { | |
489 | duk_compiler_func *func = &comp_ctx->curr_func; | |
490 | duk_hthread *thr = comp_ctx->thr; | |
491 | duk_context *ctx = (duk_context *) thr; | |
492 | duk_idx_t entry_top; | |
493 | ||
494 | entry_top = duk_get_top(ctx); | |
495 | ||
496 | DUK_MEMZERO(func, sizeof(*func)); /* intentional overlap with earlier memzero */ | |
497 | #ifdef DUK_USE_EXPLICIT_NULL_INIT | |
498 | func->h_name = NULL; | |
499 | func->h_consts = NULL; | |
500 | func->h_funcs = NULL; | |
501 | func->h_decls = NULL; | |
502 | func->h_labelnames = NULL; | |
503 | func->h_labelinfos = NULL; | |
504 | func->h_argnames = NULL; | |
505 | func->h_varmap = NULL; | |
506 | #endif | |
507 | ||
508 | duk_require_stack(ctx, DUK__FUNCTION_INIT_REQUIRE_SLOTS); | |
509 | ||
510 | DUK_BW_INIT_PUSHBUF(thr, &func->bw_code, DUK__BC_INITIAL_INSTS * sizeof(duk_compiler_instr)); | |
511 | /* code_idx = entry_top + 0 */ | |
512 | ||
513 | duk_push_array(ctx); | |
514 | func->consts_idx = entry_top + 1; | |
11fdf7f2 | 515 | func->h_consts = DUK_GET_HOBJECT_POSIDX(ctx, entry_top + 1); |
7c673cae FG |
516 | DUK_ASSERT(func->h_consts != NULL); |
517 | ||
518 | duk_push_array(ctx); | |
519 | func->funcs_idx = entry_top + 2; | |
11fdf7f2 | 520 | func->h_funcs = DUK_GET_HOBJECT_POSIDX(ctx, entry_top + 2); |
7c673cae FG |
521 | DUK_ASSERT(func->h_funcs != NULL); |
522 | DUK_ASSERT(func->fnum_next == 0); | |
523 | ||
524 | duk_push_array(ctx); | |
525 | func->decls_idx = entry_top + 3; | |
11fdf7f2 | 526 | func->h_decls = DUK_GET_HOBJECT_POSIDX(ctx, entry_top + 3); |
7c673cae FG |
527 | DUK_ASSERT(func->h_decls != NULL); |
528 | ||
529 | duk_push_array(ctx); | |
530 | func->labelnames_idx = entry_top + 4; | |
11fdf7f2 | 531 | func->h_labelnames = DUK_GET_HOBJECT_POSIDX(ctx, entry_top + 4); |
7c673cae FG |
532 | DUK_ASSERT(func->h_labelnames != NULL); |
533 | ||
534 | duk_push_dynamic_buffer(ctx, 0); | |
535 | func->labelinfos_idx = entry_top + 5; | |
536 | func->h_labelinfos = (duk_hbuffer_dynamic *) duk_get_hbuffer(ctx, entry_top + 5); | |
537 | DUK_ASSERT(func->h_labelinfos != NULL); | |
538 | DUK_ASSERT(DUK_HBUFFER_HAS_DYNAMIC(func->h_labelinfos) && !DUK_HBUFFER_HAS_EXTERNAL(func->h_labelinfos)); | |
539 | ||
540 | duk_push_array(ctx); | |
541 | func->argnames_idx = entry_top + 6; | |
11fdf7f2 | 542 | func->h_argnames = DUK_GET_HOBJECT_POSIDX(ctx, entry_top + 6); |
7c673cae FG |
543 | DUK_ASSERT(func->h_argnames != NULL); |
544 | ||
545 | duk_push_object_internal(ctx); | |
546 | func->varmap_idx = entry_top + 7; | |
11fdf7f2 | 547 | func->h_varmap = DUK_GET_HOBJECT_POSIDX(ctx, entry_top + 7); |
7c673cae FG |
548 | DUK_ASSERT(func->h_varmap != NULL); |
549 | } | |
550 | ||
551 | /* reset function state (prepare for pass 2) */ | |
552 | DUK_LOCAL void duk__reset_func_for_pass2(duk_compiler_ctx *comp_ctx) { | |
553 | duk_compiler_func *func = &comp_ctx->curr_func; | |
554 | duk_hthread *thr = comp_ctx->thr; | |
555 | duk_context *ctx = (duk_context *) thr; | |
556 | ||
557 | /* reset bytecode buffer but keep current size; pass 2 will | |
558 | * require same amount or more. | |
559 | */ | |
560 | DUK_BW_RESET_SIZE(thr, &func->bw_code); | |
561 | ||
562 | duk_hobject_set_length_zero(thr, func->h_consts); | |
563 | /* keep func->h_funcs; inner functions are not reparsed to avoid O(depth^2) parsing */ | |
564 | func->fnum_next = 0; | |
565 | /* duk_hobject_set_length_zero(thr, func->h_funcs); */ | |
566 | duk_hobject_set_length_zero(thr, func->h_labelnames); | |
567 | duk_hbuffer_reset(thr, func->h_labelinfos); | |
568 | /* keep func->h_argnames; it is fixed for all passes */ | |
569 | ||
570 | /* truncated in case pass 3 needed */ | |
571 | duk_push_object_internal(ctx); | |
572 | duk_replace(ctx, func->varmap_idx); | |
11fdf7f2 | 573 | func->h_varmap = DUK_GET_HOBJECT_POSIDX(ctx, func->varmap_idx); |
7c673cae FG |
574 | DUK_ASSERT(func->h_varmap != NULL); |
575 | } | |
576 | ||
577 | /* cleanup varmap from any null entries, compact it, etc; returns number | |
578 | * of final entries after cleanup. | |
579 | */ | |
580 | DUK_LOCAL duk_int_t duk__cleanup_varmap(duk_compiler_ctx *comp_ctx) { | |
581 | duk_hthread *thr = comp_ctx->thr; | |
582 | duk_context *ctx = (duk_context *) thr; | |
583 | duk_hobject *h_varmap; | |
584 | duk_hstring *h_key; | |
585 | duk_tval *tv; | |
586 | duk_uint32_t i, e_next; | |
587 | duk_int_t ret; | |
588 | ||
589 | /* [ ... varmap ] */ | |
590 | ||
11fdf7f2 | 591 | h_varmap = DUK_GET_HOBJECT_NEGIDX(ctx, -1); |
7c673cae FG |
592 | DUK_ASSERT(h_varmap != NULL); |
593 | ||
594 | ret = 0; | |
595 | e_next = DUK_HOBJECT_GET_ENEXT(h_varmap); | |
596 | for (i = 0; i < e_next; i++) { | |
597 | h_key = DUK_HOBJECT_E_GET_KEY(thr->heap, h_varmap, i); | |
598 | if (!h_key) { | |
599 | continue; | |
600 | } | |
601 | ||
602 | DUK_ASSERT(!DUK_HOBJECT_E_SLOT_IS_ACCESSOR(thr->heap, h_varmap, i)); | |
603 | ||
604 | /* The entries can either be register numbers or 'null' values. | |
605 | * Thus, no need to DECREF them and get side effects. DECREF'ing | |
606 | * the keys (strings) can cause memory to be freed but no side | |
607 | * effects as strings don't have finalizers. This is why we can | |
608 | * rely on the object properties not changing from underneath us. | |
609 | */ | |
610 | ||
611 | tv = DUK_HOBJECT_E_GET_VALUE_TVAL_PTR(thr->heap, h_varmap, i); | |
612 | if (!DUK_TVAL_IS_NUMBER(tv)) { | |
613 | DUK_ASSERT(!DUK_TVAL_IS_HEAP_ALLOCATED(tv)); | |
7c673cae FG |
614 | DUK_HOBJECT_E_SET_KEY(thr->heap, h_varmap, i, NULL); |
615 | DUK_HSTRING_DECREF(thr, h_key); | |
11fdf7f2 | 616 | /* when key is NULL, value is garbage so no need to set */ |
7c673cae FG |
617 | } else { |
618 | ret++; | |
619 | } | |
620 | } | |
621 | ||
622 | duk_compact(ctx, -1); | |
623 | ||
624 | return ret; | |
625 | } | |
626 | ||
627 | /* convert duk_compiler_func into a function template, leaving the result | |
628 | * on top of stack. | |
629 | */ | |
630 | /* XXX: awkward and bloated asm -- use faster internal accesses */ | |
631 | DUK_LOCAL void duk__convert_to_func_template(duk_compiler_ctx *comp_ctx, duk_bool_t force_no_namebind) { | |
632 | duk_compiler_func *func = &comp_ctx->curr_func; | |
633 | duk_hthread *thr = comp_ctx->thr; | |
634 | duk_context *ctx = (duk_context *) thr; | |
635 | duk_hcompiledfunction *h_res; | |
636 | duk_hbuffer_fixed *h_data; | |
637 | duk_size_t consts_count; | |
638 | duk_size_t funcs_count; | |
639 | duk_size_t code_count; | |
640 | duk_size_t code_size; | |
641 | duk_size_t data_size; | |
642 | duk_size_t i; | |
643 | duk_tval *p_const; | |
644 | duk_hobject **p_func; | |
645 | duk_instr_t *p_instr; | |
646 | duk_compiler_instr *q_instr; | |
647 | duk_tval *tv; | |
648 | ||
649 | DUK_DDD(DUK_DDDPRINT("converting duk_compiler_func to function/template")); | |
650 | ||
651 | /* | |
652 | * Push result object and init its flags | |
653 | */ | |
654 | ||
655 | /* Valstack should suffice here, required on function valstack init */ | |
656 | ||
657 | (void) duk_push_compiledfunction(ctx); | |
11fdf7f2 | 658 | h_res = (duk_hcompiledfunction *) DUK_GET_HOBJECT_NEGIDX(ctx, -1); /* XXX: specific getter */ |
7c673cae FG |
659 | DUK_ASSERT(h_res != NULL); |
660 | ||
661 | if (func->is_function) { | |
662 | DUK_DDD(DUK_DDDPRINT("function -> set NEWENV")); | |
663 | DUK_HOBJECT_SET_NEWENV((duk_hobject *) h_res); | |
664 | ||
665 | if (!func->is_arguments_shadowed) { | |
666 | /* arguments object would be accessible; note that shadowing | |
667 | * bindings are arguments or function declarations, neither | |
668 | * of which are deletable, so this is safe. | |
669 | */ | |
670 | ||
671 | if (func->id_access_arguments || func->may_direct_eval) { | |
672 | DUK_DDD(DUK_DDDPRINT("function may access 'arguments' object directly or " | |
673 | "indirectly -> set CREATEARGS")); | |
674 | DUK_HOBJECT_SET_CREATEARGS((duk_hobject *) h_res); | |
675 | } | |
676 | } | |
677 | } else if (func->is_eval && func->is_strict) { | |
678 | DUK_DDD(DUK_DDDPRINT("strict eval code -> set NEWENV")); | |
679 | DUK_HOBJECT_SET_NEWENV((duk_hobject *) h_res); | |
680 | } else { | |
681 | /* non-strict eval: env is caller's env or global env (direct vs. indirect call) | |
682 | * global code: env is is global env | |
683 | */ | |
684 | DUK_DDD(DUK_DDDPRINT("non-strict eval code or global code -> no NEWENV")); | |
685 | DUK_ASSERT(!DUK_HOBJECT_HAS_NEWENV((duk_hobject *) h_res)); | |
686 | } | |
687 | ||
688 | if (func->is_function && !func->is_decl && func->h_name != NULL && !force_no_namebind) { | |
689 | /* Object literal set/get functions have a name (property | |
690 | * name) but must not have a lexical name binding, see | |
691 | * test-bug-getset-func-name.js. | |
692 | */ | |
693 | DUK_DDD(DUK_DDDPRINT("function expression with a name -> set NAMEBINDING")); | |
694 | DUK_HOBJECT_SET_NAMEBINDING((duk_hobject *) h_res); | |
695 | } | |
696 | ||
697 | if (func->is_strict) { | |
698 | DUK_DDD(DUK_DDDPRINT("function is strict -> set STRICT")); | |
699 | DUK_HOBJECT_SET_STRICT((duk_hobject *) h_res); | |
700 | } | |
701 | ||
702 | if (func->is_notail) { | |
703 | DUK_DDD(DUK_DDDPRINT("function is notail -> set NOTAIL")); | |
704 | DUK_HOBJECT_SET_NOTAIL((duk_hobject *) h_res); | |
705 | } | |
706 | ||
707 | /* | |
708 | * Build function fixed size 'data' buffer, which contains bytecode, | |
709 | * constants, and inner function references. | |
710 | * | |
711 | * During the building phase 'data' is reachable but incomplete. | |
712 | * Only incref's occur during building (no refzero or GC happens), | |
713 | * so the building process is atomic. | |
714 | */ | |
715 | ||
716 | consts_count = duk_hobject_get_length(thr, func->h_consts); | |
717 | funcs_count = duk_hobject_get_length(thr, func->h_funcs) / 3; | |
718 | code_count = DUK_BW_GET_SIZE(thr, &func->bw_code) / sizeof(duk_compiler_instr); | |
719 | code_size = code_count * sizeof(duk_instr_t); | |
720 | ||
721 | data_size = consts_count * sizeof(duk_tval) + | |
722 | funcs_count * sizeof(duk_hobject *) + | |
723 | code_size; | |
724 | ||
725 | DUK_DDD(DUK_DDDPRINT("consts_count=%ld, funcs_count=%ld, code_size=%ld -> " | |
726 | "data_size=%ld*%ld + %ld*%ld + %ld = %ld", | |
727 | (long) consts_count, (long) funcs_count, (long) code_size, | |
728 | (long) consts_count, (long) sizeof(duk_tval), | |
729 | (long) funcs_count, (long) sizeof(duk_hobject *), | |
730 | (long) code_size, (long) data_size)); | |
731 | ||
732 | duk_push_fixed_buffer(ctx, data_size); | |
733 | h_data = (duk_hbuffer_fixed *) duk_get_hbuffer(ctx, -1); | |
734 | DUK_ASSERT(h_data != NULL); | |
735 | ||
736 | DUK_HCOMPILEDFUNCTION_SET_DATA(thr->heap, h_res, (duk_hbuffer *) h_data); | |
737 | DUK_HEAPHDR_INCREF(thr, h_data); | |
738 | ||
739 | p_const = (duk_tval *) (void *) DUK_HBUFFER_FIXED_GET_DATA_PTR(thr->heap, h_data); | |
740 | for (i = 0; i < consts_count; i++) { | |
741 | DUK_ASSERT(i <= DUK_UARRIDX_MAX); /* const limits */ | |
742 | tv = duk_hobject_find_existing_array_entry_tval_ptr(thr->heap, func->h_consts, (duk_uarridx_t) i); | |
743 | DUK_ASSERT(tv != NULL); | |
744 | DUK_TVAL_SET_TVAL(p_const, tv); | |
745 | p_const++; | |
746 | DUK_TVAL_INCREF(thr, tv); /* may be a string constant */ | |
747 | ||
748 | DUK_DDD(DUK_DDDPRINT("constant: %!T", (duk_tval *) tv)); | |
749 | } | |
750 | ||
751 | p_func = (duk_hobject **) p_const; | |
752 | DUK_HCOMPILEDFUNCTION_SET_FUNCS(thr->heap, h_res, p_func); | |
753 | for (i = 0; i < funcs_count; i++) { | |
754 | duk_hobject *h; | |
755 | DUK_ASSERT(i * 3 <= DUK_UARRIDX_MAX); /* func limits */ | |
756 | tv = duk_hobject_find_existing_array_entry_tval_ptr(thr->heap, func->h_funcs, (duk_uarridx_t) (i * 3)); | |
757 | DUK_ASSERT(tv != NULL); | |
758 | DUK_ASSERT(DUK_TVAL_IS_OBJECT(tv)); | |
759 | h = DUK_TVAL_GET_OBJECT(tv); | |
760 | DUK_ASSERT(h != NULL); | |
761 | DUK_ASSERT(DUK_HOBJECT_IS_COMPILEDFUNCTION(h)); | |
762 | *p_func++ = h; | |
763 | DUK_HOBJECT_INCREF(thr, h); | |
764 | ||
765 | DUK_DDD(DUK_DDDPRINT("inner function: %p -> %!iO", | |
766 | (void *) h, (duk_heaphdr *) h)); | |
767 | } | |
768 | ||
769 | p_instr = (duk_instr_t *) p_func; | |
770 | DUK_HCOMPILEDFUNCTION_SET_BYTECODE(thr->heap, h_res, p_instr); | |
771 | ||
772 | /* copy bytecode instructions one at a time */ | |
773 | q_instr = (duk_compiler_instr *) (void *) DUK_BW_GET_BASEPTR(thr, &func->bw_code); | |
774 | for (i = 0; i < code_count; i++) { | |
775 | p_instr[i] = q_instr[i].ins; | |
776 | } | |
777 | /* Note: 'q_instr' is still used below */ | |
778 | ||
779 | DUK_ASSERT((duk_uint8_t *) (p_instr + code_count) == DUK_HBUFFER_FIXED_GET_DATA_PTR(thr->heap, h_data) + data_size); | |
780 | ||
781 | duk_pop(ctx); /* 'data' (and everything in it) is reachable through h_res now */ | |
782 | ||
783 | /* | |
784 | * Init object properties | |
785 | * | |
786 | * Properties should be added in decreasing order of access frequency. | |
787 | * (Not very critical for function templates.) | |
788 | */ | |
789 | ||
790 | DUK_DDD(DUK_DDDPRINT("init function properties")); | |
791 | ||
792 | /* [ ... res ] */ | |
793 | ||
794 | /* _Varmap: omitted if function is guaranteed not to do slow path identifier | |
795 | * accesses or if it would turn out to be empty of actual register mappings | |
796 | * after a cleanup. When debugging is enabled, we always need the varmap to | |
797 | * be able to lookup variables at any point. | |
798 | */ | |
799 | #if defined(DUK_USE_DEBUGGER_SUPPORT) | |
800 | if (1) { | |
801 | #else | |
802 | if (func->id_access_slow || /* directly uses slow accesses */ | |
803 | func->may_direct_eval || /* may indirectly slow access through a direct eval */ | |
804 | funcs_count > 0) { /* has inner functions which may slow access (XXX: this can be optimized by looking at the inner functions) */ | |
805 | #endif | |
806 | duk_int_t num_used; | |
807 | duk_dup(ctx, func->varmap_idx); | |
808 | num_used = duk__cleanup_varmap(comp_ctx); | |
809 | DUK_DDD(DUK_DDDPRINT("cleaned up varmap: %!T (num_used=%ld)", | |
810 | (duk_tval *) duk_get_tval(ctx, -1), (long) num_used)); | |
811 | ||
812 | if (num_used > 0) { | |
813 | duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_INT_VARMAP, DUK_PROPDESC_FLAGS_NONE); | |
814 | } else { | |
815 | DUK_DDD(DUK_DDDPRINT("varmap is empty after cleanup -> no need to add")); | |
816 | duk_pop(ctx); | |
817 | } | |
818 | } | |
819 | ||
820 | /* _Formals: omitted if function is guaranteed not to need a (non-strict) arguments object */ | |
821 | if (1) { | |
822 | /* XXX: Add a proper condition. If formals list is omitted, recheck | |
823 | * handling for 'length' in duk_js_push_closure(); it currently relies | |
824 | * on _Formals being set. Removal may need to be conditional to debugging | |
825 | * being enabled/disabled too. | |
826 | */ | |
827 | duk_dup(ctx, func->argnames_idx); | |
828 | duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_INT_FORMALS, DUK_PROPDESC_FLAGS_NONE); | |
829 | } | |
830 | ||
831 | /* name */ | |
832 | if (func->h_name) { | |
833 | duk_push_hstring(ctx, func->h_name); | |
834 | duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_NAME, DUK_PROPDESC_FLAGS_NONE); | |
835 | } | |
836 | ||
837 | /* _Source */ | |
838 | #if defined(DUK_USE_NONSTD_FUNC_SOURCE_PROPERTY) | |
839 | if (0) { | |
840 | /* XXX: Currently function source code is not stored, as it is not | |
841 | * required by the standard. Source code should not be stored by | |
842 | * default (user should enable it explicitly), and the source should | |
843 | * probably be compressed with a trivial text compressor; average | |
844 | * compression of 20-30% is quite easy to achieve even with a trivial | |
845 | * compressor (RLE + backwards lookup). | |
846 | * | |
847 | * Debugging needs source code to be useful: sometimes input code is | |
848 | * not found in files as it may be generated and then eval()'d, given | |
849 | * by dynamic C code, etc. | |
850 | * | |
851 | * Other issues: | |
852 | * | |
853 | * - Need tokenizer indices for start and end to substring | |
854 | * - Always normalize function declaration part? | |
855 | * - If we keep _Formals, only need to store body | |
856 | */ | |
857 | ||
858 | /* | |
859 | * For global or eval code this is straightforward. For functions | |
860 | * created with the Function constructor we only get the source for | |
861 | * the body and must manufacture the "function ..." part. | |
862 | * | |
863 | * For instance, for constructed functions (v8): | |
864 | * | |
865 | * > a = new Function("foo", "bar", "print(foo)"); | |
866 | * [Function] | |
867 | * > a.toString() | |
868 | * 'function anonymous(foo,bar) {\nprint(foo)\n}' | |
869 | * | |
870 | * Similarly for e.g. getters (v8): | |
871 | * | |
872 | * > x = { get a(foo,bar) { print(foo); } } | |
873 | * { a: [Getter] } | |
874 | * > Object.getOwnPropertyDescriptor(x, 'a').get.toString() | |
875 | * 'function a(foo,bar) { print(foo); }' | |
876 | */ | |
877 | ||
878 | #if 0 | |
879 | duk_push_string(ctx, "XXX"); | |
880 | duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_INT_SOURCE, DUK_PROPDESC_FLAGS_NONE); | |
881 | #endif | |
882 | } | |
883 | #endif /* DUK_USE_NONSTD_FUNC_SOURCE_PROPERTY */ | |
884 | ||
885 | /* _Pc2line */ | |
886 | #if defined(DUK_USE_PC2LINE) | |
887 | if (1) { | |
888 | /* | |
889 | * Size-optimized pc->line mapping. | |
890 | */ | |
891 | ||
892 | DUK_ASSERT(code_count <= DUK_COMPILER_MAX_BYTECODE_LENGTH); | |
893 | duk_hobject_pc2line_pack(thr, q_instr, (duk_uint_fast32_t) code_count); /* -> pushes fixed buffer */ | |
894 | duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_INT_PC2LINE, DUK_PROPDESC_FLAGS_NONE); | |
895 | ||
896 | /* XXX: if assertions enabled, walk through all valid PCs | |
897 | * and check line mapping. | |
898 | */ | |
899 | } | |
900 | #endif /* DUK_USE_PC2LINE */ | |
901 | ||
902 | /* fileName */ | |
903 | if (comp_ctx->h_filename) { | |
904 | /* | |
905 | * Source filename (or equivalent), for identifying thrown errors. | |
906 | */ | |
907 | ||
908 | duk_push_hstring(ctx, comp_ctx->h_filename); | |
909 | duk_xdef_prop_stridx(ctx, -2, DUK_STRIDX_FILE_NAME, DUK_PROPDESC_FLAGS_NONE); | |
910 | } | |
911 | ||
912 | /* | |
913 | * Init remaining result fields | |
914 | * | |
915 | * 'nregs' controls how large a register frame is allocated. | |
916 | * | |
917 | * 'nargs' controls how many formal arguments are written to registers: | |
918 | * r0, ... r(nargs-1). The remaining registers are initialized to | |
919 | * undefined. | |
920 | */ | |
921 | ||
922 | DUK_ASSERT(func->temp_max >= 0); | |
11fdf7f2 TL |
923 | h_res->nregs = (duk_uint16_t) func->temp_max; |
924 | h_res->nargs = (duk_uint16_t) duk_hobject_get_length(thr, func->h_argnames); | |
7c673cae FG |
925 | DUK_ASSERT(h_res->nregs >= h_res->nargs); /* pass2 allocation handles this */ |
926 | #if defined(DUK_USE_DEBUGGER_SUPPORT) | |
927 | h_res->start_line = (duk_uint32_t) func->min_line; | |
928 | h_res->end_line = (duk_uint32_t) func->max_line; | |
929 | #endif | |
930 | ||
931 | DUK_DD(DUK_DDPRINT("converted function: %!ixT", | |
932 | (duk_tval *) duk_get_tval(ctx, -1))); | |
933 | ||
934 | /* | |
935 | * Compact the function template. | |
936 | */ | |
937 | ||
938 | duk_compact(ctx, -1); | |
939 | ||
940 | /* | |
941 | * Debug dumping | |
942 | */ | |
943 | ||
944 | #ifdef DUK_USE_DDDPRINT | |
945 | { | |
946 | duk_hcompiledfunction *h; | |
947 | duk_instr_t *p, *p_start, *p_end; | |
948 | ||
949 | h = (duk_hcompiledfunction *) duk_get_hobject(ctx, -1); | |
950 | p_start = (duk_instr_t *) DUK_HCOMPILEDFUNCTION_GET_CODE_BASE(thr->heap, h); | |
951 | p_end = (duk_instr_t *) DUK_HCOMPILEDFUNCTION_GET_CODE_END(thr->heap, h); | |
952 | ||
953 | p = p_start; | |
954 | while (p < p_end) { | |
955 | DUK_DDD(DUK_DDDPRINT("BC %04ld: %!I ; 0x%08lx op=%ld (%!C) a=%ld b=%ld c=%ld", | |
956 | (long) (p - p_start), | |
957 | (duk_instr_t) (*p), | |
958 | (unsigned long) (*p), | |
959 | (long) DUK_DEC_OP(*p), | |
960 | (long) DUK_DEC_OP(*p), | |
961 | (long) DUK_DEC_A(*p), | |
962 | (long) DUK_DEC_B(*p), | |
963 | (long) DUK_DEC_C(*p))); | |
964 | p++; | |
965 | } | |
966 | } | |
967 | #endif | |
968 | } | |
969 | ||
970 | /* | |
971 | * Code emission helpers | |
972 | * | |
973 | * Some emission helpers understand the range of target and source reg/const | |
974 | * values and automatically emit shuffling code if necessary. This is the | |
975 | * case when the slot in question (A, B, C) is used in the standard way and | |
976 | * for opcodes the emission helpers explicitly understand (like DUK_OP_CALL). | |
977 | * | |
978 | * The standard way is that: | |
979 | * - slot A is a target register | |
980 | * - slot B is a source register/constant | |
981 | * - slot C is a source register/constant | |
982 | * | |
983 | * If a slot is used in a non-standard way the caller must indicate this | |
984 | * somehow. If a slot is used as a target instead of a source (or vice | |
985 | * versa), this can be indicated with a flag to trigger proper shuffling | |
986 | * (e.g. DUK__EMIT_FLAG_B_IS_TARGET). If the value in the slot is not | |
987 | * register/const related at all, the caller must ensure that the raw value | |
988 | * fits into the corresponding slot so as to not trigger shuffling. The | |
989 | * caller must set a "no shuffle" flag to ensure compilation fails if | |
990 | * shuffling were to be triggered because of an internal error. | |
991 | * | |
992 | * For slots B and C the raw slot size is 9 bits but one bit is reserved for | |
993 | * the reg/const indicator. To use the full 9-bit range for a raw value, | |
994 | * shuffling must be disabled with the DUK__EMIT_FLAG_NO_SHUFFLE_{B,C} flag. | |
995 | * Shuffling is only done for A, B, and C slots, not the larger BC or ABC slots. | |
996 | * | |
997 | * There is call handling specific understanding in the A-B-C emitter to | |
998 | * convert call setup and call instructions into indirect ones if necessary. | |
999 | */ | |
1000 | ||
1001 | /* Code emission flags, passed in the 'opcode' field. Opcode + flags | |
1002 | * fit into 16 bits for now, so use duk_small_uint.t. | |
1003 | */ | |
1004 | #define DUK__EMIT_FLAG_NO_SHUFFLE_A (1 << 8) | |
1005 | #define DUK__EMIT_FLAG_NO_SHUFFLE_B (1 << 9) | |
1006 | #define DUK__EMIT_FLAG_NO_SHUFFLE_C (1 << 10) | |
1007 | #define DUK__EMIT_FLAG_A_IS_SOURCE (1 << 11) /* slot A is a source (default: target) */ | |
1008 | #define DUK__EMIT_FLAG_B_IS_TARGET (1 << 12) /* slot B is a target (default: source) */ | |
1009 | #define DUK__EMIT_FLAG_C_IS_TARGET (1 << 13) /* slot C is a target (default: source) */ | |
1010 | #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 */ | |
1011 | #define DUK__EMIT_FLAG_RESERVE_JUMPSLOT (1 << 15) /* reserve a jumpslot after instr before target spilling, used for NEXTENUM */ | |
1012 | ||
1013 | /* XXX: clarify on when and where DUK__CONST_MARKER is allowed */ | |
1014 | /* XXX: opcode specific assertions on when consts are allowed */ | |
1015 | ||
1016 | /* XXX: macro smaller than call? */ | |
1017 | DUK_LOCAL duk_int_t duk__get_current_pc(duk_compiler_ctx *comp_ctx) { | |
1018 | duk_compiler_func *func; | |
1019 | func = &comp_ctx->curr_func; | |
1020 | return (duk_int_t) (DUK_BW_GET_SIZE(comp_ctx->thr, &func->bw_code) / sizeof(duk_compiler_instr)); | |
1021 | } | |
1022 | ||
1023 | DUK_LOCAL duk_compiler_instr *duk__get_instr_ptr(duk_compiler_ctx *comp_ctx, duk_int_t pc) { | |
1024 | DUK_ASSERT(pc >= 0); | |
1025 | 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))); | |
1026 | return ((duk_compiler_instr *) (void *) DUK_BW_GET_BASEPTR(comp_ctx->thr, &comp_ctx->curr_func.bw_code)) + pc; | |
1027 | } | |
1028 | ||
1029 | /* emit instruction; could return PC but that's not needed in the majority | |
1030 | * of cases. | |
1031 | */ | |
1032 | DUK_LOCAL void duk__emit(duk_compiler_ctx *comp_ctx, duk_instr_t ins) { | |
1033 | #if defined(DUK_USE_PC2LINE) | |
1034 | duk_int_t line; | |
1035 | #endif | |
1036 | duk_compiler_instr *instr; | |
1037 | ||
1038 | DUK_DDD(DUK_DDDPRINT("duk__emit: 0x%08lx curr_token.start_line=%ld prev_token.start_line=%ld pc=%ld --> %!I", | |
1039 | (unsigned long) ins, | |
1040 | (long) comp_ctx->curr_token.start_line, | |
1041 | (long) comp_ctx->prev_token.start_line, | |
1042 | (long) duk__get_current_pc(comp_ctx), | |
1043 | (duk_instr_t) ins)); | |
1044 | ||
1045 | instr = (duk_compiler_instr *) (void *) DUK_BW_ENSURE_GETPTR(comp_ctx->thr, &comp_ctx->curr_func.bw_code, sizeof(duk_compiler_instr)); | |
1046 | DUK_BW_ADD_PTR(comp_ctx->thr, &comp_ctx->curr_func.bw_code, sizeof(duk_compiler_instr)); | |
1047 | ||
1048 | #if defined(DUK_USE_PC2LINE) | |
1049 | /* The line number tracking is a bit inconsistent right now, which | |
1050 | * affects debugger accuracy. Mostly call sites emit opcodes when | |
1051 | * they have parsed a token (say a terminating semicolon) and called | |
1052 | * duk__advance(). In this case the line number of the previous | |
1053 | * token is the most accurate one (except in prologue where | |
1054 | * prev_token.start_line is 0). This is probably not 100% correct | |
1055 | * right now. | |
1056 | */ | |
1057 | /* approximation, close enough */ | |
1058 | line = comp_ctx->prev_token.start_line; | |
1059 | if (line == 0) { | |
1060 | line = comp_ctx->curr_token.start_line; | |
1061 | } | |
1062 | #endif | |
1063 | ||
1064 | instr->ins = ins; | |
1065 | #if defined(DUK_USE_PC2LINE) | |
1066 | instr->line = line; | |
1067 | #endif | |
1068 | #if defined(DUK_USE_DEBUGGER_SUPPORT) | |
1069 | if (line < comp_ctx->curr_func.min_line) { | |
1070 | comp_ctx->curr_func.min_line = line; | |
1071 | } | |
1072 | if (line > comp_ctx->curr_func.max_line) { | |
1073 | comp_ctx->curr_func.max_line = line; | |
1074 | } | |
1075 | #endif | |
1076 | ||
1077 | /* Limit checks for bytecode byte size and line number. */ | |
1078 | if (DUK_UNLIKELY(DUK_BW_GET_SIZE(comp_ctx->thr, &comp_ctx->curr_func.bw_code) > DUK_USE_ESBC_MAX_BYTES)) { | |
1079 | goto fail_bc_limit; | |
1080 | } | |
1081 | #if defined(DUK_USE_PC2LINE) && defined(DUK_USE_ESBC_LIMITS) | |
1082 | #if defined(DUK_USE_BUFLEN16) | |
1083 | /* Buffer length is bounded to 0xffff automatically, avoid compile warning. */ | |
1084 | if (DUK_UNLIKELY(line > DUK_USE_ESBC_MAX_LINENUMBER)) { | |
1085 | goto fail_bc_limit; | |
1086 | } | |
1087 | #else | |
1088 | if (DUK_UNLIKELY(line > DUK_USE_ESBC_MAX_LINENUMBER)) { | |
1089 | goto fail_bc_limit; | |
1090 | } | |
1091 | #endif | |
1092 | #endif | |
1093 | ||
1094 | return; | |
1095 | ||
1096 | fail_bc_limit: | |
11fdf7f2 | 1097 | DUK_ERROR_RANGE(comp_ctx->thr, DUK_STR_BYTECODE_LIMIT); |
7c673cae FG |
1098 | } |
1099 | ||
1100 | /* Update function min/max line from current token. Needed to improve | |
1101 | * function line range information for debugging, so that e.g. opening | |
1102 | * curly brace is covered by line range even when no opcodes are emitted | |
1103 | * for the line containing the brace. | |
1104 | */ | |
1105 | DUK_LOCAL void duk__update_lineinfo_currtoken(duk_compiler_ctx *comp_ctx) { | |
1106 | #if defined(DUK_USE_DEBUGGER_SUPPORT) | |
1107 | duk_int_t line; | |
1108 | ||
1109 | line = comp_ctx->curr_token.start_line; | |
1110 | if (line == 0) { | |
1111 | return; | |
1112 | } | |
1113 | if (line < comp_ctx->curr_func.min_line) { | |
1114 | comp_ctx->curr_func.min_line = line; | |
1115 | } | |
1116 | if (line > comp_ctx->curr_func.max_line) { | |
1117 | comp_ctx->curr_func.max_line = line; | |
1118 | } | |
1119 | #else | |
1120 | DUK_UNREF(comp_ctx); | |
1121 | #endif | |
1122 | } | |
1123 | ||
1124 | #if 0 /* unused */ | |
1125 | DUK_LOCAL void duk__emit_op_only(duk_compiler_ctx *comp_ctx, duk_small_uint_t op) { | |
1126 | duk__emit(comp_ctx, DUK_ENC_OP_ABC(op, 0)); | |
1127 | } | |
1128 | #endif | |
1129 | ||
1130 | /* Important main primitive. */ | |
1131 | 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) { | |
1132 | duk_instr_t ins = 0; | |
1133 | duk_int_t a_out = -1; | |
1134 | duk_int_t b_out = -1; | |
1135 | duk_int_t c_out = -1; | |
1136 | duk_int_t tmp; | |
1137 | ||
1138 | DUK_DDD(DUK_DDDPRINT("emit: op_flags=%04lx, a=%ld, b=%ld, c=%ld", | |
1139 | (unsigned long) op_flags, (long) a, (long) b, (long) c)); | |
1140 | ||
1141 | /* We could rely on max temp/const checks: if they don't exceed BC | |
1142 | * limit, nothing here can either (just asserts would be enough). | |
1143 | * Currently we check for the limits, which provides additional | |
1144 | * protection against creating invalid bytecode due to compiler | |
1145 | * bugs. | |
1146 | */ | |
1147 | ||
1148 | DUK_ASSERT_DISABLE((op_flags & 0xff) >= DUK_BC_OP_MIN); /* unsigned */ | |
1149 | DUK_ASSERT((op_flags & 0xff) <= DUK_BC_OP_MAX); | |
1150 | ||
1151 | /* Input shuffling happens before the actual operation, while output | |
1152 | * shuffling happens afterwards. Output shuffling decisions are still | |
1153 | * made at the same time to reduce branch clutter; output shuffle decisions | |
1154 | * are recorded into X_out variables. | |
1155 | */ | |
1156 | ||
1157 | /* Slot A */ | |
1158 | ||
1159 | #if defined(DUK_USE_SHUFFLE_TORTURE) | |
1160 | if (a <= DUK_BC_A_MAX && (op_flags & DUK__EMIT_FLAG_NO_SHUFFLE_A)) { | |
1161 | #else | |
1162 | if (a <= DUK_BC_A_MAX) { | |
1163 | #endif | |
1164 | ; | |
1165 | } else if (op_flags & DUK__EMIT_FLAG_NO_SHUFFLE_A) { | |
1166 | DUK_D(DUK_DPRINT("out of regs: 'a' (reg) needs shuffling but shuffle prohibited, a: %ld", (long) a)); | |
1167 | goto error_outofregs; | |
1168 | } else if (a <= DUK_BC_BC_MAX) { | |
1169 | comp_ctx->curr_func.needs_shuffle = 1; | |
1170 | tmp = comp_ctx->curr_func.shuffle1; | |
1171 | if (op_flags & DUK__EMIT_FLAG_A_IS_SOURCE) { | |
1172 | duk__emit(comp_ctx, DUK_ENC_OP_A_BC(DUK_OP_LDREG, tmp, a)); | |
1173 | } else { | |
1174 | duk_small_int_t op = op_flags & 0xff; | |
1175 | if (op == DUK_OP_CSVAR || op == DUK_OP_CSREG || op == DUK_OP_CSPROP) { | |
1176 | /* Special handling for call setup instructions. The target | |
1177 | * is expressed indirectly, but there is no output shuffling. | |
1178 | */ | |
1179 | DUK_ASSERT((op_flags & DUK__EMIT_FLAG_A_IS_SOURCE) == 0); | |
1180 | duk__emit_load_int32_noshuffle(comp_ctx, tmp, a); | |
1181 | DUK_ASSERT(DUK_OP_CSVARI == DUK_OP_CSVAR + 1); | |
1182 | DUK_ASSERT(DUK_OP_CSREGI == DUK_OP_CSREG + 1); | |
1183 | DUK_ASSERT(DUK_OP_CSPROPI == DUK_OP_CSPROP + 1); | |
1184 | op_flags++; /* indirect opcode follows direct */ | |
1185 | } else { | |
1186 | /* Output shuffle needed after main operation */ | |
1187 | a_out = a; | |
1188 | } | |
1189 | } | |
1190 | a = tmp; | |
1191 | } else { | |
1192 | DUK_D(DUK_DPRINT("out of regs: 'a' (reg) needs shuffling but does not fit into BC, a: %ld", (long) a)); | |
1193 | goto error_outofregs; | |
1194 | } | |
1195 | ||
1196 | /* Slot B */ | |
1197 | ||
1198 | if (b & DUK__CONST_MARKER) { | |
1199 | DUK_ASSERT((op_flags & DUK__EMIT_FLAG_NO_SHUFFLE_B) == 0); | |
1200 | DUK_ASSERT((op_flags & DUK__EMIT_FLAG_B_IS_TARGET) == 0); | |
1201 | DUK_ASSERT((op_flags & 0xff) != DUK_OP_CALL); | |
1202 | DUK_ASSERT((op_flags & 0xff) != DUK_OP_NEW); | |
1203 | b = b & ~DUK__CONST_MARKER; | |
1204 | #if defined(DUK_USE_SHUFFLE_TORTURE) | |
1205 | if (0) { | |
1206 | #else | |
1207 | if (b <= 0xff) { | |
1208 | #endif | |
1209 | ins |= DUK_ENC_OP_A_B_C(0, 0, 0x100, 0); /* const flag for B */ | |
1210 | } else if (b <= DUK_BC_BC_MAX) { | |
1211 | comp_ctx->curr_func.needs_shuffle = 1; | |
1212 | tmp = comp_ctx->curr_func.shuffle2; | |
1213 | duk__emit(comp_ctx, DUK_ENC_OP_A_BC(DUK_OP_LDCONST, tmp, b)); | |
1214 | b = tmp; | |
1215 | } else { | |
1216 | DUK_D(DUK_DPRINT("out of regs: 'b' (const) needs shuffling but does not fit into BC, b: %ld", (long) b)); | |
1217 | goto error_outofregs; | |
1218 | } | |
1219 | } else { | |
1220 | #if defined(DUK_USE_SHUFFLE_TORTURE) | |
1221 | if (b <= 0xff && (op_flags & DUK__EMIT_FLAG_NO_SHUFFLE_B)) { | |
1222 | #else | |
1223 | if (b <= 0xff) { | |
1224 | #endif | |
1225 | ; | |
1226 | } else if (op_flags & DUK__EMIT_FLAG_NO_SHUFFLE_B) { | |
1227 | if (b > DUK_BC_B_MAX) { | |
1228 | /* Note: 0xff != DUK_BC_B_MAX */ | |
1229 | DUK_D(DUK_DPRINT("out of regs: 'b' (reg) needs shuffling but shuffle prohibited, b: %ld", (long) b)); | |
1230 | goto error_outofregs; | |
1231 | } | |
1232 | } else if (b <= DUK_BC_BC_MAX) { | |
1233 | comp_ctx->curr_func.needs_shuffle = 1; | |
1234 | tmp = comp_ctx->curr_func.shuffle2; | |
1235 | if (op_flags & DUK__EMIT_FLAG_B_IS_TARGET) { | |
1236 | /* Output shuffle needed after main operation */ | |
1237 | b_out = b; | |
1238 | } | |
1239 | if (!(op_flags & DUK__EMIT_FLAG_B_IS_TARGET) || (op_flags & DUK__EMIT_FLAG_B_IS_TARGETSOURCE)) { | |
1240 | duk_small_int_t op = op_flags & 0xff; | |
1241 | if (op == DUK_OP_CALL || op == DUK_OP_NEW || | |
1242 | op == DUK_OP_MPUTOBJ || op == DUK_OP_MPUTARR) { | |
1243 | /* Special handling for CALL/NEW/MPUTOBJ/MPUTARR shuffling. | |
1244 | * For each, slot B identifies the first register of a range | |
1245 | * of registers, so normal shuffling won't work. Instead, | |
1246 | * an indirect version of the opcode is used. | |
1247 | */ | |
1248 | DUK_ASSERT((op_flags & DUK__EMIT_FLAG_B_IS_TARGET) == 0); | |
1249 | duk__emit_load_int32_noshuffle(comp_ctx, tmp, b); | |
1250 | DUK_ASSERT(DUK_OP_CALLI == DUK_OP_CALL + 1); | |
1251 | DUK_ASSERT(DUK_OP_NEWI == DUK_OP_NEW + 1); | |
1252 | DUK_ASSERT(DUK_OP_MPUTOBJI == DUK_OP_MPUTOBJ + 1); | |
1253 | DUK_ASSERT(DUK_OP_MPUTARRI == DUK_OP_MPUTARR + 1); | |
1254 | op_flags++; /* indirect opcode follows direct */ | |
1255 | } else { | |
1256 | duk__emit(comp_ctx, DUK_ENC_OP_A_BC(DUK_OP_LDREG, tmp, b)); | |
1257 | } | |
1258 | } | |
1259 | b = tmp; | |
1260 | } else { | |
1261 | DUK_D(DUK_DPRINT("out of regs: 'b' (reg) needs shuffling but does not fit into BC, b: %ld", (long) b)); | |
1262 | goto error_outofregs; | |
1263 | } | |
1264 | } | |
1265 | ||
1266 | /* Slot C */ | |
1267 | ||
1268 | if (c & DUK__CONST_MARKER) { | |
1269 | DUK_ASSERT((op_flags & DUK__EMIT_FLAG_NO_SHUFFLE_C) == 0); | |
1270 | DUK_ASSERT((op_flags & DUK__EMIT_FLAG_C_IS_TARGET) == 0); | |
1271 | c = c & ~DUK__CONST_MARKER; | |
1272 | #if defined(DUK_USE_SHUFFLE_TORTURE) | |
1273 | if (0) { | |
1274 | #else | |
1275 | if (c <= 0xff) { | |
1276 | #endif | |
1277 | ins |= DUK_ENC_OP_A_B_C(0, 0, 0, 0x100); /* const flag for C */ | |
1278 | } else if (c <= DUK_BC_BC_MAX) { | |
1279 | comp_ctx->curr_func.needs_shuffle = 1; | |
1280 | tmp = comp_ctx->curr_func.shuffle3; | |
1281 | duk__emit(comp_ctx, DUK_ENC_OP_A_BC(DUK_OP_LDCONST, tmp, c)); | |
1282 | c = tmp; | |
1283 | } else { | |
1284 | DUK_D(DUK_DPRINT("out of regs: 'c' (const) needs shuffling but does not fit into BC, c: %ld", (long) c)); | |
1285 | goto error_outofregs; | |
1286 | } | |
1287 | } else { | |
1288 | #if defined(DUK_USE_SHUFFLE_TORTURE) | |
1289 | if (c <= 0xff && (op_flags & DUK__EMIT_FLAG_NO_SHUFFLE_C)) { | |
1290 | #else | |
1291 | if (c <= 0xff) { | |
1292 | #endif | |
1293 | ; | |
1294 | } else if (op_flags & DUK__EMIT_FLAG_NO_SHUFFLE_C) { | |
1295 | if (c > DUK_BC_C_MAX) { | |
1296 | /* Note: 0xff != DUK_BC_C_MAX */ | |
1297 | DUK_D(DUK_DPRINT("out of regs: 'c' (reg) needs shuffling but shuffle prohibited, c: %ld", (long) c)); | |
1298 | goto error_outofregs; | |
1299 | } | |
1300 | } else if (c <= DUK_BC_BC_MAX) { | |
1301 | comp_ctx->curr_func.needs_shuffle = 1; | |
1302 | tmp = comp_ctx->curr_func.shuffle3; | |
1303 | if (op_flags & DUK__EMIT_FLAG_C_IS_TARGET) { | |
1304 | /* Output shuffle needed after main operation */ | |
1305 | c_out = c; | |
1306 | } else { | |
1307 | duk_small_int_t op = op_flags & 0xff; | |
1308 | if (op == DUK_OP_EXTRA && | |
1309 | (a == DUK_EXTRAOP_INITGET || a == DUK_EXTRAOP_INITSET)) { | |
1310 | /* Special shuffling for INITGET/INITSET, where slot C | |
1311 | * identifies a register pair and cannot be shuffled | |
1312 | * normally. Use an indirect variant instead. | |
1313 | */ | |
1314 | DUK_ASSERT((op_flags & DUK__EMIT_FLAG_C_IS_TARGET) == 0); | |
1315 | duk__emit_load_int32_noshuffle(comp_ctx, tmp, c); | |
1316 | DUK_ASSERT(DUK_EXTRAOP_INITGETI == DUK_EXTRAOP_INITGET + 1); | |
1317 | DUK_ASSERT(DUK_EXTRAOP_INITSETI == DUK_EXTRAOP_INITSET + 1); | |
1318 | a++; /* indirect opcode follows direct */ | |
1319 | } else { | |
1320 | duk__emit(comp_ctx, DUK_ENC_OP_A_BC(DUK_OP_LDREG, tmp, c)); | |
1321 | } | |
1322 | } | |
1323 | c = tmp; | |
1324 | } else { | |
1325 | DUK_D(DUK_DPRINT("out of regs: 'c' (reg) needs shuffling but does not fit into BC, c: %ld", (long) c)); | |
1326 | goto error_outofregs; | |
1327 | } | |
1328 | } | |
1329 | ||
1330 | /* Main operation */ | |
1331 | ||
1332 | DUK_ASSERT_DISABLE(a >= DUK_BC_A_MIN); /* unsigned */ | |
1333 | DUK_ASSERT(a <= DUK_BC_A_MAX); | |
1334 | DUK_ASSERT_DISABLE(b >= DUK_BC_B_MIN); /* unsigned */ | |
1335 | DUK_ASSERT(b <= DUK_BC_B_MAX); | |
1336 | DUK_ASSERT_DISABLE(c >= DUK_BC_C_MIN); /* unsigned */ | |
1337 | DUK_ASSERT(c <= DUK_BC_C_MAX); | |
1338 | ||
1339 | ins |= DUK_ENC_OP_A_B_C(op_flags & 0xff, a, b, c); | |
1340 | duk__emit(comp_ctx, ins); | |
1341 | ||
1342 | /* NEXTENUM needs a jump slot right after the main instruction. | |
1343 | * When the JUMP is taken, output spilling is not needed so this | |
1344 | * workaround is possible. The jump slot PC is exceptionally | |
1345 | * plumbed through comp_ctx to minimize call sites. | |
1346 | */ | |
1347 | if (op_flags & DUK__EMIT_FLAG_RESERVE_JUMPSLOT) { | |
1348 | comp_ctx->emit_jumpslot_pc = duk__get_current_pc(comp_ctx); | |
1349 | duk__emit_abc(comp_ctx, DUK_OP_JUMP, 0); | |
1350 | } | |
1351 | ||
1352 | /* Output shuffling: only one output register is realistically possible. | |
1353 | * | |
1354 | * (Zero would normally be an OK marker value: if the target register | |
1355 | * was zero, it would never be shuffled. But with DUK_USE_SHUFFLE_TORTURE | |
1356 | * this is no longer true, so use -1 as a marker instead.) | |
1357 | */ | |
1358 | ||
1359 | if (a_out >= 0) { | |
1360 | DUK_ASSERT(b_out < 0); | |
1361 | DUK_ASSERT(c_out < 0); | |
1362 | duk__emit(comp_ctx, DUK_ENC_OP_A_BC(DUK_OP_STREG, a, a_out)); | |
1363 | } else if (b_out >= 0) { | |
1364 | DUK_ASSERT(a_out < 0); | |
1365 | DUK_ASSERT(c_out < 0); | |
1366 | duk__emit(comp_ctx, DUK_ENC_OP_A_BC(DUK_OP_STREG, b, b_out)); | |
1367 | } else if (c_out >= 0) { | |
1368 | DUK_ASSERT(b_out < 0); | |
1369 | DUK_ASSERT(c_out < 0); | |
1370 | duk__emit(comp_ctx, DUK_ENC_OP_A_BC(DUK_OP_STREG, c, c_out)); | |
1371 | } | |
1372 | ||
1373 | return; | |
1374 | ||
1375 | error_outofregs: | |
11fdf7f2 | 1376 | DUK_ERROR_RANGE(comp_ctx->thr, DUK_STR_REG_LIMIT); |
7c673cae FG |
1377 | } |
1378 | ||
1379 | 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) { | |
1380 | duk__emit_a_b_c(comp_ctx, op_flags | DUK__EMIT_FLAG_NO_SHUFFLE_C, a, b, 0); | |
1381 | } | |
1382 | ||
1383 | #if 0 /* unused */ | |
1384 | DUK_LOCAL void duk__emit_a(duk_compiler_ctx *comp_ctx, int op_flags, int a) { | |
1385 | duk__emit_a_b_c(comp_ctx, op_flags | DUK__EMIT_FLAG_NO_SHUFFLE_B | DUK__EMIT_FLAG_NO_SHUFFLE_C, a, 0, 0); | |
1386 | } | |
1387 | #endif | |
1388 | ||
1389 | 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) { | |
1390 | duk_instr_t ins; | |
1391 | duk_int_t tmp; | |
1392 | ||
1393 | /* allow caller to give a const number with the DUK__CONST_MARKER */ | |
1394 | bc = bc & (~DUK__CONST_MARKER); | |
1395 | ||
1396 | DUK_ASSERT_DISABLE((op_flags & 0xff) >= DUK_BC_OP_MIN); /* unsigned */ | |
1397 | DUK_ASSERT((op_flags & 0xff) <= DUK_BC_OP_MAX); | |
1398 | DUK_ASSERT_DISABLE(bc >= DUK_BC_BC_MIN); /* unsigned */ | |
1399 | DUK_ASSERT(bc <= DUK_BC_BC_MAX); | |
1400 | DUK_ASSERT((bc & DUK__CONST_MARKER) == 0); | |
1401 | ||
1402 | if (bc <= DUK_BC_BC_MAX) { | |
1403 | ; | |
1404 | } else { | |
1405 | /* No BC shuffling now. */ | |
1406 | goto error_outofregs; | |
1407 | } | |
1408 | ||
1409 | #if defined(DUK_USE_SHUFFLE_TORTURE) | |
1410 | if (a <= DUK_BC_A_MAX && (op_flags & DUK__EMIT_FLAG_NO_SHUFFLE_A)) { | |
1411 | #else | |
1412 | if (a <= DUK_BC_A_MAX) { | |
1413 | #endif | |
1414 | ins = DUK_ENC_OP_A_BC(op_flags & 0xff, a, bc); | |
1415 | duk__emit(comp_ctx, ins); | |
1416 | } else if (op_flags & DUK__EMIT_FLAG_NO_SHUFFLE_A) { | |
1417 | goto error_outofregs; | |
1418 | } else if (a <= DUK_BC_BC_MAX) { | |
1419 | comp_ctx->curr_func.needs_shuffle = 1; | |
1420 | tmp = comp_ctx->curr_func.shuffle1; | |
1421 | ins = DUK_ENC_OP_A_BC(op_flags & 0xff, tmp, bc); | |
1422 | if (op_flags & DUK__EMIT_FLAG_A_IS_SOURCE) { | |
1423 | duk__emit(comp_ctx, DUK_ENC_OP_A_BC(DUK_OP_LDREG, tmp, a)); | |
1424 | duk__emit(comp_ctx, ins); | |
1425 | } else { | |
1426 | duk__emit(comp_ctx, ins); | |
1427 | duk__emit(comp_ctx, DUK_ENC_OP_A_BC(DUK_OP_STREG, tmp, a)); | |
1428 | } | |
1429 | } else { | |
1430 | goto error_outofregs; | |
1431 | } | |
1432 | return; | |
1433 | ||
1434 | error_outofregs: | |
11fdf7f2 | 1435 | DUK_ERROR_RANGE(comp_ctx->thr, DUK_STR_REG_LIMIT); |
7c673cae FG |
1436 | } |
1437 | ||
1438 | DUK_LOCAL void duk__emit_abc(duk_compiler_ctx *comp_ctx, duk_small_uint_t op, duk_regconst_t abc) { | |
1439 | duk_instr_t ins; | |
1440 | ||
1441 | DUK_ASSERT_DISABLE(op >= DUK_BC_OP_MIN); /* unsigned */ | |
1442 | DUK_ASSERT(op <= DUK_BC_OP_MAX); | |
1443 | DUK_ASSERT_DISABLE(abc >= DUK_BC_ABC_MIN); /* unsigned */ | |
1444 | DUK_ASSERT(abc <= DUK_BC_ABC_MAX); | |
1445 | DUK_ASSERT((abc & DUK__CONST_MARKER) == 0); | |
1446 | ||
1447 | if (abc <= DUK_BC_ABC_MAX) { | |
1448 | ; | |
1449 | } else { | |
1450 | goto error_outofregs; | |
1451 | } | |
1452 | ins = DUK_ENC_OP_ABC(op, abc); | |
1453 | DUK_DDD(DUK_DDDPRINT("duk__emit_abc: 0x%08lx line=%ld pc=%ld op=%ld (%!C) abc=%ld (%!I)", | |
1454 | (unsigned long) ins, (long) comp_ctx->curr_token.start_line, | |
1455 | (long) duk__get_current_pc(comp_ctx), (long) op, (long) op, | |
1456 | (long) abc, (duk_instr_t) ins)); | |
1457 | duk__emit(comp_ctx, ins); | |
1458 | return; | |
1459 | ||
1460 | error_outofregs: | |
11fdf7f2 | 1461 | DUK_ERROR_RANGE(comp_ctx->thr, DUK_STR_REG_LIMIT); |
7c673cae FG |
1462 | } |
1463 | ||
1464 | 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) { | |
1465 | DUK_ASSERT_DISABLE((extraop_flags & 0xff) >= DUK_BC_EXTRAOP_MIN); /* unsigned */ | |
1466 | DUK_ASSERT((extraop_flags & 0xff) <= DUK_BC_EXTRAOP_MAX); | |
1467 | /* Setting "no shuffle A" is covered by the assert, but it's needed | |
1468 | * with DUK_USE_SHUFFLE_TORTURE. | |
1469 | */ | |
1470 | duk__emit_a_b_c(comp_ctx, | |
1471 | DUK_OP_EXTRA | DUK__EMIT_FLAG_NO_SHUFFLE_A | (extraop_flags & ~0xff), /* transfer flags */ | |
1472 | extraop_flags & 0xff, | |
1473 | b, | |
1474 | c); | |
1475 | } | |
1476 | ||
1477 | DUK_LOCAL void duk__emit_extraop_b(duk_compiler_ctx *comp_ctx, duk_small_uint_t extraop_flags, duk_regconst_t b) { | |
1478 | DUK_ASSERT_DISABLE((extraop_flags & 0xff) >= DUK_BC_EXTRAOP_MIN); /* unsigned */ | |
1479 | DUK_ASSERT((extraop_flags & 0xff) <= DUK_BC_EXTRAOP_MAX); | |
1480 | /* Setting "no shuffle A" is covered by the assert, but it's needed | |
1481 | * with DUK_USE_SHUFFLE_TORTURE. | |
1482 | */ | |
1483 | duk__emit_a_b_c(comp_ctx, | |
1484 | DUK_OP_EXTRA | DUK__EMIT_FLAG_NO_SHUFFLE_A | (extraop_flags & ~0xff), /* transfer flags */ | |
1485 | extraop_flags & 0xff, | |
1486 | b, | |
1487 | 0); | |
1488 | } | |
1489 | ||
1490 | DUK_LOCAL void duk__emit_extraop_bc(duk_compiler_ctx *comp_ctx, duk_small_uint_t extraop, duk_regconst_t bc) { | |
1491 | DUK_ASSERT_DISABLE(extraop >= DUK_BC_EXTRAOP_MIN); /* unsigned */ | |
1492 | DUK_ASSERT(extraop <= DUK_BC_EXTRAOP_MAX); | |
1493 | /* Setting "no shuffle A" is covered by the assert, but it's needed | |
1494 | * with DUK_USE_SHUFFLE_TORTURE. | |
1495 | */ | |
1496 | duk__emit_a_bc(comp_ctx, | |
1497 | DUK_OP_EXTRA | DUK__EMIT_FLAG_NO_SHUFFLE_A, | |
1498 | extraop, | |
1499 | bc); | |
1500 | } | |
1501 | ||
1502 | DUK_LOCAL void duk__emit_extraop_only(duk_compiler_ctx *comp_ctx, duk_small_uint_t extraop_flags) { | |
1503 | DUK_ASSERT_DISABLE((extraop_flags & 0xff) >= DUK_BC_EXTRAOP_MIN); /* unsigned */ | |
1504 | DUK_ASSERT((extraop_flags & 0xff) <= DUK_BC_EXTRAOP_MAX); | |
1505 | /* Setting "no shuffle A" is covered by the assert, but it's needed | |
1506 | * with DUK_USE_SHUFFLE_TORTURE. | |
1507 | */ | |
1508 | duk__emit_a_b_c(comp_ctx, | |
1509 | DUK_OP_EXTRA | DUK__EMIT_FLAG_NO_SHUFFLE_A | DUK__EMIT_FLAG_NO_SHUFFLE_B | | |
1510 | DUK__EMIT_FLAG_NO_SHUFFLE_C | (extraop_flags & ~0xff), /* transfer flags */ | |
1511 | extraop_flags & 0xff, | |
1512 | 0, | |
1513 | 0); | |
1514 | } | |
1515 | ||
1516 | 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) { | |
1517 | /* XXX: Shuffling support could be implemented here so that LDINT+LDINTX | |
1518 | * would only shuffle once (instead of twice). The current code works | |
1519 | * though, and has a smaller compiler footprint. | |
1520 | */ | |
1521 | ||
1522 | if ((val >= (duk_int32_t) DUK_BC_BC_MIN - (duk_int32_t) DUK_BC_LDINT_BIAS) && | |
1523 | (val <= (duk_int32_t) DUK_BC_BC_MAX - (duk_int32_t) DUK_BC_LDINT_BIAS)) { | |
1524 | DUK_DDD(DUK_DDDPRINT("emit LDINT to reg %ld for %ld", (long) reg, (long) val)); | |
1525 | duk__emit_a_bc(comp_ctx, DUK_OP_LDINT | op_flags, reg, (duk_regconst_t) (val + (duk_int32_t) DUK_BC_LDINT_BIAS)); | |
1526 | } else { | |
1527 | duk_int32_t hi = val >> DUK_BC_LDINTX_SHIFT; | |
1528 | duk_int32_t lo = val & ((((duk_int32_t) 1) << DUK_BC_LDINTX_SHIFT) - 1); | |
1529 | DUK_ASSERT(lo >= 0); | |
1530 | DUK_DDD(DUK_DDDPRINT("emit LDINT+LDINTX to reg %ld for %ld -> hi %ld, lo %ld", | |
1531 | (long) reg, (long) val, (long) hi, (long) lo)); | |
1532 | duk__emit_a_bc(comp_ctx, DUK_OP_LDINT | op_flags, reg, (duk_regconst_t) (hi + (duk_int32_t) DUK_BC_LDINT_BIAS)); | |
1533 | duk__emit_a_bc(comp_ctx, DUK_OP_LDINTX | op_flags, reg, (duk_regconst_t) lo); | |
1534 | } | |
1535 | } | |
1536 | ||
1537 | DUK_LOCAL void duk__emit_load_int32(duk_compiler_ctx *comp_ctx, duk_reg_t reg, duk_int32_t val) { | |
1538 | duk__emit_load_int32_raw(comp_ctx, reg, val, 0 /*op_flags*/); | |
1539 | } | |
1540 | ||
1541 | #if defined(DUK_USE_SHUFFLE_TORTURE) | |
1542 | /* Used by duk__emit*() calls so that we don't shuffle the loadints that | |
1543 | * are needed to handle indirect opcodes. | |
1544 | */ | |
1545 | DUK_LOCAL void duk__emit_load_int32_noshuffle(duk_compiler_ctx *comp_ctx, duk_reg_t reg, duk_int32_t val) { | |
1546 | duk__emit_load_int32_raw(comp_ctx, reg, val, DUK__EMIT_FLAG_NO_SHUFFLE_A /*op_flags*/); | |
1547 | } | |
1548 | #else | |
1549 | DUK_LOCAL void duk__emit_load_int32_noshuffle(duk_compiler_ctx *comp_ctx, duk_reg_t reg, duk_int32_t val) { | |
1550 | /* When torture not enabled, can just use the same helper because | |
1551 | * 'reg' won't get spilled. | |
1552 | */ | |
1553 | DUK_ASSERT(reg <= DUK_BC_A_MAX); | |
1554 | duk__emit_load_int32(comp_ctx, reg, val); | |
1555 | } | |
1556 | #endif | |
1557 | ||
1558 | DUK_LOCAL void duk__emit_jump(duk_compiler_ctx *comp_ctx, duk_int_t target_pc) { | |
1559 | duk_int_t curr_pc; | |
1560 | duk_int_t offset; | |
1561 | ||
1562 | curr_pc = (duk_int_t) (DUK_BW_GET_SIZE(comp_ctx->thr, &comp_ctx->curr_func.bw_code) / sizeof(duk_compiler_instr)); | |
1563 | offset = (duk_int_t) target_pc - (duk_int_t) curr_pc - 1; | |
1564 | DUK_ASSERT(offset + DUK_BC_JUMP_BIAS >= DUK_BC_ABC_MIN); | |
1565 | DUK_ASSERT(offset + DUK_BC_JUMP_BIAS <= DUK_BC_ABC_MAX); | |
1566 | duk__emit_abc(comp_ctx, DUK_OP_JUMP, (duk_regconst_t) (offset + DUK_BC_JUMP_BIAS)); | |
1567 | } | |
1568 | ||
1569 | DUK_LOCAL duk_int_t duk__emit_jump_empty(duk_compiler_ctx *comp_ctx) { | |
1570 | duk_int_t ret; | |
1571 | ||
1572 | ret = duk__get_current_pc(comp_ctx); /* useful for patching jumps later */ | |
1573 | duk__emit_abc(comp_ctx, DUK_OP_JUMP, 0); | |
1574 | return ret; | |
1575 | } | |
1576 | ||
1577 | /* Insert an empty jump in the middle of code emitted earlier. This is | |
1578 | * currently needed for compiling for-in. | |
1579 | */ | |
1580 | DUK_LOCAL void duk__insert_jump_entry(duk_compiler_ctx *comp_ctx, duk_int_t jump_pc) { | |
1581 | #if defined(DUK_USE_PC2LINE) | |
1582 | duk_int_t line; | |
1583 | #endif | |
1584 | duk_compiler_instr *instr; | |
1585 | duk_size_t offset; | |
1586 | ||
1587 | offset = jump_pc * sizeof(duk_compiler_instr), | |
1588 | instr = (duk_compiler_instr *) (void *) | |
1589 | DUK_BW_INSERT_ENSURE_AREA(comp_ctx->thr, | |
1590 | &comp_ctx->curr_func.bw_code, | |
1591 | offset, | |
1592 | sizeof(duk_compiler_instr)); | |
1593 | ||
1594 | #if defined(DUK_USE_PC2LINE) | |
1595 | line = comp_ctx->curr_token.start_line; /* approximation, close enough */ | |
1596 | #endif | |
1597 | instr->ins = DUK_ENC_OP_ABC(DUK_OP_JUMP, 0); | |
1598 | #if defined(DUK_USE_PC2LINE) | |
1599 | instr->line = line; | |
1600 | #endif | |
1601 | ||
1602 | DUK_BW_ADD_PTR(comp_ctx->thr, &comp_ctx->curr_func.bw_code, sizeof(duk_compiler_instr)); | |
1603 | if (DUK_UNLIKELY(DUK_BW_GET_SIZE(comp_ctx->thr, &comp_ctx->curr_func.bw_code) > DUK_USE_ESBC_MAX_BYTES)) { | |
1604 | goto fail_bc_limit; | |
1605 | } | |
1606 | return; | |
1607 | ||
1608 | fail_bc_limit: | |
11fdf7f2 | 1609 | DUK_ERROR_RANGE(comp_ctx->thr, DUK_STR_BYTECODE_LIMIT); |
7c673cae FG |
1610 | } |
1611 | ||
1612 | /* Does not assume that jump_pc contains a DUK_OP_JUMP previously; this is intentional | |
1613 | * to allow e.g. an INVALID opcode be overwritten with a JUMP (label management uses this). | |
1614 | */ | |
1615 | DUK_LOCAL void duk__patch_jump(duk_compiler_ctx *comp_ctx, duk_int_t jump_pc, duk_int_t target_pc) { | |
1616 | duk_compiler_instr *instr; | |
1617 | duk_int_t offset; | |
1618 | ||
1619 | /* allow negative PCs, behave as a no-op */ | |
1620 | if (jump_pc < 0) { | |
1621 | DUK_DDD(DUK_DDDPRINT("duk__patch_jump(): nop call, jump_pc=%ld (<0), target_pc=%ld", | |
1622 | (long) jump_pc, (long) target_pc)); | |
1623 | return; | |
1624 | } | |
1625 | DUK_ASSERT(jump_pc >= 0); | |
1626 | ||
1627 | /* XXX: range assert */ | |
1628 | instr = duk__get_instr_ptr(comp_ctx, jump_pc); | |
1629 | DUK_ASSERT(instr != NULL); | |
1630 | ||
1631 | /* XXX: range assert */ | |
1632 | offset = target_pc - jump_pc - 1; | |
1633 | ||
1634 | instr->ins = DUK_ENC_OP_ABC(DUK_OP_JUMP, offset + DUK_BC_JUMP_BIAS); | |
1635 | DUK_DDD(DUK_DDDPRINT("duk__patch_jump(): jump_pc=%ld, target_pc=%ld, offset=%ld", | |
1636 | (long) jump_pc, (long) target_pc, (long) offset)); | |
1637 | } | |
1638 | ||
1639 | DUK_LOCAL void duk__patch_jump_here(duk_compiler_ctx *comp_ctx, duk_int_t jump_pc) { | |
1640 | duk__patch_jump(comp_ctx, jump_pc, duk__get_current_pc(comp_ctx)); | |
1641 | } | |
1642 | ||
1643 | 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) { | |
1644 | duk_compiler_instr *instr; | |
1645 | ||
1646 | DUK_ASSERT((reg_catch & DUK__CONST_MARKER) == 0); | |
1647 | ||
1648 | instr = duk__get_instr_ptr(comp_ctx, ldconst_pc); | |
1649 | DUK_ASSERT(DUK_DEC_OP(instr->ins) == DUK_OP_LDCONST); | |
1650 | DUK_ASSERT(instr != NULL); | |
1651 | if (const_varname & DUK__CONST_MARKER) { | |
1652 | /* Have a catch variable. */ | |
1653 | const_varname = const_varname & (~DUK__CONST_MARKER); | |
1654 | if (reg_catch > DUK_BC_BC_MAX || const_varname > DUK_BC_BC_MAX) { | |
1655 | /* Catch attempts to use out-of-range reg/const. Without this | |
1656 | * check Duktape 0.12.0 could generate invalid code which caused | |
1657 | * an assert failure on execution. This error is triggered e.g. | |
1658 | * for functions with a lot of constants and a try-catch statement. | |
1659 | * Shuffling or opcode semantics change is needed to fix the issue. | |
1660 | * See: test-bug-trycatch-many-constants.js. | |
1661 | */ | |
1662 | DUK_D(DUK_DPRINT("failed to patch trycatch: flags=%ld, reg_catch=%ld, const_varname=%ld (0x%08lx)", | |
1663 | (long) flags, (long) reg_catch, (long) const_varname, (long) const_varname)); | |
11fdf7f2 | 1664 | DUK_ERROR_RANGE(comp_ctx->thr, DUK_STR_REG_LIMIT); |
7c673cae FG |
1665 | } |
1666 | instr->ins |= DUK_ENC_OP_A_BC(0, 0, const_varname); | |
1667 | } else { | |
1668 | /* No catch variable, e.g. a try-finally; replace LDCONST with | |
1669 | * NOP to avoid a bogus LDCONST. | |
1670 | */ | |
1671 | instr->ins = DUK_ENC_OP_A(DUK_OP_EXTRA, DUK_EXTRAOP_NOP); | |
1672 | } | |
1673 | ||
1674 | instr = duk__get_instr_ptr(comp_ctx, trycatch_pc); | |
1675 | DUK_ASSERT(instr != NULL); | |
1676 | DUK_ASSERT_DISABLE(flags >= DUK_BC_A_MIN); | |
1677 | DUK_ASSERT(flags <= DUK_BC_A_MAX); | |
1678 | instr->ins = DUK_ENC_OP_A_BC(DUK_OP_TRYCATCH, flags, reg_catch); | |
1679 | } | |
1680 | ||
1681 | DUK_LOCAL void duk__emit_if_false_skip(duk_compiler_ctx *comp_ctx, duk_regconst_t regconst) { | |
1682 | duk__emit_a_b_c(comp_ctx, | |
1683 | DUK_OP_IF | DUK__EMIT_FLAG_NO_SHUFFLE_A | DUK__EMIT_FLAG_NO_SHUFFLE_C, | |
1684 | 0 /*false*/, | |
1685 | regconst, | |
1686 | 0 /*unused*/); | |
1687 | } | |
1688 | ||
1689 | DUK_LOCAL void duk__emit_if_true_skip(duk_compiler_ctx *comp_ctx, duk_regconst_t regconst) { | |
1690 | duk__emit_a_b_c(comp_ctx, | |
1691 | DUK_OP_IF | DUK__EMIT_FLAG_NO_SHUFFLE_A | DUK__EMIT_FLAG_NO_SHUFFLE_C, | |
1692 | 1 /*true*/, | |
1693 | regconst, | |
1694 | 0 /*unused*/); | |
1695 | } | |
1696 | ||
1697 | DUK_LOCAL void duk__emit_invalid(duk_compiler_ctx *comp_ctx) { | |
1698 | duk__emit_extraop_bc(comp_ctx, DUK_EXTRAOP_INVALID, 0); | |
1699 | } | |
1700 | ||
1701 | /* | |
1702 | * Peephole optimizer for finished bytecode. | |
1703 | * | |
1704 | * Does not remove opcodes; currently only straightens out unconditional | |
1705 | * jump chains which are generated by several control structures. | |
1706 | */ | |
1707 | ||
1708 | DUK_LOCAL void duk__peephole_optimize_bytecode(duk_compiler_ctx *comp_ctx) { | |
1709 | duk_compiler_instr *bc; | |
1710 | duk_small_uint_t iter; | |
1711 | duk_int_t i, n; | |
1712 | duk_int_t count_opt; | |
1713 | ||
1714 | bc = (duk_compiler_instr *) (void *) DUK_BW_GET_BASEPTR(comp_ctx->thr, &comp_ctx->curr_func.bw_code); | |
1715 | #if defined(DUK_USE_BUFLEN16) | |
1716 | /* No need to assert, buffer size maximum is 0xffff. */ | |
1717 | #else | |
1718 | 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 */ | |
1719 | #endif | |
1720 | n = (duk_int_t) (DUK_BW_GET_SIZE(comp_ctx->thr, &comp_ctx->curr_func.bw_code) / sizeof(duk_compiler_instr)); | |
1721 | ||
1722 | for (iter = 0; iter < DUK_COMPILER_PEEPHOLE_MAXITER; iter++) { | |
1723 | count_opt = 0; | |
1724 | ||
1725 | for (i = 0; i < n; i++) { | |
1726 | duk_instr_t ins; | |
1727 | duk_int_t target_pc1; | |
1728 | duk_int_t target_pc2; | |
1729 | ||
1730 | ins = bc[i].ins; | |
1731 | if (DUK_DEC_OP(ins) != DUK_OP_JUMP) { | |
1732 | continue; | |
1733 | } | |
1734 | ||
1735 | target_pc1 = i + 1 + DUK_DEC_ABC(ins) - DUK_BC_JUMP_BIAS; | |
1736 | DUK_DDD(DUK_DDDPRINT("consider jump at pc %ld; target_pc=%ld", (long) i, (long) target_pc1)); | |
1737 | DUK_ASSERT(target_pc1 >= 0); | |
1738 | DUK_ASSERT(target_pc1 < n); | |
1739 | ||
1740 | /* Note: if target_pc1 == i, we'll optimize a jump to itself. | |
1741 | * This does not need to be checked for explicitly; the case | |
1742 | * is rare and max iter breaks us out. | |
1743 | */ | |
1744 | ||
1745 | ins = bc[target_pc1].ins; | |
1746 | if (DUK_DEC_OP(ins) != DUK_OP_JUMP) { | |
1747 | continue; | |
1748 | } | |
1749 | ||
1750 | target_pc2 = target_pc1 + 1 + DUK_DEC_ABC(ins) - DUK_BC_JUMP_BIAS; | |
1751 | ||
1752 | DUK_DDD(DUK_DDDPRINT("optimizing jump at pc %ld; old target is %ld -> new target is %ld", | |
1753 | (long) i, (long) target_pc1, (long) target_pc2)); | |
1754 | ||
1755 | bc[i].ins = DUK_ENC_OP_ABC(DUK_OP_JUMP, target_pc2 - (i + 1) + DUK_BC_JUMP_BIAS); | |
1756 | ||
1757 | count_opt++; | |
1758 | } | |
1759 | ||
1760 | DUK_DD(DUK_DDPRINT("optimized %ld jumps on peephole round %ld", (long) count_opt, (long) (iter + 1))); | |
1761 | ||
1762 | if (count_opt == 0) { | |
1763 | break; | |
1764 | } | |
1765 | } | |
1766 | } | |
1767 | ||
1768 | /* | |
1769 | * Intermediate value helpers | |
1770 | */ | |
1771 | ||
1772 | #define DUK__ISREG(comp_ctx,x) (((x) & DUK__CONST_MARKER) == 0) | |
1773 | #define DUK__ISCONST(comp_ctx,x) (((x) & DUK__CONST_MARKER) != 0) | |
1774 | #define DUK__ISTEMP(comp_ctx,x) (DUK__ISREG((comp_ctx), (x)) && (duk_regconst_t) (x) >= (duk_regconst_t) ((comp_ctx)->curr_func.temp_first)) | |
1775 | #define DUK__GETTEMP(comp_ctx) ((comp_ctx)->curr_func.temp_next) | |
1776 | #define DUK__SETTEMP(comp_ctx,x) ((comp_ctx)->curr_func.temp_next = (x)) /* dangerous: must only lower (temp_max not updated) */ | |
1777 | #define DUK__SETTEMP_CHECKMAX(comp_ctx,x) duk__settemp_checkmax((comp_ctx),(x)) | |
1778 | #define DUK__ALLOCTEMP(comp_ctx) duk__alloctemp((comp_ctx)) | |
1779 | #define DUK__ALLOCTEMPS(comp_ctx,count) duk__alloctemps((comp_ctx),(count)) | |
1780 | ||
1781 | /* Flags for intermediate value coercions. A flag for using a forced reg | |
1782 | * is not needed, the forced_reg argument suffices and generates better | |
1783 | * code (it is checked as it is used). | |
1784 | */ | |
1785 | #define DUK__IVAL_FLAG_ALLOW_CONST (1 << 0) /* allow a constant to be returned */ | |
11fdf7f2 | 1786 | #define DUK__IVAL_FLAG_REQUIRE_TEMP (1 << 1) /* require a (mutable) temporary as a result (or a const if allowed) */ |
7c673cae FG |
1787 | #define DUK__IVAL_FLAG_REQUIRE_SHORT (1 << 2) /* require a short (8-bit) reg/const which fits into bytecode B/C slot */ |
1788 | ||
1789 | /* XXX: some code might benefit from DUK__SETTEMP_IFTEMP(ctx,x) */ | |
1790 | ||
11fdf7f2 TL |
1791 | #if 0 /* enable manually for dumping */ |
1792 | #define DUK__DUMP_ISPEC(compctx,ispec) do { duk__dump_ispec((compctx), (ispec)); } while (0) | |
1793 | #define DUK__DUMP_IVALUE(compctx,ivalue) do { duk__dump_ivalue((compctx), (ivalue)); } while (0) | |
1794 | ||
1795 | DUK_LOCAL void duk__dump_ispec(duk_compiler_ctx *comp_ctx, duk_ispec *x) { | |
1796 | DUK_D(DUK_DPRINT("ispec dump: t=%ld regconst=0x%08lx, valstack_idx=%ld, value=%!T", | |
1797 | (long) x->t, (unsigned long) x->regconst, (long) x->valstack_idx, | |
1798 | duk_get_tval((duk_context *) comp_ctx->thr, x->valstack_idx))); | |
1799 | } | |
1800 | DUK_LOCAL void duk__dump_ivalue(duk_compiler_ctx *comp_ctx, duk_ivalue *x) { | |
1801 | DUK_D(DUK_DPRINT("ivalue dump: t=%ld op=%ld " | |
1802 | "x1={t=%ld regconst=0x%08lx valstack_idx=%ld value=%!T} " | |
1803 | "x2={t=%ld regconst=0x%08lx valstack_idx=%ld value=%!T}", | |
1804 | (long) x->t, (long) x->op, | |
1805 | (long) x->x1.t, (unsigned long) x->x1.regconst, (long) x->x1.valstack_idx, | |
1806 | duk_get_tval((duk_context *) comp_ctx->thr, x->x1.valstack_idx), | |
1807 | (long) x->x2.t, (unsigned long) x->x2.regconst, (long) x->x2.valstack_idx, | |
1808 | duk_get_tval((duk_context *) comp_ctx->thr, x->x2.valstack_idx))); | |
1809 | } | |
1810 | #else | |
1811 | #define DUK__DUMP_ISPEC(comp_ctx,x) do {} while (0) | |
1812 | #define DUK__DUMP_IVALUE(comp_ctx,x) do {} while (0) | |
1813 | #endif | |
1814 | ||
7c673cae FG |
1815 | DUK_LOCAL void duk__copy_ispec(duk_compiler_ctx *comp_ctx, duk_ispec *src, duk_ispec *dst) { |
1816 | duk_context *ctx = (duk_context *) comp_ctx->thr; | |
1817 | ||
1818 | dst->t = src->t; | |
1819 | dst->regconst = src->regconst; | |
1820 | duk_copy(ctx, src->valstack_idx, dst->valstack_idx); | |
1821 | } | |
1822 | ||
1823 | DUK_LOCAL void duk__copy_ivalue(duk_compiler_ctx *comp_ctx, duk_ivalue *src, duk_ivalue *dst) { | |
1824 | duk_context *ctx = (duk_context *) comp_ctx->thr; | |
1825 | ||
1826 | dst->t = src->t; | |
1827 | dst->op = src->op; | |
1828 | dst->x1.t = src->x1.t; | |
1829 | dst->x1.regconst = src->x1.regconst; | |
1830 | dst->x2.t = src->x2.t; | |
1831 | dst->x2.regconst = src->x2.regconst; | |
1832 | duk_copy(ctx, src->x1.valstack_idx, dst->x1.valstack_idx); | |
1833 | duk_copy(ctx, src->x2.valstack_idx, dst->x2.valstack_idx); | |
1834 | } | |
1835 | ||
1836 | /* XXX: to util */ | |
1837 | DUK_LOCAL duk_bool_t duk__is_whole_get_int32(duk_double_t x, duk_int32_t *ival) { | |
1838 | duk_small_int_t c; | |
1839 | duk_int32_t t; | |
1840 | ||
1841 | c = DUK_FPCLASSIFY(x); | |
1842 | if (c == DUK_FP_NORMAL || (c == DUK_FP_ZERO && !DUK_SIGNBIT(x))) { | |
1843 | /* Don't allow negative zero as it will cause trouble with | |
1844 | * LDINT+LDINTX. But positive zero is OK. | |
1845 | */ | |
1846 | t = (duk_int32_t) x; | |
1847 | if ((duk_double_t) t == x) { | |
1848 | *ival = t; | |
1849 | return 1; | |
1850 | } | |
1851 | } | |
1852 | ||
1853 | return 0; | |
1854 | } | |
1855 | ||
1856 | DUK_LOCAL duk_reg_t duk__alloctemps(duk_compiler_ctx *comp_ctx, duk_small_int_t num) { | |
1857 | duk_reg_t res; | |
1858 | ||
1859 | res = comp_ctx->curr_func.temp_next; | |
1860 | comp_ctx->curr_func.temp_next += num; | |
1861 | ||
1862 | if (comp_ctx->curr_func.temp_next > DUK__MAX_TEMPS) { /* == DUK__MAX_TEMPS is OK */ | |
11fdf7f2 | 1863 | DUK_ERROR_RANGE(comp_ctx->thr, DUK_STR_TEMP_LIMIT); |
7c673cae FG |
1864 | } |
1865 | ||
1866 | /* maintain highest 'used' temporary, needed to figure out nregs of function */ | |
1867 | if (comp_ctx->curr_func.temp_next > comp_ctx->curr_func.temp_max) { | |
1868 | comp_ctx->curr_func.temp_max = comp_ctx->curr_func.temp_next; | |
1869 | } | |
1870 | ||
1871 | return res; | |
1872 | } | |
1873 | ||
1874 | DUK_LOCAL duk_reg_t duk__alloctemp(duk_compiler_ctx *comp_ctx) { | |
1875 | return duk__alloctemps(comp_ctx, 1); | |
1876 | } | |
1877 | ||
1878 | DUK_LOCAL void duk__settemp_checkmax(duk_compiler_ctx *comp_ctx, duk_reg_t temp_next) { | |
1879 | comp_ctx->curr_func.temp_next = temp_next; | |
1880 | if (temp_next > comp_ctx->curr_func.temp_max) { | |
1881 | comp_ctx->curr_func.temp_max = temp_next; | |
1882 | } | |
1883 | } | |
1884 | ||
1885 | /* get const for value at valstack top */ | |
1886 | DUK_LOCAL duk_regconst_t duk__getconst(duk_compiler_ctx *comp_ctx) { | |
1887 | duk_hthread *thr = comp_ctx->thr; | |
1888 | duk_context *ctx = (duk_context *) thr; | |
1889 | duk_compiler_func *f = &comp_ctx->curr_func; | |
1890 | duk_tval *tv1; | |
1891 | duk_int_t i, n, n_check; | |
1892 | ||
1893 | n = (duk_int_t) duk_get_length(ctx, f->consts_idx); | |
1894 | ||
11fdf7f2 | 1895 | tv1 = DUK_GET_TVAL_NEGIDX(ctx, -1); |
7c673cae FG |
1896 | DUK_ASSERT(tv1 != NULL); |
1897 | ||
1898 | #if defined(DUK_USE_FASTINT) | |
1899 | /* Explicit check for fastint downgrade. */ | |
1900 | DUK_TVAL_CHKFAST_INPLACE(tv1); | |
1901 | #endif | |
1902 | ||
1903 | /* Sanity workaround for handling functions with a large number of | |
1904 | * constants at least somewhat reasonably. Otherwise checking whether | |
1905 | * we already have the constant would grow very slow (as it is O(N^2)). | |
1906 | */ | |
1907 | n_check = (n > DUK__GETCONST_MAX_CONSTS_CHECK ? DUK__GETCONST_MAX_CONSTS_CHECK : n); | |
1908 | for (i = 0; i < n_check; i++) { | |
1909 | duk_tval *tv2 = DUK_HOBJECT_A_GET_VALUE_PTR(thr->heap, f->h_consts, i); | |
1910 | ||
1911 | /* Strict equality is NOT enough, because we cannot use the same | |
1912 | * constant for e.g. +0 and -0. | |
1913 | */ | |
1914 | if (duk_js_samevalue(tv1, tv2)) { | |
1915 | DUK_DDD(DUK_DDDPRINT("reused existing constant for %!T -> const index %ld", | |
1916 | (duk_tval *) tv1, (long) i)); | |
1917 | duk_pop(ctx); | |
1918 | return (duk_regconst_t) (i | DUK__CONST_MARKER); | |
1919 | } | |
1920 | } | |
1921 | ||
1922 | if (n > DUK__MAX_CONSTS) { | |
11fdf7f2 | 1923 | DUK_ERROR_RANGE(comp_ctx->thr, DUK_STR_CONST_LIMIT); |
7c673cae FG |
1924 | } |
1925 | ||
1926 | DUK_DDD(DUK_DDDPRINT("allocating new constant for %!T -> const index %ld", | |
1927 | (duk_tval *) tv1, (long) n)); | |
1928 | (void) duk_put_prop_index(ctx, f->consts_idx, n); /* invalidates tv1, tv2 */ | |
1929 | return (duk_regconst_t) (n | DUK__CONST_MARKER); | |
1930 | } | |
1931 | ||
1932 | /* Get the value represented by an duk_ispec to a register or constant. | |
1933 | * The caller can control the result by indicating whether or not: | |
1934 | * | |
1935 | * (1) a constant is allowed (sometimes the caller needs the result to | |
1936 | * be in a register) | |
1937 | * | |
1938 | * (2) a temporary register is required (usually when caller requires | |
1939 | * the register to be safely mutable; normally either a bound | |
1940 | * register or a temporary register are both OK) | |
1941 | * | |
1942 | * (3) a forced register target needs to be used | |
1943 | * | |
1944 | * Bytecode may be emitted to generate the necessary value. The return | |
1945 | * value is either a register or a constant. | |
1946 | */ | |
1947 | ||
1948 | DUK_LOCAL | |
1949 | duk_regconst_t duk__ispec_toregconst_raw(duk_compiler_ctx *comp_ctx, | |
1950 | duk_ispec *x, | |
1951 | duk_reg_t forced_reg, | |
1952 | duk_small_uint_t flags) { | |
1953 | duk_hthread *thr = comp_ctx->thr; | |
1954 | duk_context *ctx = (duk_context *) thr; | |
1955 | ||
1956 | DUK_DDD(DUK_DDDPRINT("duk__ispec_toregconst_raw(): x={%ld:%ld:%!T}, " | |
1957 | "forced_reg=%ld, flags 0x%08lx: allow_const=%ld require_temp=%ld require_short=%ld", | |
1958 | (long) x->t, | |
1959 | (long) x->regconst, | |
1960 | (duk_tval *) duk_get_tval(ctx, x->valstack_idx), | |
1961 | (long) forced_reg, | |
1962 | (unsigned long) flags, | |
1963 | (long) ((flags & DUK__IVAL_FLAG_ALLOW_CONST) ? 1 : 0), | |
1964 | (long) ((flags & DUK__IVAL_FLAG_REQUIRE_TEMP) ? 1 : 0), | |
1965 | (long) ((flags & DUK__IVAL_FLAG_REQUIRE_SHORT) ? 1 : 0))); | |
1966 | ||
1967 | switch (x->t) { | |
1968 | case DUK_ISPEC_VALUE: { | |
1969 | duk_tval *tv; | |
1970 | ||
11fdf7f2 | 1971 | tv = DUK_GET_TVAL_POSIDX(ctx, x->valstack_idx); |
7c673cae FG |
1972 | DUK_ASSERT(tv != NULL); |
1973 | ||
1974 | switch (DUK_TVAL_GET_TAG(tv)) { | |
1975 | case DUK_TAG_UNDEFINED: { | |
1976 | /* Note: although there is no 'undefined' literal, undefined | |
1977 | * values can occur during compilation as a result of e.g. | |
1978 | * the 'void' operator. | |
1979 | */ | |
1980 | duk_reg_t dest = (forced_reg >= 0 ? forced_reg : DUK__ALLOCTEMP(comp_ctx)); | |
1981 | duk__emit_extraop_bc(comp_ctx, DUK_EXTRAOP_LDUNDEF, (duk_regconst_t) dest); | |
1982 | return (duk_regconst_t) dest; | |
1983 | } | |
1984 | case DUK_TAG_NULL: { | |
1985 | duk_reg_t dest = (forced_reg >= 0 ? forced_reg : DUK__ALLOCTEMP(comp_ctx)); | |
1986 | duk__emit_extraop_bc(comp_ctx, DUK_EXTRAOP_LDNULL, (duk_regconst_t) dest); | |
1987 | return (duk_regconst_t) dest; | |
1988 | } | |
1989 | case DUK_TAG_BOOLEAN: { | |
1990 | duk_reg_t dest = (forced_reg >= 0 ? forced_reg : DUK__ALLOCTEMP(comp_ctx)); | |
1991 | duk__emit_extraop_bc(comp_ctx, | |
1992 | (DUK_TVAL_GET_BOOLEAN(tv) ? DUK_EXTRAOP_LDTRUE : DUK_EXTRAOP_LDFALSE), | |
1993 | (duk_regconst_t) dest); | |
1994 | return (duk_regconst_t) dest; | |
1995 | } | |
1996 | case DUK_TAG_POINTER: { | |
1997 | DUK_UNREACHABLE(); | |
1998 | break; | |
1999 | } | |
2000 | case DUK_TAG_STRING: { | |
2001 | duk_hstring *h; | |
2002 | duk_reg_t dest; | |
2003 | duk_regconst_t constidx; | |
2004 | ||
2005 | h = DUK_TVAL_GET_STRING(tv); | |
2006 | DUK_UNREF(h); | |
2007 | DUK_ASSERT(h != NULL); | |
2008 | ||
2009 | #if 0 /* XXX: to be implemented? */ | |
2010 | /* Use special opcodes to load short strings */ | |
2011 | if (DUK_HSTRING_GET_BYTELEN(h) <= 2) { | |
2012 | /* Encode into a single opcode (18 bits can encode 1-2 bytes + length indicator) */ | |
2013 | } else if (DUK_HSTRING_GET_BYTELEN(h) <= 6) { | |
2014 | /* Encode into a double constant (53 bits can encode 6*8 = 48 bits + 3-bit length */ | |
2015 | } | |
2016 | #endif | |
2017 | duk_dup(ctx, x->valstack_idx); | |
2018 | constidx = duk__getconst(comp_ctx); | |
2019 | ||
2020 | if (flags & DUK__IVAL_FLAG_ALLOW_CONST) { | |
2021 | return constidx; | |
2022 | } | |
2023 | ||
2024 | dest = (forced_reg >= 0 ? forced_reg : DUK__ALLOCTEMP(comp_ctx)); | |
2025 | duk__emit_a_bc(comp_ctx, DUK_OP_LDCONST, (duk_regconst_t) dest, constidx); | |
2026 | return (duk_regconst_t) dest; | |
2027 | } | |
2028 | case DUK_TAG_OBJECT: { | |
2029 | DUK_UNREACHABLE(); | |
2030 | break; | |
2031 | } | |
2032 | case DUK_TAG_BUFFER: { | |
2033 | DUK_UNREACHABLE(); | |
2034 | break; | |
2035 | } | |
2036 | case DUK_TAG_LIGHTFUNC: { | |
2037 | DUK_UNREACHABLE(); | |
2038 | break; | |
2039 | } | |
2040 | #if defined(DUK_USE_FASTINT) | |
2041 | case DUK_TAG_FASTINT: | |
2042 | #endif | |
2043 | default: { | |
2044 | /* number */ | |
2045 | duk_reg_t dest; | |
2046 | duk_regconst_t constidx; | |
2047 | duk_double_t dval; | |
2048 | duk_int32_t ival; | |
2049 | ||
11fdf7f2 | 2050 | DUK_ASSERT(!DUK_TVAL_IS_UNUSED(tv)); |
7c673cae FG |
2051 | DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv)); |
2052 | dval = DUK_TVAL_GET_NUMBER(tv); | |
2053 | ||
2054 | if (!(flags & DUK__IVAL_FLAG_ALLOW_CONST)) { | |
2055 | /* A number can be loaded either through a constant, using | |
2056 | * LDINT, or using LDINT+LDINTX. LDINT is always a size win, | |
2057 | * LDINT+LDINTX is not if the constant is used multiple times. | |
2058 | * Currently always prefer LDINT+LDINTX over a double constant. | |
2059 | */ | |
2060 | ||
2061 | if (duk__is_whole_get_int32(dval, &ival)) { | |
2062 | dest = (forced_reg >= 0 ? forced_reg : DUK__ALLOCTEMP(comp_ctx)); | |
2063 | duk__emit_load_int32(comp_ctx, dest, ival); | |
2064 | return (duk_regconst_t) dest; | |
2065 | } | |
2066 | } | |
2067 | ||
2068 | duk_dup(ctx, x->valstack_idx); | |
2069 | constidx = duk__getconst(comp_ctx); | |
2070 | ||
2071 | if (flags & DUK__IVAL_FLAG_ALLOW_CONST) { | |
2072 | return constidx; | |
2073 | } else { | |
2074 | dest = (forced_reg >= 0 ? forced_reg : DUK__ALLOCTEMP(comp_ctx)); | |
2075 | duk__emit_a_bc(comp_ctx, DUK_OP_LDCONST, (duk_regconst_t) dest, constidx); | |
2076 | return (duk_regconst_t) dest; | |
2077 | } | |
2078 | } | |
2079 | } /* end switch */ | |
2080 | } | |
2081 | case DUK_ISPEC_REGCONST: { | |
11fdf7f2 TL |
2082 | if (forced_reg >= 0) { |
2083 | if (x->regconst & DUK__CONST_MARKER) { | |
2084 | duk__emit_a_bc(comp_ctx, DUK_OP_LDCONST, forced_reg, x->regconst); | |
2085 | } else if (x->regconst != (duk_regconst_t) forced_reg) { | |
2086 | duk__emit_a_bc(comp_ctx, DUK_OP_LDREG, forced_reg, x->regconst); | |
7c673cae | 2087 | } else { |
11fdf7f2 | 2088 | ; /* already in correct reg */ |
7c673cae | 2089 | } |
11fdf7f2 | 2090 | return (duk_regconst_t) forced_reg; |
7c673cae | 2091 | } |
11fdf7f2 TL |
2092 | |
2093 | DUK_ASSERT(forced_reg < 0); | |
2094 | if (x->regconst & DUK__CONST_MARKER) { | |
2095 | if (!(flags & DUK__IVAL_FLAG_ALLOW_CONST)) { | |
2096 | duk_reg_t dest = DUK__ALLOCTEMP(comp_ctx); | |
2097 | duk__emit_a_bc(comp_ctx, DUK_OP_LDCONST, (duk_regconst_t) dest, x->regconst); | |
2098 | return (duk_regconst_t) dest; | |
2099 | } | |
2100 | return x->regconst; | |
2101 | } | |
2102 | ||
2103 | DUK_ASSERT(forced_reg < 0 && !(x->regconst & DUK__CONST_MARKER)); | |
2104 | if ((flags & DUK__IVAL_FLAG_REQUIRE_TEMP) && !DUK__ISTEMP(comp_ctx, x->regconst)) { | |
2105 | duk_reg_t dest = DUK__ALLOCTEMP(comp_ctx); | |
2106 | duk__emit_a_bc(comp_ctx, DUK_OP_LDREG, (duk_regconst_t) dest, x->regconst); | |
2107 | return (duk_regconst_t) dest; | |
2108 | } | |
2109 | return x->regconst; | |
7c673cae FG |
2110 | } |
2111 | default: { | |
2112 | break; | |
2113 | } | |
2114 | } | |
2115 | ||
11fdf7f2 | 2116 | DUK_ERROR_INTERNAL_DEFMSG(thr); |
7c673cae FG |
2117 | return 0; |
2118 | } | |
2119 | ||
2120 | DUK_LOCAL void duk__ispec_toforcedreg(duk_compiler_ctx *comp_ctx, duk_ispec *x, duk_reg_t forced_reg) { | |
2121 | DUK_ASSERT(forced_reg >= 0); | |
2122 | (void) duk__ispec_toregconst_raw(comp_ctx, x, forced_reg, 0 /*flags*/); | |
2123 | } | |
2124 | ||
2125 | /* Coerce an duk_ivalue to a 'plain' value by generating the necessary | |
2126 | * arithmetic operations, property access, or variable access bytecode. | |
2127 | * The duk_ivalue argument ('x') is converted into a plain value as a | |
2128 | * side effect. | |
2129 | */ | |
2130 | DUK_LOCAL void duk__ivalue_toplain_raw(duk_compiler_ctx *comp_ctx, duk_ivalue *x, duk_reg_t forced_reg) { | |
2131 | duk_hthread *thr = comp_ctx->thr; | |
2132 | duk_context *ctx = (duk_context *) thr; | |
2133 | ||
2134 | DUK_DDD(DUK_DDDPRINT("duk__ivalue_toplain_raw(): x={t=%ld,op=%ld,x1={%ld:%ld:%!T},x2={%ld:%ld:%!T}}, " | |
2135 | "forced_reg=%ld", | |
2136 | (long) x->t, (long) x->op, | |
2137 | (long) x->x1.t, (long) x->x1.regconst, | |
2138 | (duk_tval *) duk_get_tval(ctx, x->x1.valstack_idx), | |
2139 | (long) x->x2.t, (long) x->x2.regconst, | |
2140 | (duk_tval *) duk_get_tval(ctx, x->x2.valstack_idx), | |
2141 | (long) forced_reg)); | |
2142 | ||
2143 | switch (x->t) { | |
2144 | case DUK_IVAL_PLAIN: { | |
2145 | return; | |
2146 | } | |
2147 | /* XXX: support unary arithmetic ivalues (useful?) */ | |
2148 | case DUK_IVAL_ARITH: | |
2149 | case DUK_IVAL_ARITH_EXTRAOP: { | |
2150 | duk_regconst_t arg1; | |
2151 | duk_regconst_t arg2; | |
2152 | duk_reg_t dest; | |
2153 | duk_tval *tv1; | |
2154 | duk_tval *tv2; | |
2155 | ||
2156 | DUK_DDD(DUK_DDDPRINT("arith to plain conversion")); | |
2157 | ||
2158 | /* inline arithmetic check for constant values */ | |
2159 | /* XXX: use the exactly same arithmetic function here as in executor */ | |
2160 | if (x->x1.t == DUK_ISPEC_VALUE && x->x2.t == DUK_ISPEC_VALUE && x->t == DUK_IVAL_ARITH) { | |
11fdf7f2 TL |
2161 | tv1 = DUK_GET_TVAL_POSIDX(ctx, x->x1.valstack_idx); |
2162 | tv2 = DUK_GET_TVAL_POSIDX(ctx, x->x2.valstack_idx); | |
7c673cae FG |
2163 | DUK_ASSERT(tv1 != NULL); |
2164 | DUK_ASSERT(tv2 != NULL); | |
2165 | ||
2166 | DUK_DDD(DUK_DDDPRINT("arith: tv1=%!T, tv2=%!T", | |
2167 | (duk_tval *) tv1, | |
2168 | (duk_tval *) tv2)); | |
2169 | ||
2170 | if (DUK_TVAL_IS_NUMBER(tv1) && DUK_TVAL_IS_NUMBER(tv2)) { | |
2171 | duk_double_t d1 = DUK_TVAL_GET_NUMBER(tv1); | |
2172 | duk_double_t d2 = DUK_TVAL_GET_NUMBER(tv2); | |
2173 | duk_double_t d3; | |
2174 | duk_bool_t accept = 1; | |
2175 | ||
2176 | DUK_DDD(DUK_DDDPRINT("arith inline check: d1=%lf, d2=%lf, op=%ld", | |
2177 | (double) d1, (double) d2, (long) x->op)); | |
2178 | switch (x->op) { | |
2179 | case DUK_OP_ADD: d3 = d1 + d2; break; | |
2180 | case DUK_OP_SUB: d3 = d1 - d2; break; | |
2181 | case DUK_OP_MUL: d3 = d1 * d2; break; | |
2182 | case DUK_OP_DIV: d3 = d1 / d2; break; | |
2183 | default: accept = 0; break; | |
2184 | } | |
2185 | ||
2186 | if (accept) { | |
2187 | duk_double_union du; | |
2188 | du.d = d3; | |
2189 | DUK_DBLUNION_NORMALIZE_NAN_CHECK(&du); | |
2190 | d3 = du.d; | |
2191 | ||
2192 | x->t = DUK_IVAL_PLAIN; | |
2193 | DUK_ASSERT(x->x1.t == DUK_ISPEC_VALUE); | |
2194 | DUK_TVAL_SET_NUMBER(tv1, d3); /* old value is number: no refcount */ | |
2195 | return; | |
2196 | } | |
2197 | } else if (x->op == DUK_OP_ADD && DUK_TVAL_IS_STRING(tv1) && DUK_TVAL_IS_STRING(tv2)) { | |
2198 | /* inline string concatenation */ | |
2199 | duk_dup(ctx, x->x1.valstack_idx); | |
2200 | duk_dup(ctx, x->x2.valstack_idx); | |
2201 | duk_concat(ctx, 2); | |
2202 | duk_replace(ctx, x->x1.valstack_idx); | |
2203 | x->t = DUK_IVAL_PLAIN; | |
2204 | DUK_ASSERT(x->x1.t == DUK_ISPEC_VALUE); | |
2205 | return; | |
2206 | } | |
2207 | } | |
2208 | ||
2209 | arg1 = duk__ispec_toregconst_raw(comp_ctx, &x->x1, -1, DUK__IVAL_FLAG_ALLOW_CONST | DUK__IVAL_FLAG_REQUIRE_SHORT /*flags*/); | |
2210 | arg2 = duk__ispec_toregconst_raw(comp_ctx, &x->x2, -1, DUK__IVAL_FLAG_ALLOW_CONST | DUK__IVAL_FLAG_REQUIRE_SHORT /*flags*/); | |
2211 | ||
2212 | /* If forced reg, use it as destination. Otherwise try to | |
2213 | * use either coerced ispec if it is a temporary. | |
2214 | * | |
2215 | * When using extraops, avoid reusing arg2 as dest because that | |
2216 | * would lead to an LDREG shuffle below. We still can't guarantee | |
2217 | * dest != arg2 because we may have a forced_reg. | |
2218 | */ | |
2219 | if (forced_reg >= 0) { | |
2220 | dest = forced_reg; | |
2221 | } else if (DUK__ISTEMP(comp_ctx, arg1)) { | |
2222 | dest = (duk_reg_t) arg1; | |
2223 | } else if (DUK__ISTEMP(comp_ctx, arg2) && x->t != DUK_IVAL_ARITH_EXTRAOP) { | |
2224 | dest = (duk_reg_t) arg2; | |
2225 | } else { | |
2226 | dest = DUK__ALLOCTEMP(comp_ctx); | |
2227 | } | |
2228 | ||
2229 | /* Extraop arithmetic opcodes must have destination same as | |
2230 | * first source. If second source matches destination we need | |
2231 | * a temporary register to avoid clobbering the second source. | |
2232 | * | |
2233 | * XXX: change calling code to avoid this situation in most cases. | |
2234 | */ | |
2235 | ||
2236 | if (x->t == DUK_IVAL_ARITH_EXTRAOP) { | |
2237 | if (!(DUK__ISREG(comp_ctx, arg1) && (duk_reg_t) arg1 == dest)) { | |
2238 | if (DUK__ISREG(comp_ctx, arg2) && (duk_reg_t) arg2 == dest) { | |
2239 | /* arg2 would be clobbered so reassign it to a temp. */ | |
2240 | duk_reg_t tempreg; | |
2241 | tempreg = DUK__ALLOCTEMP(comp_ctx); | |
2242 | duk__emit_a_bc(comp_ctx, DUK_OP_LDREG, tempreg, arg2); | |
2243 | arg2 = tempreg; | |
2244 | } | |
2245 | ||
2246 | if (DUK__ISREG(comp_ctx, arg1)) { | |
2247 | duk__emit_a_bc(comp_ctx, DUK_OP_LDREG, dest, arg1); | |
2248 | } else { | |
2249 | DUK_ASSERT(DUK__ISCONST(comp_ctx, arg1)); | |
2250 | duk__emit_a_bc(comp_ctx, DUK_OP_LDCONST, dest, arg1); | |
2251 | } | |
2252 | } | |
2253 | ||
2254 | /* Note: special DUK__EMIT_FLAG_B_IS_TARGETSOURCE | |
2255 | * used to indicate that B is both a source and a | |
2256 | * target register. When shuffled, it needs to be | |
2257 | * both input and output shuffled. | |
2258 | */ | |
2259 | DUK_ASSERT(DUK__ISREG(comp_ctx, dest)); | |
2260 | duk__emit_extraop_b_c(comp_ctx, | |
2261 | x->op | DUK__EMIT_FLAG_B_IS_TARGET | | |
2262 | DUK__EMIT_FLAG_B_IS_TARGETSOURCE, | |
2263 | (duk_regconst_t) dest, | |
2264 | (duk_regconst_t) arg2); | |
2265 | ||
2266 | } else { | |
2267 | DUK_ASSERT(DUK__ISREG(comp_ctx, dest)); | |
2268 | duk__emit_a_b_c(comp_ctx, x->op, (duk_regconst_t) dest, arg1, arg2); | |
2269 | } | |
2270 | ||
2271 | x->t = DUK_IVAL_PLAIN; | |
2272 | x->x1.t = DUK_ISPEC_REGCONST; | |
2273 | x->x1.regconst = (duk_regconst_t) dest; | |
2274 | return; | |
2275 | } | |
2276 | case DUK_IVAL_PROP: { | |
2277 | /* XXX: very similar to DUK_IVAL_ARITH - merge? */ | |
2278 | duk_regconst_t arg1; | |
2279 | duk_regconst_t arg2; | |
2280 | duk_reg_t dest; | |
2281 | ||
2282 | /* Need a short reg/const, does not have to be a mutable temp. */ | |
2283 | arg1 = duk__ispec_toregconst_raw(comp_ctx, &x->x1, -1, DUK__IVAL_FLAG_ALLOW_CONST | DUK__IVAL_FLAG_REQUIRE_SHORT /*flags*/); | |
2284 | arg2 = duk__ispec_toregconst_raw(comp_ctx, &x->x2, -1, DUK__IVAL_FLAG_ALLOW_CONST | DUK__IVAL_FLAG_REQUIRE_SHORT /*flags*/); | |
2285 | ||
2286 | /* Pick a destination register. If either base value or key | |
2287 | * happens to be a temp value, reuse it as the destination. | |
2288 | * | |
2289 | * XXX: The temp must be a "mutable" one, i.e. such that no | |
2290 | * other expression is using it anymore. Here this should be | |
2291 | * the case because the value of a property access expression | |
2292 | * is neither the base nor the key, but the lookup result. | |
2293 | */ | |
2294 | ||
2295 | if (forced_reg >= 0) { | |
2296 | dest = forced_reg; | |
2297 | } else if (DUK__ISTEMP(comp_ctx, arg1)) { | |
2298 | dest = (duk_reg_t) arg1; | |
2299 | } else if (DUK__ISTEMP(comp_ctx, arg2)) { | |
2300 | dest = (duk_reg_t) arg2; | |
2301 | } else { | |
2302 | dest = DUK__ALLOCTEMP(comp_ctx); | |
2303 | } | |
2304 | ||
2305 | duk__emit_a_b_c(comp_ctx, DUK_OP_GETPROP, (duk_regconst_t) dest, arg1, arg2); | |
2306 | ||
2307 | x->t = DUK_IVAL_PLAIN; | |
2308 | x->x1.t = DUK_ISPEC_REGCONST; | |
2309 | x->x1.regconst = (duk_regconst_t) dest; | |
2310 | return; | |
2311 | } | |
2312 | case DUK_IVAL_VAR: { | |
2313 | /* x1 must be a string */ | |
2314 | duk_reg_t dest; | |
2315 | duk_reg_t reg_varbind; | |
2316 | duk_regconst_t rc_varname; | |
2317 | ||
2318 | DUK_ASSERT(x->x1.t == DUK_ISPEC_VALUE); | |
2319 | ||
2320 | duk_dup(ctx, x->x1.valstack_idx); | |
2321 | if (duk__lookup_lhs(comp_ctx, ®_varbind, &rc_varname)) { | |
2322 | x->t = DUK_IVAL_PLAIN; | |
2323 | x->x1.t = DUK_ISPEC_REGCONST; | |
2324 | x->x1.regconst = (duk_regconst_t) reg_varbind; | |
2325 | } else { | |
2326 | dest = (forced_reg >= 0 ? forced_reg : DUK__ALLOCTEMP(comp_ctx)); | |
2327 | duk__emit_a_bc(comp_ctx, DUK_OP_GETVAR, (duk_regconst_t) dest, rc_varname); | |
2328 | x->t = DUK_IVAL_PLAIN; | |
2329 | x->x1.t = DUK_ISPEC_REGCONST; | |
2330 | x->x1.regconst = (duk_regconst_t) dest; | |
2331 | } | |
2332 | return; | |
2333 | } | |
2334 | case DUK_IVAL_NONE: | |
2335 | default: { | |
11fdf7f2 | 2336 | DUK_D(DUK_DPRINT("invalid ivalue type: %ld", (long) x->t)); |
7c673cae FG |
2337 | break; |
2338 | } | |
2339 | } | |
2340 | ||
11fdf7f2 | 2341 | DUK_ERROR_INTERNAL_DEFMSG(thr); |
7c673cae FG |
2342 | return; |
2343 | } | |
2344 | ||
2345 | /* evaluate to plain value, no forced register (temp/bound reg both ok) */ | |
2346 | DUK_LOCAL void duk__ivalue_toplain(duk_compiler_ctx *comp_ctx, duk_ivalue *x) { | |
2347 | duk__ivalue_toplain_raw(comp_ctx, x, -1 /*forced_reg*/); | |
2348 | } | |
2349 | ||
2350 | /* evaluate to final form (e.g. coerce GETPROP to code), throw away temp */ | |
2351 | DUK_LOCAL void duk__ivalue_toplain_ignore(duk_compiler_ctx *comp_ctx, duk_ivalue *x) { | |
2352 | duk_reg_t temp; | |
2353 | ||
2354 | /* If duk__ivalue_toplain_raw() allocates a temp, forget it and | |
2355 | * restore next temp state. | |
2356 | */ | |
2357 | temp = DUK__GETTEMP(comp_ctx); | |
2358 | duk__ivalue_toplain_raw(comp_ctx, x, -1 /*forced_reg*/); | |
2359 | DUK__SETTEMP(comp_ctx, temp); | |
2360 | } | |
2361 | ||
2362 | /* Coerce an duk_ivalue to a register or constant; result register may | |
2363 | * be a temp or a bound register. | |
2364 | * | |
2365 | * The duk_ivalue argument ('x') is converted into a regconst as a | |
2366 | * side effect. | |
2367 | */ | |
2368 | DUK_LOCAL | |
2369 | duk_regconst_t duk__ivalue_toregconst_raw(duk_compiler_ctx *comp_ctx, | |
2370 | duk_ivalue *x, | |
2371 | duk_reg_t forced_reg, | |
2372 | duk_small_uint_t flags) { | |
2373 | duk_hthread *thr = comp_ctx->thr; | |
2374 | duk_context *ctx = (duk_context *) thr; | |
2375 | duk_regconst_t reg; | |
2376 | DUK_UNREF(thr); | |
2377 | DUK_UNREF(ctx); | |
2378 | ||
2379 | DUK_DDD(DUK_DDDPRINT("duk__ivalue_toregconst_raw(): x={t=%ld,op=%ld,x1={%ld:%ld:%!T},x2={%ld:%ld:%!T}}, " | |
2380 | "forced_reg=%ld, flags 0x%08lx: allow_const=%ld require_temp=%ld require_short=%ld", | |
2381 | (long) x->t, (long) x->op, | |
2382 | (long) x->x1.t, (long) x->x1.regconst, | |
2383 | (duk_tval *) duk_get_tval(ctx, x->x1.valstack_idx), | |
2384 | (long) x->x2.t, (long) x->x2.regconst, | |
2385 | (duk_tval *) duk_get_tval(ctx, x->x2.valstack_idx), | |
2386 | (long) forced_reg, | |
2387 | (unsigned long) flags, | |
2388 | (long) ((flags & DUK__IVAL_FLAG_ALLOW_CONST) ? 1 : 0), | |
2389 | (long) ((flags & DUK__IVAL_FLAG_REQUIRE_TEMP) ? 1 : 0), | |
2390 | (long) ((flags & DUK__IVAL_FLAG_REQUIRE_SHORT) ? 1 : 0))); | |
2391 | ||
2392 | /* first coerce to a plain value */ | |
2393 | duk__ivalue_toplain_raw(comp_ctx, x, forced_reg); | |
2394 | DUK_ASSERT(x->t == DUK_IVAL_PLAIN); | |
2395 | ||
2396 | /* then to a register */ | |
2397 | reg = duk__ispec_toregconst_raw(comp_ctx, &x->x1, forced_reg, flags); | |
2398 | x->x1.t = DUK_ISPEC_REGCONST; | |
2399 | x->x1.regconst = reg; | |
2400 | ||
2401 | return reg; | |
2402 | } | |
2403 | ||
2404 | DUK_LOCAL duk_reg_t duk__ivalue_toreg(duk_compiler_ctx *comp_ctx, duk_ivalue *x) { | |
2405 | return duk__ivalue_toregconst_raw(comp_ctx, x, -1, 0 /*flags*/); | |
2406 | } | |
2407 | ||
2408 | #if 0 /* unused */ | |
11fdf7f2 | 2409 | DUK_LOCAL duk_reg_t duk__ivalue_totemp(duk_compiler_ctx *comp_ctx, duk_ivalue *x) { |
7c673cae FG |
2410 | return duk__ivalue_toregconst_raw(comp_ctx, x, -1, DUK__IVAL_FLAG_REQUIRE_TEMP /*flags*/); |
2411 | } | |
2412 | #endif | |
2413 | ||
2414 | DUK_LOCAL void duk__ivalue_toforcedreg(duk_compiler_ctx *comp_ctx, duk_ivalue *x, duk_int_t forced_reg) { | |
2415 | DUK_ASSERT(forced_reg >= 0); | |
2416 | (void) duk__ivalue_toregconst_raw(comp_ctx, x, forced_reg, 0 /*flags*/); | |
2417 | } | |
2418 | ||
2419 | DUK_LOCAL duk_regconst_t duk__ivalue_toregconst(duk_compiler_ctx *comp_ctx, duk_ivalue *x) { | |
2420 | return duk__ivalue_toregconst_raw(comp_ctx, x, -1, DUK__IVAL_FLAG_ALLOW_CONST /*flags*/); | |
2421 | } | |
2422 | ||
11fdf7f2 TL |
2423 | DUK_LOCAL duk_regconst_t duk__ivalue_totempconst(duk_compiler_ctx *comp_ctx, duk_ivalue *x) { |
2424 | return duk__ivalue_toregconst_raw(comp_ctx, x, -1, DUK__IVAL_FLAG_ALLOW_CONST | DUK__IVAL_FLAG_REQUIRE_TEMP /*flags*/); | |
2425 | } | |
2426 | ||
7c673cae FG |
2427 | /* The issues below can be solved with better flags */ |
2428 | ||
2429 | /* XXX: many operations actually want toforcedtemp() -- brand new temp? */ | |
2430 | /* XXX: need a toplain_ignore() which will only coerce a value to a temp | |
2431 | * register if it might have a side effect. Side-effect free values do not | |
2432 | * need to be coerced. | |
2433 | */ | |
2434 | ||
2435 | /* | |
2436 | * Identifier handling | |
2437 | */ | |
2438 | ||
2439 | DUK_LOCAL duk_reg_t duk__lookup_active_register_binding(duk_compiler_ctx *comp_ctx) { | |
2440 | duk_hthread *thr = comp_ctx->thr; | |
2441 | duk_context *ctx = (duk_context *) thr; | |
2442 | duk_hstring *h_varname; | |
2443 | duk_reg_t ret; | |
2444 | ||
2445 | DUK_DDD(DUK_DDDPRINT("resolving identifier reference to '%!T'", | |
2446 | (duk_tval *) duk_get_tval(ctx, -1))); | |
2447 | ||
2448 | /* | |
2449 | * Special name handling | |
2450 | */ | |
2451 | ||
2452 | h_varname = duk_get_hstring(ctx, -1); | |
2453 | DUK_ASSERT(h_varname != NULL); | |
2454 | ||
2455 | if (h_varname == DUK_HTHREAD_STRING_LC_ARGUMENTS(thr)) { | |
2456 | DUK_DDD(DUK_DDDPRINT("flagging function as accessing 'arguments'")); | |
2457 | comp_ctx->curr_func.id_access_arguments = 1; | |
2458 | } | |
2459 | ||
2460 | /* | |
2461 | * Inside one or more 'with' statements fall back to slow path always. | |
2462 | * (See e.g. test-stmt-with.js.) | |
2463 | */ | |
2464 | ||
2465 | if (comp_ctx->curr_func.with_depth > 0) { | |
2466 | DUK_DDD(DUK_DDDPRINT("identifier lookup inside a 'with' -> fall back to slow path")); | |
2467 | goto slow_path; | |
2468 | } | |
2469 | ||
2470 | /* | |
2471 | * Any catch bindings ("catch (e)") also affect identifier binding. | |
2472 | * | |
2473 | * Currently, the varmap is modified for the duration of the catch | |
2474 | * clause to ensure any identifier accesses with the catch variable | |
2475 | * name will use slow path. | |
2476 | */ | |
2477 | ||
2478 | duk_get_prop(ctx, comp_ctx->curr_func.varmap_idx); | |
2479 | if (duk_is_number(ctx, -1)) { | |
2480 | ret = duk_to_int(ctx, -1); | |
2481 | duk_pop(ctx); | |
2482 | } else { | |
2483 | duk_pop(ctx); | |
2484 | goto slow_path; | |
2485 | } | |
2486 | ||
2487 | DUK_DDD(DUK_DDDPRINT("identifier lookup -> reg %ld", (long) ret)); | |
2488 | return ret; | |
2489 | ||
2490 | slow_path: | |
2491 | DUK_DDD(DUK_DDDPRINT("identifier lookup -> slow path")); | |
2492 | ||
2493 | comp_ctx->curr_func.id_access_slow = 1; | |
2494 | return (duk_reg_t) -1; | |
2495 | } | |
2496 | ||
2497 | /* Lookup an identifier name in the current varmap, indicating whether the | |
2498 | * identifier is register-bound and if not, allocating a constant for the | |
2499 | * identifier name. Returns 1 if register-bound, 0 otherwise. Caller can | |
2500 | * also check (out_reg_varbind >= 0) to check whether or not identifier is | |
2501 | * register bound. The caller must NOT use out_rc_varname at all unless | |
2502 | * return code is 0 or out_reg_varbind is < 0; this is becuase out_rc_varname | |
2503 | * is unsigned and doesn't have a "unused" / none value. | |
2504 | */ | |
2505 | DUK_LOCAL duk_bool_t duk__lookup_lhs(duk_compiler_ctx *comp_ctx, duk_reg_t *out_reg_varbind, duk_regconst_t *out_rc_varname) { | |
2506 | duk_hthread *thr = comp_ctx->thr; | |
2507 | duk_context *ctx = (duk_context *) thr; | |
2508 | duk_reg_t reg_varbind; | |
2509 | duk_regconst_t rc_varname; | |
2510 | ||
2511 | /* [ ... varname ] */ | |
2512 | ||
2513 | duk_dup_top(ctx); | |
2514 | reg_varbind = duk__lookup_active_register_binding(comp_ctx); | |
2515 | ||
2516 | if (reg_varbind >= 0) { | |
2517 | *out_reg_varbind = reg_varbind; | |
2518 | *out_rc_varname = 0; /* duk_regconst_t is unsigned, so use 0 as dummy value (ignored by caller) */ | |
2519 | duk_pop(ctx); | |
2520 | return 1; | |
2521 | } else { | |
2522 | rc_varname = duk__getconst(comp_ctx); | |
2523 | *out_reg_varbind = -1; | |
2524 | *out_rc_varname = rc_varname; | |
2525 | return 0; | |
2526 | } | |
2527 | } | |
2528 | ||
2529 | /* | |
2530 | * Label handling | |
2531 | * | |
2532 | * Labels are initially added with flags prohibiting both break and continue. | |
2533 | * When the statement type is finally uncovered (after potentially multiple | |
2534 | * labels), all the labels are updated to allow/prohibit break and continue. | |
2535 | */ | |
2536 | ||
2537 | DUK_LOCAL void duk__add_label(duk_compiler_ctx *comp_ctx, duk_hstring *h_label, duk_int_t pc_label, duk_int_t label_id) { | |
2538 | duk_hthread *thr = comp_ctx->thr; | |
2539 | duk_context *ctx = (duk_context *) thr; | |
2540 | duk_size_t n; | |
2541 | duk_size_t new_size; | |
2542 | duk_uint8_t *p; | |
2543 | duk_labelinfo *li_start, *li; | |
2544 | ||
2545 | /* Duplicate (shadowing) labels are not allowed, except for the empty | |
2546 | * labels (which are used as default labels for switch and iteration | |
2547 | * statements). | |
2548 | * | |
2549 | * We could also allow shadowing of non-empty pending labels without any | |
2550 | * other issues than breaking the required label shadowing requirements | |
2551 | * of the E5 specification, see Section 12.12. | |
2552 | */ | |
2553 | ||
2554 | p = (duk_uint8_t *) DUK_HBUFFER_DYNAMIC_GET_DATA_PTR(thr->heap, comp_ctx->curr_func.h_labelinfos); | |
2555 | li_start = (duk_labelinfo *) (void *) p; | |
2556 | li = (duk_labelinfo *) (void *) (p + DUK_HBUFFER_GET_SIZE(comp_ctx->curr_func.h_labelinfos)); | |
2557 | n = (duk_size_t) (li - li_start); | |
2558 | ||
2559 | while (li > li_start) { | |
2560 | li--; | |
2561 | ||
2562 | if (li->h_label == h_label && h_label != DUK_HTHREAD_STRING_EMPTY_STRING(thr)) { | |
11fdf7f2 | 2563 | DUK_ERROR_SYNTAX(thr, DUK_STR_DUPLICATE_LABEL); |
7c673cae FG |
2564 | } |
2565 | } | |
2566 | ||
2567 | duk_push_hstring(ctx, h_label); | |
2568 | DUK_ASSERT(n <= DUK_UARRIDX_MAX); /* label limits */ | |
2569 | (void) duk_put_prop_index(ctx, comp_ctx->curr_func.labelnames_idx, (duk_uarridx_t) n); | |
2570 | ||
2571 | new_size = (n + 1) * sizeof(duk_labelinfo); | |
2572 | duk_hbuffer_resize(thr, comp_ctx->curr_func.h_labelinfos, new_size); | |
2573 | /* XXX: spare handling, slow now */ | |
2574 | ||
2575 | /* relookup after possible realloc */ | |
2576 | p = (duk_uint8_t *) DUK_HBUFFER_DYNAMIC_GET_DATA_PTR(thr->heap, comp_ctx->curr_func.h_labelinfos); | |
2577 | li_start = (duk_labelinfo *) (void *) p; | |
2578 | DUK_UNREF(li_start); /* silence scan-build warning */ | |
2579 | li = (duk_labelinfo *) (void *) (p + DUK_HBUFFER_GET_SIZE(comp_ctx->curr_func.h_labelinfos)); | |
2580 | li--; | |
2581 | ||
2582 | /* Labels can be used for iteration statements but also for other statements, | |
2583 | * in particular a label can be used for a block statement. All cases of a | |
2584 | * named label accept a 'break' so that flag is set here. Iteration statements | |
2585 | * also allow 'continue', so that flag is updated when we figure out the | |
2586 | * statement type. | |
2587 | */ | |
2588 | ||
2589 | li->flags = DUK_LABEL_FLAG_ALLOW_BREAK; | |
2590 | li->label_id = label_id; | |
2591 | li->h_label = h_label; | |
2592 | li->catch_depth = comp_ctx->curr_func.catch_depth; /* catch depth from current func */ | |
2593 | li->pc_label = pc_label; | |
2594 | ||
2595 | DUK_DDD(DUK_DDDPRINT("registered label: flags=0x%08lx, id=%ld, name=%!O, catch_depth=%ld, pc_label=%ld", | |
2596 | (unsigned long) li->flags, (long) li->label_id, (duk_heaphdr *) li->h_label, | |
2597 | (long) li->catch_depth, (long) li->pc_label)); | |
2598 | } | |
2599 | ||
2600 | /* Update all labels with matching label_id. */ | |
2601 | DUK_LOCAL void duk__update_label_flags(duk_compiler_ctx *comp_ctx, duk_int_t label_id, duk_small_uint_t flags) { | |
2602 | duk_uint8_t *p; | |
2603 | duk_labelinfo *li_start, *li; | |
2604 | ||
2605 | p = (duk_uint8_t *) DUK_HBUFFER_DYNAMIC_GET_DATA_PTR(comp_ctx->thr->heap, comp_ctx->curr_func.h_labelinfos); | |
2606 | li_start = (duk_labelinfo *) (void *) p; | |
2607 | li = (duk_labelinfo *) (void *) (p + DUK_HBUFFER_GET_SIZE(comp_ctx->curr_func.h_labelinfos)); | |
2608 | ||
2609 | /* Match labels starting from latest; once label_id no longer matches, we can | |
2610 | * safely exit without checking the rest of the labels (only the topmost labels | |
2611 | * are ever updated). | |
2612 | */ | |
2613 | while (li > li_start) { | |
2614 | li--; | |
2615 | ||
2616 | if (li->label_id != label_id) { | |
2617 | break; | |
2618 | } | |
2619 | ||
2620 | DUK_DDD(DUK_DDDPRINT("updating (overwriting) label flags for li=%p, label_id=%ld, flags=%ld", | |
2621 | (void *) li, (long) label_id, (long) flags)); | |
2622 | ||
2623 | li->flags = flags; | |
2624 | } | |
2625 | } | |
2626 | ||
2627 | /* Lookup active label information. Break/continue distinction is necessary to handle switch | |
2628 | * statement related labels correctly: a switch will only catch a 'break', not a 'continue'. | |
2629 | * | |
2630 | * An explicit label cannot appear multiple times in the active set, but empty labels (unlabelled | |
2631 | * iteration and switch statements) can. A break will match the closest unlabelled or labelled | |
2632 | * statement. A continue will match the closest unlabelled or labelled iteration statement. It is | |
2633 | * a syntax error if a continue matches a labelled switch statement; because an explicit label cannot | |
2634 | * be duplicated, the continue cannot match any valid label outside the switch. | |
2635 | * | |
2636 | * A side effect of these rules is that a LABEL statement related to a switch should never actually | |
2637 | * catch a continue abrupt completion at run-time. Hence an INVALID opcode can be placed in the | |
2638 | * continue slot of the switch's LABEL statement. | |
2639 | */ | |
2640 | ||
2641 | /* XXX: awkward, especially the bunch of separate output values -> output struct? */ | |
2642 | DUK_LOCAL 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) { | |
2643 | duk_hthread *thr = comp_ctx->thr; | |
2644 | duk_context *ctx = (duk_context *) thr; | |
2645 | duk_uint8_t *p; | |
2646 | duk_labelinfo *li_start, *li_end, *li; | |
2647 | duk_bool_t match = 0; | |
2648 | ||
2649 | DUK_DDD(DUK_DDDPRINT("looking up active label: label='%!O', is_break=%ld", | |
2650 | (duk_heaphdr *) h_label, (long) is_break)); | |
2651 | ||
2652 | DUK_UNREF(ctx); | |
2653 | ||
2654 | p = (duk_uint8_t *) DUK_HBUFFER_DYNAMIC_GET_DATA_PTR(thr->heap, comp_ctx->curr_func.h_labelinfos); | |
2655 | li_start = (duk_labelinfo *) (void *) p; | |
2656 | li_end = (duk_labelinfo *) (void *) (p + DUK_HBUFFER_GET_SIZE(comp_ctx->curr_func.h_labelinfos)); | |
2657 | li = li_end; | |
2658 | ||
2659 | /* Match labels starting from latest label because there can be duplicate empty | |
2660 | * labels in the label set. | |
2661 | */ | |
2662 | while (li > li_start) { | |
2663 | li--; | |
2664 | ||
2665 | if (li->h_label != h_label) { | |
2666 | DUK_DDD(DUK_DDDPRINT("labelinfo[%ld] ->'%!O' != %!O", | |
2667 | (long) (li - li_start), | |
2668 | (duk_heaphdr *) li->h_label, | |
2669 | (duk_heaphdr *) h_label)); | |
2670 | continue; | |
2671 | } | |
2672 | ||
2673 | DUK_DDD(DUK_DDDPRINT("labelinfo[%ld] -> '%!O' label name matches (still need to check type)", | |
2674 | (long) (li - li_start), (duk_heaphdr *) h_label)); | |
2675 | ||
2676 | /* currently all labels accept a break, so no explicit check for it now */ | |
2677 | DUK_ASSERT(li->flags & DUK_LABEL_FLAG_ALLOW_BREAK); | |
2678 | ||
2679 | if (is_break) { | |
2680 | /* break matches always */ | |
2681 | match = 1; | |
2682 | break; | |
2683 | } else if (li->flags & DUK_LABEL_FLAG_ALLOW_CONTINUE) { | |
2684 | /* iteration statements allow continue */ | |
2685 | match = 1; | |
2686 | break; | |
2687 | } else { | |
2688 | /* continue matched this label -- we can only continue if this is the empty | |
2689 | * label, for which duplication is allowed, and thus there is hope of | |
2690 | * finding a match deeper in the label stack. | |
2691 | */ | |
2692 | if (h_label != DUK_HTHREAD_STRING_EMPTY_STRING(thr)) { | |
11fdf7f2 | 2693 | DUK_ERROR_SYNTAX(thr, DUK_STR_INVALID_LABEL); |
7c673cae FG |
2694 | } else { |
2695 | DUK_DDD(DUK_DDDPRINT("continue matched an empty label which does not " | |
2696 | "allow a continue -> continue lookup deeper in label stack")); | |
2697 | } | |
2698 | } | |
2699 | } | |
2700 | /* XXX: match flag is awkward, rework */ | |
2701 | if (!match) { | |
11fdf7f2 | 2702 | DUK_ERROR_SYNTAX(thr, DUK_STR_INVALID_LABEL); |
7c673cae FG |
2703 | } |
2704 | ||
2705 | DUK_DDD(DUK_DDDPRINT("label match: %!O -> label_id %ld, catch_depth=%ld, pc_label=%ld", | |
2706 | (duk_heaphdr *) h_label, (long) li->label_id, | |
2707 | (long) li->catch_depth, (long) li->pc_label)); | |
2708 | ||
2709 | *out_label_id = li->label_id; | |
2710 | *out_label_catch_depth = li->catch_depth; | |
2711 | *out_label_pc = li->pc_label; | |
2712 | *out_is_closest = (li == li_end - 1); | |
2713 | } | |
2714 | ||
2715 | DUK_LOCAL void duk__reset_labels_to_length(duk_compiler_ctx *comp_ctx, duk_int_t len) { | |
2716 | duk_hthread *thr = comp_ctx->thr; | |
2717 | duk_context *ctx = (duk_context *) thr; | |
2718 | duk_size_t new_size; | |
2719 | ||
2720 | /* XXX: duk_set_length */ | |
2721 | new_size = sizeof(duk_labelinfo) * (duk_size_t) len; | |
2722 | duk_push_int(ctx, len); | |
2723 | duk_put_prop_stridx(ctx, comp_ctx->curr_func.labelnames_idx, DUK_STRIDX_LENGTH); | |
2724 | duk_hbuffer_resize(thr, comp_ctx->curr_func.h_labelinfos, new_size); | |
2725 | } | |
2726 | ||
2727 | /* | |
2728 | * Expression parsing: duk__expr_nud(), duk__expr_led(), duk__expr_lbp(), and helpers. | |
2729 | * | |
2730 | * - duk__expr_nud(): ("null denotation"): process prev_token as a "start" of an expression (e.g. literal) | |
2731 | * - duk__expr_led(): ("left denotation"): process prev_token in the "middle" of an expression (e.g. operator) | |
2732 | * - duk__expr_lbp(): ("left-binding power"): return left-binding power of curr_token | |
2733 | */ | |
2734 | ||
2735 | /* object literal key tracking flags */ | |
2736 | #define DUK__OBJ_LIT_KEY_PLAIN (1 << 0) /* key encountered as a plain property */ | |
2737 | #define DUK__OBJ_LIT_KEY_GET (1 << 1) /* key encountered as a getter */ | |
2738 | #define DUK__OBJ_LIT_KEY_SET (1 << 2) /* key encountered as a setter */ | |
2739 | ||
2740 | DUK_LOCAL void duk__nud_array_literal(duk_compiler_ctx *comp_ctx, duk_ivalue *res) { | |
2741 | duk_hthread *thr = comp_ctx->thr; | |
2742 | duk_reg_t reg_obj; /* result reg */ | |
2743 | duk_reg_t reg_temp; /* temp reg */ | |
2744 | duk_reg_t temp_start; /* temp reg value for start of loop */ | |
2745 | duk_small_uint_t max_init_values; /* max # of values initialized in one MPUTARR set */ | |
2746 | duk_small_uint_t num_values; /* number of values in current MPUTARR set */ | |
2747 | duk_uarridx_t curr_idx; /* current (next) array index */ | |
2748 | duk_uarridx_t start_idx; /* start array index of current MPUTARR set */ | |
2749 | duk_uarridx_t init_idx; /* last array index explicitly initialized, +1 */ | |
2750 | duk_bool_t require_comma; /* next loop requires a comma */ | |
2751 | ||
2752 | /* DUK_TOK_LBRACKET already eaten, current token is right after that */ | |
2753 | DUK_ASSERT(comp_ctx->prev_token.t == DUK_TOK_LBRACKET); | |
2754 | ||
2755 | max_init_values = DUK__MAX_ARRAY_INIT_VALUES; /* XXX: depend on available temps? */ | |
2756 | ||
2757 | reg_obj = DUK__ALLOCTEMP(comp_ctx); | |
2758 | duk__emit_extraop_b_c(comp_ctx, | |
2759 | DUK_EXTRAOP_NEWARR | DUK__EMIT_FLAG_B_IS_TARGET, | |
2760 | reg_obj, | |
2761 | 0); /* XXX: patch initial size afterwards? */ | |
2762 | temp_start = DUK__GETTEMP(comp_ctx); | |
2763 | ||
2764 | /* | |
2765 | * Emit initializers in sets of maximum max_init_values. | |
2766 | * Corner cases such as single value initializers do not have | |
2767 | * special handling now. | |
2768 | * | |
2769 | * Elided elements must not be emitted as 'undefined' values, | |
2770 | * because such values would be enumerable (which is incorrect). | |
2771 | * Also note that trailing elisions must be reflected in the | |
2772 | * length of the final array but cause no elements to be actually | |
2773 | * inserted. | |
2774 | */ | |
2775 | ||
2776 | curr_idx = 0; | |
2777 | init_idx = 0; /* tracks maximum initialized index + 1 */ | |
2778 | start_idx = 0; | |
2779 | require_comma = 0; | |
2780 | ||
2781 | for (;;) { | |
2782 | num_values = 0; | |
2783 | DUK__SETTEMP(comp_ctx, temp_start); | |
2784 | ||
2785 | if (comp_ctx->curr_token.t == DUK_TOK_RBRACKET) { | |
2786 | break; | |
2787 | } | |
2788 | ||
2789 | for (;;) { | |
2790 | if (comp_ctx->curr_token.t == DUK_TOK_RBRACKET) { | |
2791 | /* the outer loop will recheck and exit */ | |
2792 | break; | |
2793 | } | |
2794 | ||
2795 | /* comma check */ | |
2796 | if (require_comma) { | |
2797 | if (comp_ctx->curr_token.t == DUK_TOK_COMMA) { | |
2798 | /* comma after a value, expected */ | |
2799 | duk__advance(comp_ctx); | |
2800 | require_comma = 0; | |
2801 | continue; | |
2802 | } else { | |
2803 | goto syntax_error; | |
2804 | } | |
2805 | } else { | |
2806 | if (comp_ctx->curr_token.t == DUK_TOK_COMMA) { | |
2807 | /* elision - flush */ | |
2808 | curr_idx++; | |
2809 | duk__advance(comp_ctx); | |
2810 | /* if num_values > 0, MPUTARR emitted by outer loop after break */ | |
2811 | break; | |
2812 | } | |
2813 | } | |
2814 | /* else an array initializer element */ | |
2815 | ||
2816 | /* initial index */ | |
2817 | if (num_values == 0) { | |
2818 | start_idx = curr_idx; | |
2819 | reg_temp = DUK__ALLOCTEMP(comp_ctx); | |
2820 | duk__emit_load_int32(comp_ctx, reg_temp, (duk_int32_t) start_idx); | |
2821 | } | |
2822 | ||
2823 | reg_temp = DUK__ALLOCTEMP(comp_ctx); /* alloc temp just in case, to update max temp */ | |
2824 | DUK__SETTEMP(comp_ctx, reg_temp); | |
2825 | duk__expr_toforcedreg(comp_ctx, res, DUK__BP_COMMA /*rbp_flags*/, reg_temp /*forced_reg*/); | |
2826 | DUK__SETTEMP(comp_ctx, reg_temp + 1); | |
2827 | ||
2828 | num_values++; | |
2829 | curr_idx++; | |
2830 | require_comma = 1; | |
2831 | ||
2832 | if (num_values >= max_init_values) { | |
2833 | /* MPUTARR emitted by outer loop */ | |
2834 | break; | |
2835 | } | |
2836 | } | |
2837 | ||
2838 | if (num_values > 0) { | |
2839 | /* - A is a source register (it's not a write target, but used | |
2840 | * to identify the target object) but can be shuffled. | |
2841 | * - B cannot be shuffled normally because it identifies a range | |
2842 | * of registers, the emitter has special handling for this | |
2843 | * (the "no shuffle" flag must not be set). | |
2844 | * - C is a non-register number and cannot be shuffled, but | |
2845 | * never needs to be. | |
2846 | */ | |
2847 | duk__emit_a_b_c(comp_ctx, | |
2848 | DUK_OP_MPUTARR | | |
2849 | DUK__EMIT_FLAG_NO_SHUFFLE_C | | |
2850 | DUK__EMIT_FLAG_A_IS_SOURCE, | |
2851 | (duk_regconst_t) reg_obj, | |
2852 | (duk_regconst_t) temp_start, | |
2853 | (duk_regconst_t) num_values); | |
2854 | init_idx = start_idx + num_values; | |
2855 | ||
2856 | /* num_values and temp_start reset at top of outer loop */ | |
2857 | } | |
2858 | } | |
2859 | ||
2860 | DUK_ASSERT(comp_ctx->curr_token.t == DUK_TOK_RBRACKET); | |
2861 | duk__advance(comp_ctx); | |
2862 | ||
2863 | DUK_DDD(DUK_DDDPRINT("array literal done, curridx=%ld, initidx=%ld", | |
2864 | (long) curr_idx, (long) init_idx)); | |
2865 | ||
2866 | /* trailing elisions? */ | |
2867 | if (curr_idx > init_idx) { | |
2868 | /* yes, must set array length explicitly */ | |
2869 | DUK_DDD(DUK_DDDPRINT("array literal has trailing elisions which affect its length")); | |
2870 | reg_temp = DUK__ALLOCTEMP(comp_ctx); | |
2871 | duk__emit_load_int32(comp_ctx, reg_temp, (duk_int_t) curr_idx); | |
2872 | duk__emit_extraop_b_c(comp_ctx, | |
2873 | DUK_EXTRAOP_SETALEN, | |
2874 | (duk_regconst_t) reg_obj, | |
2875 | (duk_regconst_t) reg_temp); | |
2876 | } | |
2877 | ||
2878 | DUK__SETTEMP(comp_ctx, temp_start); | |
2879 | ||
2880 | res->t = DUK_IVAL_PLAIN; | |
2881 | res->x1.t = DUK_ISPEC_REGCONST; | |
2882 | res->x1.regconst = (duk_regconst_t) reg_obj; | |
2883 | return; | |
2884 | ||
2885 | syntax_error: | |
11fdf7f2 | 2886 | DUK_ERROR_SYNTAX(thr, DUK_STR_INVALID_ARRAY_LITERAL); |
7c673cae FG |
2887 | } |
2888 | ||
2889 | /* duplicate/invalid key checks; returns 1 if syntax error */ | |
2890 | DUK_LOCAL duk_bool_t duk__nud_object_literal_key_check(duk_compiler_ctx *comp_ctx, duk_small_uint_t new_key_flags) { | |
2891 | duk_hthread *thr = comp_ctx->thr; | |
2892 | duk_context *ctx = (duk_context *) thr; | |
2893 | duk_small_uint_t key_flags; | |
2894 | ||
2895 | /* [ ... key_obj key ] */ | |
2896 | ||
2897 | DUK_ASSERT(duk_is_string(ctx, -1)); | |
2898 | ||
2899 | /* | |
2900 | * 'key_obj' tracks keys encountered so far by associating an | |
2901 | * integer with flags with already encountered keys. The checks | |
2902 | * below implement E5 Section 11.1.5, step 4 for production: | |
2903 | * | |
2904 | * PropertyNameAndValueList: PropertyNameAndValueList , PropertyAssignment | |
2905 | */ | |
2906 | ||
2907 | duk_dup(ctx, -1); /* [ ... key_obj key key ] */ | |
2908 | duk_get_prop(ctx, -3); /* [ ... key_obj key val ] */ | |
2909 | key_flags = duk_to_int(ctx, -1); | |
2910 | duk_pop(ctx); /* [ ... key_obj key ] */ | |
2911 | ||
2912 | if (new_key_flags & DUK__OBJ_LIT_KEY_PLAIN) { | |
2913 | if ((key_flags & DUK__OBJ_LIT_KEY_PLAIN) && comp_ctx->curr_func.is_strict) { | |
2914 | /* step 4.a */ | |
2915 | DUK_DDD(DUK_DDDPRINT("duplicate key: plain key appears twice in strict mode")); | |
2916 | return 1; | |
2917 | } | |
2918 | if (key_flags & (DUK__OBJ_LIT_KEY_GET | DUK__OBJ_LIT_KEY_SET)) { | |
2919 | /* step 4.c */ | |
2920 | DUK_DDD(DUK_DDDPRINT("duplicate key: plain key encountered after setter/getter")); | |
2921 | return 1; | |
2922 | } | |
2923 | } else { | |
2924 | if (key_flags & DUK__OBJ_LIT_KEY_PLAIN) { | |
2925 | /* step 4.b */ | |
2926 | DUK_DDD(DUK_DDDPRINT("duplicate key: getter/setter encountered after plain key")); | |
2927 | return 1; | |
2928 | } | |
2929 | if (key_flags & new_key_flags) { | |
2930 | /* step 4.d */ | |
2931 | DUK_DDD(DUK_DDDPRINT("duplicate key: getter/setter encountered twice")); | |
2932 | return 1; | |
2933 | } | |
2934 | } | |
2935 | ||
2936 | new_key_flags |= key_flags; | |
2937 | DUK_DDD(DUK_DDDPRINT("setting/updating key %!T flags: 0x%08lx -> 0x%08lx", | |
2938 | (duk_tval *) duk_get_tval(ctx, -1), | |
2939 | (unsigned long) key_flags, | |
2940 | (unsigned long) new_key_flags)); | |
2941 | duk_dup(ctx, -1); | |
2942 | duk_push_int(ctx, new_key_flags); /* [ ... key_obj key key flags ] */ | |
2943 | duk_put_prop(ctx, -4); /* [ ... key_obj key ] */ | |
2944 | ||
2945 | return 0; | |
2946 | } | |
2947 | ||
2948 | DUK_LOCAL void duk__nud_object_literal(duk_compiler_ctx *comp_ctx, duk_ivalue *res) { | |
2949 | duk_hthread *thr = comp_ctx->thr; | |
2950 | duk_context *ctx = (duk_context *) thr; | |
2951 | duk_reg_t reg_obj; /* result reg */ | |
2952 | duk_reg_t reg_key; /* temp reg for key literal */ | |
2953 | duk_reg_t reg_temp; /* temp reg */ | |
2954 | duk_reg_t temp_start; /* temp reg value for start of loop */ | |
2955 | duk_small_uint_t max_init_pairs; /* max # of key-value pairs initialized in one MPUTOBJ set */ | |
2956 | duk_small_uint_t num_pairs; /* number of pairs in current MPUTOBJ set */ | |
2957 | duk_bool_t first; /* first value: comma must not precede the value */ | |
2958 | duk_bool_t is_set, is_get; /* temps */ | |
2959 | ||
2960 | DUK_ASSERT(comp_ctx->prev_token.t == DUK_TOK_LCURLY); | |
2961 | ||
2962 | max_init_pairs = DUK__MAX_OBJECT_INIT_PAIRS; /* XXX: depend on available temps? */ | |
2963 | ||
2964 | reg_obj = DUK__ALLOCTEMP(comp_ctx); | |
2965 | duk__emit_extraop_b_c(comp_ctx, | |
2966 | DUK_EXTRAOP_NEWOBJ | DUK__EMIT_FLAG_B_IS_TARGET, | |
2967 | reg_obj, | |
2968 | 0); /* XXX: patch initial size afterwards? */ | |
2969 | temp_start = DUK__GETTEMP(comp_ctx); | |
2970 | ||
2971 | /* temp object for tracking / detecting duplicate keys */ | |
2972 | duk_push_object(ctx); | |
2973 | ||
2974 | /* | |
2975 | * Emit initializers in sets of maximum max_init_pairs keys. | |
2976 | * Setter/getter is handled separately and terminates the | |
2977 | * current set of initializer values. Corner cases such as | |
2978 | * single value initializers do not have special handling now. | |
2979 | */ | |
2980 | ||
2981 | first = 1; | |
2982 | for (;;) { | |
2983 | num_pairs = 0; | |
2984 | DUK__SETTEMP(comp_ctx, temp_start); | |
2985 | ||
2986 | if (comp_ctx->curr_token.t == DUK_TOK_RCURLY) { | |
2987 | break; | |
2988 | } | |
2989 | ||
2990 | for (;;) { | |
2991 | /* | |
2992 | * Three possible element formats: | |
2993 | * 1) PropertyName : AssignmentExpression | |
2994 | * 2) get PropertyName () { FunctionBody } | |
2995 | * 3) set PropertyName ( PropertySetParameterList ) { FunctionBody } | |
2996 | * | |
2997 | * PropertyName can be IdentifierName (includes reserved words), a string | |
2998 | * literal, or a number literal. Note that IdentifierName allows 'get' and | |
2999 | * 'set' too, so we need to look ahead to the next token to distinguish: | |
3000 | * | |
3001 | * { get : 1 } | |
3002 | * | |
3003 | * and | |
3004 | * | |
3005 | * { get foo() { return 1 } } | |
3006 | * { get get() { return 1 } } // 'get' as getter propertyname | |
3007 | * | |
3008 | * Finally, a trailing comma is allowed. | |
3009 | * | |
3010 | * Key name is coerced to string at compile time (and ends up as a | |
3011 | * a string constant) even for numeric keys (e.g. "{1:'foo'}"). | |
3012 | * These could be emitted using e.g. LDINT, but that seems hardly | |
3013 | * worth the effort and would increase code size. | |
3014 | */ | |
3015 | ||
3016 | DUK_DDD(DUK_DDDPRINT("object literal inner loop, curr_token->t = %ld", | |
3017 | (long) comp_ctx->curr_token.t)); | |
3018 | ||
3019 | if (comp_ctx->curr_token.t == DUK_TOK_RCURLY) { | |
3020 | /* the outer loop will recheck and exit */ | |
3021 | break; | |
3022 | } | |
3023 | if (num_pairs >= max_init_pairs) { | |
3024 | /* MPUTOBJ emitted by outer loop */ | |
3025 | break; | |
3026 | } | |
3027 | ||
3028 | if (first) { | |
3029 | first = 0; | |
3030 | } else { | |
3031 | if (comp_ctx->curr_token.t != DUK_TOK_COMMA) { | |
3032 | goto syntax_error; | |
3033 | } | |
3034 | duk__advance(comp_ctx); | |
3035 | if (comp_ctx->curr_token.t == DUK_TOK_RCURLY) { | |
3036 | /* trailing comma followed by rcurly */ | |
3037 | break; | |
3038 | } | |
3039 | } | |
3040 | ||
3041 | /* advance to get one step of lookup */ | |
3042 | duk__advance(comp_ctx); | |
3043 | ||
3044 | /* NOTE: "get" and "set" are not officially ReservedWords and the lexer | |
3045 | * currently treats them always like ordinary identifiers (DUK_TOK_GET | |
3046 | * and DUK_TOK_SET are unused). They need to be detected based on the | |
3047 | * identifier string content. | |
3048 | */ | |
3049 | ||
3050 | is_get = (comp_ctx->prev_token.t == DUK_TOK_IDENTIFIER && | |
3051 | comp_ctx->prev_token.str1 == DUK_HTHREAD_STRING_GET(thr)); | |
3052 | is_set = (comp_ctx->prev_token.t == DUK_TOK_IDENTIFIER && | |
3053 | comp_ctx->prev_token.str1 == DUK_HTHREAD_STRING_SET(thr)); | |
3054 | if ((is_get || is_set) && comp_ctx->curr_token.t != DUK_TOK_COLON) { | |
3055 | /* getter/setter */ | |
3056 | duk_int_t fnum; | |
3057 | ||
3058 | if (comp_ctx->curr_token.t_nores == DUK_TOK_IDENTIFIER || | |
3059 | comp_ctx->curr_token.t_nores == DUK_TOK_STRING) { | |
3060 | /* same handling for identifiers and strings */ | |
3061 | DUK_ASSERT(comp_ctx->curr_token.str1 != NULL); | |
3062 | duk_push_hstring(ctx, comp_ctx->curr_token.str1); | |
3063 | } else if (comp_ctx->curr_token.t == DUK_TOK_NUMBER) { | |
3064 | duk_push_number(ctx, comp_ctx->curr_token.num); | |
3065 | duk_to_string(ctx, -1); | |
3066 | } else { | |
3067 | goto syntax_error; | |
3068 | } | |
3069 | ||
3070 | DUK_ASSERT(duk_is_string(ctx, -1)); | |
3071 | if (duk__nud_object_literal_key_check(comp_ctx, | |
3072 | (is_get ? DUK__OBJ_LIT_KEY_GET : DUK__OBJ_LIT_KEY_SET))) { | |
3073 | goto syntax_error; | |
3074 | } | |
3075 | reg_key = duk__getconst(comp_ctx); | |
3076 | ||
3077 | if (num_pairs > 0) { | |
3078 | /* - A is a source register (it's not a write target, but used | |
3079 | * to identify the target object) but can be shuffled. | |
3080 | * - B cannot be shuffled normally because it identifies a range | |
3081 | * of registers, the emitter has special handling for this | |
3082 | * (the "no shuffle" flag must not be set). | |
3083 | * - C is a non-register number and cannot be shuffled, but | |
3084 | * never needs to be. | |
3085 | */ | |
3086 | duk__emit_a_b_c(comp_ctx, | |
3087 | DUK_OP_MPUTOBJ | | |
3088 | DUK__EMIT_FLAG_NO_SHUFFLE_C | | |
3089 | DUK__EMIT_FLAG_A_IS_SOURCE, | |
3090 | reg_obj, | |
3091 | temp_start, | |
3092 | num_pairs); | |
3093 | num_pairs = 0; | |
3094 | DUK__SETTEMP(comp_ctx, temp_start); | |
3095 | } | |
3096 | ||
3097 | /* curr_token = get/set name */ | |
3098 | fnum = duk__parse_func_like_fnum(comp_ctx, 0 /*is_decl*/, 1 /*is_setget*/); | |
3099 | ||
3100 | DUK_ASSERT(DUK__GETTEMP(comp_ctx) == temp_start); | |
3101 | reg_temp = DUK__ALLOCTEMP(comp_ctx); | |
3102 | duk__emit_a_bc(comp_ctx, | |
3103 | DUK_OP_LDCONST, | |
3104 | (duk_regconst_t) reg_temp, | |
3105 | (duk_regconst_t) reg_key); | |
3106 | reg_temp = DUK__ALLOCTEMP(comp_ctx); | |
3107 | duk__emit_a_bc(comp_ctx, | |
3108 | DUK_OP_CLOSURE, | |
3109 | (duk_regconst_t) reg_temp, | |
3110 | (duk_regconst_t) fnum); | |
3111 | ||
3112 | /* Slot C is used in a non-standard fashion (range of regs), | |
3113 | * emitter code has special handling for it (must not set the | |
3114 | * "no shuffle" flag). | |
3115 | */ | |
3116 | duk__emit_extraop_b_c(comp_ctx, | |
3117 | (is_get ? DUK_EXTRAOP_INITGET : DUK_EXTRAOP_INITSET), | |
3118 | reg_obj, | |
3119 | temp_start); /* temp_start+0 = key, temp_start+1 = closure */ | |
3120 | ||
3121 | DUK__SETTEMP(comp_ctx, temp_start); | |
3122 | } else { | |
3123 | /* normal key/value */ | |
3124 | if (comp_ctx->prev_token.t_nores == DUK_TOK_IDENTIFIER || | |
3125 | comp_ctx->prev_token.t_nores == DUK_TOK_STRING) { | |
3126 | /* same handling for identifiers and strings */ | |
3127 | DUK_ASSERT(comp_ctx->prev_token.str1 != NULL); | |
3128 | duk_push_hstring(ctx, comp_ctx->prev_token.str1); | |
3129 | } else if (comp_ctx->prev_token.t == DUK_TOK_NUMBER) { | |
3130 | duk_push_number(ctx, comp_ctx->prev_token.num); | |
3131 | duk_to_string(ctx, -1); | |
3132 | } else { | |
3133 | goto syntax_error; | |
3134 | } | |
3135 | ||
3136 | DUK_ASSERT(duk_is_string(ctx, -1)); | |
3137 | if (duk__nud_object_literal_key_check(comp_ctx, DUK__OBJ_LIT_KEY_PLAIN)) { | |
3138 | goto syntax_error; | |
3139 | } | |
3140 | reg_key = duk__getconst(comp_ctx); | |
3141 | ||
3142 | reg_temp = DUK__ALLOCTEMP(comp_ctx); | |
3143 | duk__emit_a_bc(comp_ctx, | |
3144 | DUK_OP_LDCONST, | |
3145 | (duk_regconst_t) reg_temp, | |
3146 | (duk_regconst_t) reg_key); | |
3147 | duk__advance_expect(comp_ctx, DUK_TOK_COLON); | |
3148 | ||
3149 | reg_temp = DUK__ALLOCTEMP(comp_ctx); /* alloc temp just in case, to update max temp */ | |
3150 | DUK__SETTEMP(comp_ctx, reg_temp); | |
3151 | duk__expr_toforcedreg(comp_ctx, res, DUK__BP_COMMA /*rbp_flags*/, reg_temp /*forced_reg*/); | |
3152 | DUK__SETTEMP(comp_ctx, reg_temp + 1); | |
3153 | ||
3154 | num_pairs++; | |
3155 | } | |
3156 | } | |
3157 | ||
3158 | if (num_pairs > 0) { | |
3159 | /* See MPUTOBJ comments above. */ | |
3160 | duk__emit_a_b_c(comp_ctx, | |
3161 | DUK_OP_MPUTOBJ | | |
3162 | DUK__EMIT_FLAG_NO_SHUFFLE_C | | |
3163 | DUK__EMIT_FLAG_A_IS_SOURCE, | |
3164 | reg_obj, | |
3165 | temp_start, | |
3166 | num_pairs); | |
3167 | ||
3168 | /* num_pairs and temp_start reset at top of outer loop */ | |
3169 | } | |
3170 | } | |
3171 | ||
3172 | DUK_ASSERT(comp_ctx->curr_token.t == DUK_TOK_RCURLY); | |
3173 | duk__advance(comp_ctx); | |
3174 | ||
3175 | DUK__SETTEMP(comp_ctx, temp_start); | |
3176 | ||
3177 | res->t = DUK_IVAL_PLAIN; | |
3178 | res->x1.t = DUK_ISPEC_REGCONST; | |
3179 | res->x1.regconst = (duk_regconst_t) reg_obj; | |
3180 | ||
3181 | DUK_DDD(DUK_DDDPRINT("final tracking object: %!T", | |
3182 | (duk_tval *) duk_get_tval(ctx, -1))); | |
3183 | duk_pop(ctx); | |
3184 | return; | |
3185 | ||
3186 | syntax_error: | |
11fdf7f2 | 3187 | DUK_ERROR_SYNTAX(thr, DUK_STR_INVALID_OBJECT_LITERAL); |
7c673cae FG |
3188 | } |
3189 | ||
3190 | /* Parse argument list. Arguments are written to temps starting from | |
3191 | * "next temp". Returns number of arguments parsed. Expects left paren | |
3192 | * to be already eaten, and eats the right paren before returning. | |
3193 | */ | |
3194 | DUK_LOCAL duk_int_t duk__parse_arguments(duk_compiler_ctx *comp_ctx, duk_ivalue *res) { | |
3195 | duk_int_t nargs = 0; | |
3196 | duk_reg_t reg_temp; | |
3197 | ||
3198 | /* Note: expect that caller has already eaten the left paren */ | |
3199 | ||
3200 | DUK_DDD(DUK_DDDPRINT("start parsing arguments, prev_token.t=%ld, curr_token.t=%ld", | |
3201 | (long) comp_ctx->prev_token.t, (long) comp_ctx->curr_token.t)); | |
3202 | ||
3203 | for (;;) { | |
3204 | if (comp_ctx->curr_token.t == DUK_TOK_RPAREN) { | |
3205 | break; | |
3206 | } | |
3207 | if (nargs > 0) { | |
3208 | duk__advance_expect(comp_ctx, DUK_TOK_COMMA); | |
3209 | } | |
3210 | ||
3211 | /* We want the argument expression value to go to "next temp" | |
3212 | * without additional moves. That should almost always be the | |
3213 | * case, but we double check after expression parsing. | |
3214 | * | |
3215 | * This is not the cleanest possible approach. | |
3216 | */ | |
3217 | ||
3218 | reg_temp = DUK__ALLOCTEMP(comp_ctx); /* bump up "allocated" reg count, just in case */ | |
3219 | DUK__SETTEMP(comp_ctx, reg_temp); | |
3220 | ||
3221 | /* binding power must be high enough to NOT allow comma expressions directly */ | |
3222 | duk__expr_toforcedreg(comp_ctx, res, DUK__BP_COMMA /*rbp_flags*/, reg_temp); /* always allow 'in', coerce to 'tr' just in case */ | |
3223 | ||
3224 | DUK__SETTEMP(comp_ctx, reg_temp + 1); | |
3225 | nargs++; | |
3226 | ||
3227 | DUK_DDD(DUK_DDDPRINT("argument #%ld written into reg %ld", (long) nargs, (long) reg_temp)); | |
3228 | } | |
3229 | ||
3230 | /* eat the right paren */ | |
3231 | duk__advance_expect(comp_ctx, DUK_TOK_RPAREN); | |
3232 | ||
3233 | DUK_DDD(DUK_DDDPRINT("end parsing arguments")); | |
3234 | ||
3235 | return nargs; | |
3236 | } | |
3237 | ||
3238 | DUK_LOCAL duk_bool_t duk__expr_is_empty(duk_compiler_ctx *comp_ctx) { | |
3239 | /* empty expressions can be detected conveniently with nud/led counts */ | |
3240 | return (comp_ctx->curr_func.nud_count == 0) && | |
3241 | (comp_ctx->curr_func.led_count == 0); | |
3242 | } | |
3243 | ||
3244 | DUK_LOCAL void duk__expr_nud(duk_compiler_ctx *comp_ctx, duk_ivalue *res) { | |
3245 | duk_hthread *thr = comp_ctx->thr; | |
3246 | duk_context *ctx = (duk_context *) thr; | |
3247 | duk_token *tk; | |
3248 | duk_reg_t temp_at_entry; | |
3249 | duk_small_int_t tok; | |
3250 | duk_uint32_t args; /* temp variable to pass constants and flags to shared code */ | |
3251 | ||
3252 | /* | |
3253 | * ctx->prev_token token to process with duk__expr_nud() | |
3254 | * ctx->curr_token updated by caller | |
3255 | * | |
3256 | * Note: the token in the switch below has already been eaten. | |
3257 | */ | |
3258 | ||
3259 | temp_at_entry = DUK__GETTEMP(comp_ctx); | |
3260 | ||
3261 | comp_ctx->curr_func.nud_count++; | |
3262 | ||
3263 | tk = &comp_ctx->prev_token; | |
3264 | tok = tk->t; | |
3265 | res->t = DUK_IVAL_NONE; | |
3266 | ||
3267 | DUK_DDD(DUK_DDDPRINT("duk__expr_nud(), prev_token.t=%ld, allow_in=%ld, paren_level=%ld", | |
3268 | (long) tk->t, (long) comp_ctx->curr_func.allow_in, (long) comp_ctx->curr_func.paren_level)); | |
3269 | ||
3270 | switch (tok) { | |
3271 | ||
3272 | /* PRIMARY EXPRESSIONS */ | |
3273 | ||
3274 | case DUK_TOK_THIS: { | |
3275 | duk_reg_t reg_temp; | |
3276 | reg_temp = DUK__ALLOCTEMP(comp_ctx); | |
3277 | duk__emit_extraop_bc(comp_ctx, | |
3278 | DUK_EXTRAOP_LDTHIS, | |
3279 | (duk_regconst_t) reg_temp); | |
3280 | res->t = DUK_IVAL_PLAIN; | |
3281 | res->x1.t = DUK_ISPEC_REGCONST; | |
3282 | res->x1.regconst = (duk_regconst_t) reg_temp; | |
3283 | return; | |
3284 | } | |
3285 | case DUK_TOK_IDENTIFIER: { | |
3286 | res->t = DUK_IVAL_VAR; | |
3287 | res->x1.t = DUK_ISPEC_VALUE; | |
3288 | duk_push_hstring(ctx, tk->str1); | |
3289 | duk_replace(ctx, res->x1.valstack_idx); | |
3290 | return; | |
3291 | } | |
3292 | case DUK_TOK_NULL: { | |
3293 | duk_push_null(ctx); | |
3294 | goto plain_value; | |
3295 | } | |
3296 | case DUK_TOK_TRUE: { | |
3297 | duk_push_true(ctx); | |
3298 | goto plain_value; | |
3299 | } | |
3300 | case DUK_TOK_FALSE: { | |
3301 | duk_push_false(ctx); | |
3302 | goto plain_value; | |
3303 | } | |
3304 | case DUK_TOK_NUMBER: { | |
3305 | duk_push_number(ctx, tk->num); | |
3306 | goto plain_value; | |
3307 | } | |
3308 | case DUK_TOK_STRING: { | |
3309 | DUK_ASSERT(tk->str1 != NULL); | |
3310 | duk_push_hstring(ctx, tk->str1); | |
3311 | goto plain_value; | |
3312 | } | |
3313 | case DUK_TOK_REGEXP: { | |
3314 | #ifdef DUK_USE_REGEXP_SUPPORT | |
3315 | duk_reg_t reg_temp; | |
3316 | duk_regconst_t rc_re_bytecode; /* const */ | |
3317 | duk_regconst_t rc_re_source; /* const */ | |
3318 | ||
3319 | DUK_ASSERT(tk->str1 != NULL); | |
3320 | DUK_ASSERT(tk->str2 != NULL); | |
3321 | ||
3322 | DUK_DDD(DUK_DDDPRINT("emitting regexp op, str1=%!O, str2=%!O", | |
3323 | (duk_heaphdr *) tk->str1, | |
3324 | (duk_heaphdr *) tk->str2)); | |
3325 | ||
3326 | reg_temp = DUK__ALLOCTEMP(comp_ctx); | |
3327 | duk_push_hstring(ctx, tk->str1); | |
3328 | duk_push_hstring(ctx, tk->str2); | |
3329 | ||
3330 | /* [ ... pattern flags ] */ | |
3331 | ||
3332 | duk_regexp_compile(thr); | |
3333 | ||
3334 | /* [ ... escaped_source bytecode ] */ | |
3335 | ||
3336 | rc_re_bytecode = duk__getconst(comp_ctx); | |
3337 | rc_re_source = duk__getconst(comp_ctx); | |
3338 | ||
3339 | duk__emit_a_b_c(comp_ctx, | |
3340 | DUK_OP_REGEXP, | |
3341 | (duk_regconst_t) reg_temp /*a*/, | |
3342 | rc_re_bytecode /*b*/, | |
3343 | rc_re_source /*c*/); | |
3344 | ||
3345 | res->t = DUK_IVAL_PLAIN; | |
3346 | res->x1.t = DUK_ISPEC_REGCONST; | |
3347 | res->x1.regconst = (duk_regconst_t) reg_temp; | |
3348 | return; | |
3349 | #else /* DUK_USE_REGEXP_SUPPORT */ | |
3350 | goto syntax_error; | |
3351 | #endif /* DUK_USE_REGEXP_SUPPORT */ | |
3352 | } | |
3353 | case DUK_TOK_LBRACKET: { | |
3354 | DUK_DDD(DUK_DDDPRINT("parsing array literal")); | |
3355 | duk__nud_array_literal(comp_ctx, res); | |
3356 | return; | |
3357 | } | |
3358 | case DUK_TOK_LCURLY: { | |
3359 | DUK_DDD(DUK_DDDPRINT("parsing object literal")); | |
3360 | duk__nud_object_literal(comp_ctx, res); | |
3361 | return; | |
3362 | } | |
3363 | case DUK_TOK_LPAREN: { | |
3364 | duk_bool_t prev_allow_in; | |
3365 | ||
3366 | comp_ctx->curr_func.paren_level++; | |
3367 | prev_allow_in = comp_ctx->curr_func.allow_in; | |
3368 | comp_ctx->curr_func.allow_in = 1; /* reset 'allow_in' for parenthesized expression */ | |
3369 | ||
3370 | duk__expr(comp_ctx, res, DUK__BP_FOR_EXPR /*rbp_flags*/); /* Expression, terminates at a ')' */ | |
3371 | ||
3372 | duk__advance_expect(comp_ctx, DUK_TOK_RPAREN); | |
3373 | comp_ctx->curr_func.allow_in = prev_allow_in; | |
3374 | comp_ctx->curr_func.paren_level--; | |
3375 | return; | |
3376 | } | |
3377 | ||
3378 | /* MEMBER/NEW/CALL EXPRESSIONS */ | |
3379 | ||
3380 | case DUK_TOK_NEW: { | |
3381 | /* | |
3382 | * Parsing an expression starting with 'new' is tricky because | |
3383 | * there are multiple possible productions deriving from | |
3384 | * LeftHandSideExpression which begin with 'new'. | |
3385 | * | |
3386 | * We currently resort to one-token lookahead to distinguish the | |
3387 | * cases. Hopefully this is correct. The binding power must be | |
3388 | * such that parsing ends at an LPAREN (CallExpression) but not at | |
3389 | * a PERIOD or LBRACKET (MemberExpression). | |
3390 | * | |
3391 | * See doc/compiler.rst for discussion on the parsing approach, | |
3392 | * and testcases/test-dev-new.js for a bunch of documented tests. | |
3393 | */ | |
3394 | ||
3395 | duk_reg_t reg_target; | |
3396 | duk_int_t nargs; | |
3397 | ||
3398 | DUK_DDD(DUK_DDDPRINT("begin parsing new expression")); | |
3399 | ||
3400 | reg_target = DUK__ALLOCTEMP(comp_ctx); | |
3401 | duk__expr_toforcedreg(comp_ctx, res, DUK__BP_CALL /*rbp_flags*/, reg_target /*forced_reg*/); | |
3402 | DUK__SETTEMP(comp_ctx, reg_target + 1); | |
3403 | ||
3404 | if (comp_ctx->curr_token.t == DUK_TOK_LPAREN) { | |
3405 | /* 'new' MemberExpression Arguments */ | |
3406 | DUK_DDD(DUK_DDDPRINT("new expression has argument list")); | |
3407 | duk__advance(comp_ctx); | |
3408 | nargs = duk__parse_arguments(comp_ctx, res); /* parse args starting from "next temp", reg_target + 1 */ | |
3409 | /* right paren eaten */ | |
3410 | } else { | |
3411 | /* 'new' MemberExpression */ | |
3412 | DUK_DDD(DUK_DDDPRINT("new expression has no argument list")); | |
3413 | nargs = 0; | |
3414 | } | |
3415 | ||
3416 | /* Opcode slot C is used in a non-standard way, so shuffling | |
3417 | * is not allowed. | |
3418 | */ | |
3419 | duk__emit_a_b_c(comp_ctx, | |
3420 | DUK_OP_NEW | DUK__EMIT_FLAG_NO_SHUFFLE_A | DUK__EMIT_FLAG_NO_SHUFFLE_C, | |
3421 | 0 /*unused*/, | |
3422 | reg_target /*target*/, | |
3423 | nargs /*num_args*/); | |
3424 | ||
3425 | DUK_DDD(DUK_DDDPRINT("end parsing new expression")); | |
3426 | ||
3427 | res->t = DUK_IVAL_PLAIN; | |
3428 | res->x1.t = DUK_ISPEC_REGCONST; | |
3429 | res->x1.regconst = (duk_regconst_t) reg_target; | |
3430 | return; | |
3431 | } | |
3432 | ||
3433 | /* FUNCTION EXPRESSIONS */ | |
3434 | ||
3435 | case DUK_TOK_FUNCTION: { | |
3436 | /* Function expression. Note that any statement beginning with 'function' | |
3437 | * is handled by the statement parser as a function declaration, or a | |
3438 | * non-standard function expression/statement (or a SyntaxError). We only | |
3439 | * handle actual function expressions (occurring inside an expression) here. | |
3440 | * | |
3441 | * O(depth^2) parse count for inner functions is handled by recording a | |
3442 | * lexer offset on the first compilation pass, so that the function can | |
3443 | * be efficiently skipped on the second pass. This is encapsulated into | |
3444 | * duk__parse_func_like_fnum(). | |
3445 | */ | |
3446 | ||
3447 | duk_reg_t reg_temp; | |
3448 | duk_int_t fnum; | |
3449 | ||
3450 | reg_temp = DUK__ALLOCTEMP(comp_ctx); | |
3451 | ||
3452 | /* curr_token follows 'function' */ | |
3453 | fnum = duk__parse_func_like_fnum(comp_ctx, 0 /*is_decl*/, 0 /*is_setget*/); | |
3454 | DUK_DDD(DUK_DDDPRINT("parsed inner function -> fnum %ld", (long) fnum)); | |
3455 | ||
3456 | duk__emit_a_bc(comp_ctx, | |
3457 | DUK_OP_CLOSURE, | |
3458 | (duk_regconst_t) reg_temp /*a*/, | |
3459 | (duk_regconst_t) fnum /*bc*/); | |
3460 | ||
3461 | res->t = DUK_IVAL_PLAIN; | |
3462 | res->x1.t = DUK_ISPEC_REGCONST; | |
3463 | res->x1.regconst = (duk_regconst_t) reg_temp; | |
3464 | return; | |
3465 | } | |
3466 | ||
3467 | /* UNARY EXPRESSIONS */ | |
3468 | ||
3469 | case DUK_TOK_DELETE: { | |
3470 | /* Delete semantics are a bit tricky. The description in E5 specification | |
3471 | * is kind of confusing, because it distinguishes between resolvability of | |
3472 | * a reference (which is only known at runtime) seemingly at compile time | |
3473 | * (= SyntaxError throwing). | |
3474 | */ | |
3475 | duk__expr(comp_ctx, res, DUK__BP_MULTIPLICATIVE /*rbp_flags*/); /* UnaryExpression */ | |
3476 | if (res->t == DUK_IVAL_VAR) { | |
3477 | /* not allowed in strict mode, regardless of whether resolves; | |
3478 | * in non-strict mode DELVAR handles both non-resolving and | |
3479 | * resolving cases (the specification description is a bit confusing). | |
3480 | */ | |
3481 | ||
3482 | duk_reg_t reg_temp; | |
3483 | duk_reg_t reg_varbind; | |
3484 | duk_regconst_t rc_varname; | |
3485 | ||
3486 | if (comp_ctx->curr_func.is_strict) { | |
11fdf7f2 | 3487 | DUK_ERROR_SYNTAX(thr, DUK_STR_CANNOT_DELETE_IDENTIFIER); |
7c673cae FG |
3488 | } |
3489 | ||
3490 | DUK__SETTEMP(comp_ctx, temp_at_entry); | |
3491 | reg_temp = DUK__ALLOCTEMP(comp_ctx); | |
3492 | ||
3493 | duk_dup(ctx, res->x1.valstack_idx); | |
3494 | if (duk__lookup_lhs(comp_ctx, ®_varbind, &rc_varname)) { | |
3495 | /* register bound variables are non-configurable -> always false */ | |
3496 | duk__emit_extraop_bc(comp_ctx, | |
3497 | DUK_EXTRAOP_LDFALSE, | |
3498 | (duk_regconst_t) reg_temp); | |
3499 | } else { | |
3500 | duk_dup(ctx, res->x1.valstack_idx); | |
3501 | rc_varname = duk__getconst(comp_ctx); | |
3502 | duk__emit_a_b(comp_ctx, | |
3503 | DUK_OP_DELVAR, | |
3504 | (duk_regconst_t) reg_temp, | |
3505 | (duk_regconst_t) rc_varname); | |
3506 | } | |
3507 | res->t = DUK_IVAL_PLAIN; | |
3508 | res->x1.t = DUK_ISPEC_REGCONST; | |
3509 | res->x1.regconst = (duk_regconst_t) reg_temp; | |
3510 | } else if (res->t == DUK_IVAL_PROP) { | |
3511 | duk_reg_t reg_temp; | |
3512 | duk_reg_t reg_obj; | |
3513 | duk_regconst_t rc_key; | |
3514 | ||
3515 | DUK__SETTEMP(comp_ctx, temp_at_entry); | |
3516 | reg_temp = DUK__ALLOCTEMP(comp_ctx); | |
3517 | reg_obj = duk__ispec_toregconst_raw(comp_ctx, &res->x1, -1 /*forced_reg*/, 0 /*flags*/); /* don't allow const */ | |
3518 | rc_key = duk__ispec_toregconst_raw(comp_ctx, &res->x2, -1 /*forced_reg*/, DUK__IVAL_FLAG_ALLOW_CONST /*flags*/); | |
3519 | duk__emit_a_b_c(comp_ctx, | |
3520 | DUK_OP_DELPROP, | |
3521 | (duk_regconst_t) reg_temp, | |
3522 | (duk_regconst_t) reg_obj, | |
3523 | rc_key); | |
3524 | ||
3525 | res->t = DUK_IVAL_PLAIN; | |
3526 | res->x1.t = DUK_ISPEC_REGCONST; | |
3527 | res->x1.regconst = (duk_regconst_t) reg_temp; | |
3528 | } else { | |
3529 | /* non-Reference deletion is always 'true', even in strict mode */ | |
3530 | duk_push_true(ctx); | |
3531 | goto plain_value; | |
3532 | } | |
3533 | return; | |
3534 | } | |
3535 | case DUK_TOK_VOID: { | |
3536 | duk__expr_toplain_ignore(comp_ctx, res, DUK__BP_MULTIPLICATIVE /*rbp_flags*/); /* UnaryExpression */ | |
3537 | duk_push_undefined(ctx); | |
3538 | goto plain_value; | |
3539 | } | |
3540 | case DUK_TOK_TYPEOF: { | |
3541 | /* 'typeof' must handle unresolvable references without throwing | |
3542 | * a ReferenceError (E5 Section 11.4.3). Register mapped values | |
3543 | * will never be unresolvable so special handling is only required | |
3544 | * when an identifier is a "slow path" one. | |
3545 | */ | |
3546 | duk__expr(comp_ctx, res, DUK__BP_MULTIPLICATIVE /*rbp_flags*/); /* UnaryExpression */ | |
3547 | ||
3548 | if (res->t == DUK_IVAL_VAR) { | |
3549 | duk_reg_t reg_varbind; | |
3550 | duk_regconst_t rc_varname; | |
3551 | duk_reg_t reg_temp; | |
3552 | ||
3553 | duk_dup(ctx, res->x1.valstack_idx); | |
3554 | if (!duk__lookup_lhs(comp_ctx, ®_varbind, &rc_varname)) { | |
3555 | DUK_DDD(DUK_DDDPRINT("typeof for an identifier name which could not be resolved " | |
3556 | "at compile time, need to use special run-time handling")); | |
3557 | reg_temp = DUK__ALLOCTEMP(comp_ctx); | |
3558 | duk__emit_extraop_b_c(comp_ctx, | |
3559 | DUK_EXTRAOP_TYPEOFID | DUK__EMIT_FLAG_B_IS_TARGET, | |
3560 | reg_temp, | |
3561 | rc_varname); | |
3562 | res->t = DUK_IVAL_PLAIN; | |
3563 | res->x1.t = DUK_ISPEC_REGCONST; | |
3564 | res->x1.regconst = (duk_regconst_t) reg_temp; | |
3565 | return; | |
3566 | } | |
3567 | } | |
3568 | ||
3569 | args = (DUK_EXTRAOP_TYPEOF << 8) + 0; | |
3570 | goto unary_extraop; | |
3571 | } | |
3572 | case DUK_TOK_INCREMENT: { | |
3573 | args = (DUK_OP_PREINCR << 8) + 0; | |
3574 | goto preincdec; | |
3575 | } | |
3576 | case DUK_TOK_DECREMENT: { | |
3577 | args = (DUK_OP_PREDECR << 8) + 0; | |
3578 | goto preincdec; | |
3579 | } | |
3580 | case DUK_TOK_ADD: { | |
3581 | /* unary plus */ | |
3582 | duk__expr(comp_ctx, res, DUK__BP_MULTIPLICATIVE /*rbp_flags*/); /* UnaryExpression */ | |
3583 | if (res->t == DUK_IVAL_PLAIN && res->x1.t == DUK_ISPEC_VALUE && | |
3584 | duk_is_number(ctx, res->x1.valstack_idx)) { | |
3585 | /* unary plus of a number is identity */ | |
3586 | ; | |
3587 | return; | |
3588 | } | |
3589 | args = (DUK_EXTRAOP_UNP << 8) + 0; | |
3590 | goto unary_extraop; | |
3591 | } | |
3592 | case DUK_TOK_SUB: { | |
3593 | /* unary minus */ | |
3594 | duk__expr(comp_ctx, res, DUK__BP_MULTIPLICATIVE /*rbp_flags*/); /* UnaryExpression */ | |
3595 | if (res->t == DUK_IVAL_PLAIN && res->x1.t == DUK_ISPEC_VALUE && | |
3596 | duk_is_number(ctx, res->x1.valstack_idx)) { | |
11fdf7f2 TL |
3597 | /* this optimization is important to handle negative literals |
3598 | * (which are not directly provided by the lexical grammar) | |
7c673cae | 3599 | */ |
11fdf7f2 | 3600 | duk_tval *tv_num; |
7c673cae FG |
3601 | duk_double_union du; |
3602 | ||
11fdf7f2 | 3603 | tv_num = DUK_GET_TVAL_POSIDX(ctx, res->x1.valstack_idx); |
7c673cae FG |
3604 | DUK_ASSERT(tv_num != NULL); |
3605 | DUK_ASSERT(DUK_TVAL_IS_NUMBER(tv_num)); | |
3606 | du.d = DUK_TVAL_GET_NUMBER(tv_num); | |
3607 | du.d = -du.d; | |
3608 | DUK_DBLUNION_NORMALIZE_NAN_CHECK(&du); | |
3609 | DUK_TVAL_SET_NUMBER(tv_num, du.d); | |
3610 | return; | |
3611 | } | |
3612 | args = (DUK_EXTRAOP_UNM << 8) + 0; | |
3613 | goto unary_extraop; | |
3614 | } | |
3615 | case DUK_TOK_BNOT: { | |
3616 | duk__expr(comp_ctx, res, DUK__BP_MULTIPLICATIVE /*rbp_flags*/); /* UnaryExpression */ | |
3617 | args = (DUK_EXTRAOP_BNOT << 8) + 0; | |
3618 | goto unary_extraop; | |
3619 | } | |
3620 | case DUK_TOK_LNOT: { | |
3621 | duk__expr(comp_ctx, res, DUK__BP_MULTIPLICATIVE /*rbp_flags*/); /* UnaryExpression */ | |
3622 | if (res->t == DUK_IVAL_PLAIN && res->x1.t == DUK_ISPEC_VALUE) { | |
3623 | /* Very minimal inlining to handle common idioms '!0' and '!1', | |
3624 | * and also boolean arguments like '!false' and '!true'. | |
3625 | */ | |
11fdf7f2 | 3626 | duk_tval *tv_val; |
7c673cae | 3627 | |
11fdf7f2 | 3628 | tv_val = DUK_GET_TVAL_POSIDX(ctx, res->x1.valstack_idx); |
7c673cae FG |
3629 | DUK_ASSERT(tv_val != NULL); |
3630 | if (DUK_TVAL_IS_NUMBER(tv_val)) { | |
3631 | duk_double_t d; | |
3632 | d = DUK_TVAL_GET_NUMBER(tv_val); | |
3633 | if (d == 0.0) { | |
3634 | /* Matches both +0 and -0 on purpose. */ | |
3635 | DUK_DDD(DUK_DDDPRINT("inlined lnot: !0 -> true")); | |
3636 | DUK_TVAL_SET_BOOLEAN_TRUE(tv_val); | |
3637 | return; | |
3638 | } else if (d == 1.0) { | |
3639 | DUK_DDD(DUK_DDDPRINT("inlined lnot: !1 -> false")); | |
3640 | DUK_TVAL_SET_BOOLEAN_FALSE(tv_val); | |
3641 | return; | |
3642 | } | |
3643 | } else if (DUK_TVAL_IS_BOOLEAN(tv_val)) { | |
3644 | duk_small_int_t v; | |
3645 | v = DUK_TVAL_GET_BOOLEAN(tv_val); | |
3646 | DUK_DDD(DUK_DDDPRINT("inlined lnot boolean: %ld", (long) v)); | |
3647 | DUK_ASSERT(v == 0 || v == 1); | |
3648 | DUK_TVAL_SET_BOOLEAN(tv_val, v ^ 0x01); | |
3649 | return; | |
3650 | } | |
3651 | } | |
3652 | args = (DUK_EXTRAOP_LNOT << 8) + 0; | |
3653 | goto unary_extraop; | |
3654 | } | |
3655 | ||
3656 | } /* end switch */ | |
3657 | ||
11fdf7f2 | 3658 | DUK_ERROR_SYNTAX(thr, DUK_STR_PARSE_ERROR); |
7c673cae FG |
3659 | return; |
3660 | ||
3661 | unary_extraop: | |
3662 | { | |
3663 | /* Note: must coerce to a (writable) temp register, so that e.g. "!x" where x | |
3664 | * is a reg-mapped variable works correctly (does not mutate the variable register). | |
3665 | */ | |
3666 | ||
3667 | duk_reg_t reg_temp; | |
3668 | reg_temp = duk__ivalue_toregconst_raw(comp_ctx, res, -1 /*forced_reg*/, DUK__IVAL_FLAG_REQUIRE_TEMP /*flags*/); | |
3669 | duk__emit_extraop_bc(comp_ctx, | |
3670 | (args >> 8), | |
3671 | (duk_regconst_t) reg_temp); | |
3672 | res->t = DUK_IVAL_PLAIN; | |
3673 | res->x1.t = DUK_ISPEC_REGCONST; | |
3674 | res->x1.regconst = (duk_regconst_t) reg_temp; | |
3675 | return; | |
3676 | } | |
3677 | ||
3678 | preincdec: | |
3679 | { | |
3680 | /* preincrement and predecrement */ | |
3681 | duk_reg_t reg_res; | |
3682 | duk_small_uint_t args_op = args >> 8; | |
3683 | ||
3684 | /* Specific assumptions for opcode numbering. */ | |
3685 | DUK_ASSERT(DUK_OP_PREINCR + 4 == DUK_OP_PREINCV); | |
3686 | DUK_ASSERT(DUK_OP_PREDECR + 4 == DUK_OP_PREDECV); | |
3687 | DUK_ASSERT(DUK_OP_PREINCR + 8 == DUK_OP_PREINCP); | |
3688 | DUK_ASSERT(DUK_OP_PREDECR + 8 == DUK_OP_PREDECP); | |
3689 | ||
3690 | reg_res = DUK__ALLOCTEMP(comp_ctx); | |
3691 | ||
3692 | duk__expr(comp_ctx, res, DUK__BP_MULTIPLICATIVE /*rbp_flags*/); /* UnaryExpression */ | |
3693 | if (res->t == DUK_IVAL_VAR) { | |
3694 | duk_hstring *h_varname; | |
3695 | duk_reg_t reg_varbind; | |
3696 | duk_regconst_t rc_varname; | |
3697 | ||
3698 | h_varname = duk_get_hstring(ctx, res->x1.valstack_idx); | |
3699 | DUK_ASSERT(h_varname != NULL); | |
3700 | ||
3701 | if (duk__hstring_is_eval_or_arguments_in_strict_mode(comp_ctx, h_varname)) { | |
3702 | goto syntax_error; | |
3703 | } | |
3704 | ||
3705 | duk_dup(ctx, res->x1.valstack_idx); | |
3706 | if (duk__lookup_lhs(comp_ctx, ®_varbind, &rc_varname)) { | |
3707 | duk__emit_a_bc(comp_ctx, | |
3708 | args_op, /* e.g. DUK_OP_PREINCR */ | |
3709 | (duk_regconst_t) reg_res, | |
3710 | (duk_regconst_t) reg_varbind); | |
3711 | } else { | |
3712 | duk__emit_a_bc(comp_ctx, | |
3713 | args_op + 4, /* e.g. DUK_OP_PREINCV */ | |
3714 | (duk_regconst_t) reg_res, | |
3715 | rc_varname); | |
3716 | } | |
3717 | ||
3718 | DUK_DDD(DUK_DDDPRINT("preincdec to '%!O' -> reg_varbind=%ld, rc_varname=%ld", | |
3719 | (duk_heaphdr *) h_varname, (long) reg_varbind, (long) rc_varname)); | |
3720 | } else if (res->t == DUK_IVAL_PROP) { | |
3721 | duk_reg_t reg_obj; /* allocate to reg only (not const) */ | |
3722 | duk_regconst_t rc_key; | |
3723 | reg_obj = duk__ispec_toregconst_raw(comp_ctx, &res->x1, -1 /*forced_reg*/, 0 /*flags*/); /* don't allow const */ | |
3724 | rc_key = duk__ispec_toregconst_raw(comp_ctx, &res->x2, -1 /*forced_reg*/, DUK__IVAL_FLAG_ALLOW_CONST /*flags*/); | |
3725 | duk__emit_a_b_c(comp_ctx, | |
3726 | args_op + 8, /* e.g. DUK_OP_PREINCP */ | |
3727 | (duk_regconst_t) reg_res, | |
3728 | (duk_regconst_t) reg_obj, | |
3729 | rc_key); | |
3730 | } else { | |
3731 | /* Technically return value is not needed because INVLHS will | |
3732 | * unconditially throw a ReferenceError. Coercion is necessary | |
3733 | * for proper semantics (consider ToNumber() called for an object). | |
3734 | * Use DUK_EXTRAOP_UNP with a dummy register to get ToNumber(). | |
3735 | */ | |
3736 | ||
3737 | duk__ivalue_toforcedreg(comp_ctx, res, reg_res); | |
3738 | duk__emit_extraop_bc(comp_ctx, | |
3739 | DUK_EXTRAOP_UNP, | |
3740 | reg_res); /* for side effects, result ignored */ | |
3741 | duk__emit_extraop_only(comp_ctx, | |
3742 | DUK_EXTRAOP_INVLHS); | |
3743 | } | |
3744 | res->t = DUK_IVAL_PLAIN; | |
3745 | res->x1.t = DUK_ISPEC_REGCONST; | |
3746 | res->x1.regconst = (duk_regconst_t) reg_res; | |
3747 | DUK__SETTEMP(comp_ctx, reg_res + 1); | |
3748 | return; | |
3749 | } | |
3750 | ||
3751 | plain_value: | |
3752 | { | |
3753 | /* Stack top contains plain value */ | |
3754 | res->t = DUK_IVAL_PLAIN; | |
3755 | res->x1.t = DUK_ISPEC_VALUE; | |
3756 | duk_replace(ctx, res->x1.valstack_idx); | |
3757 | return; | |
3758 | } | |
3759 | ||
3760 | syntax_error: | |
11fdf7f2 | 3761 | DUK_ERROR_SYNTAX(thr, DUK_STR_INVALID_EXPRESSION); |
7c673cae FG |
3762 | } |
3763 | ||
3764 | /* XXX: add flag to indicate whether caller cares about return value; this | |
3765 | * affects e.g. handling of assignment expressions. This change needs API | |
3766 | * changes elsewhere too. | |
3767 | */ | |
3768 | DUK_LOCAL void duk__expr_led(duk_compiler_ctx *comp_ctx, duk_ivalue *left, duk_ivalue *res) { | |
3769 | duk_hthread *thr = comp_ctx->thr; | |
3770 | duk_context *ctx = (duk_context *) thr; | |
3771 | duk_token *tk; | |
3772 | duk_small_int_t tok; | |
3773 | duk_uint32_t args; /* temp variable to pass constants and flags to shared code */ | |
3774 | ||
3775 | /* | |
3776 | * ctx->prev_token token to process with duk__expr_led() | |
3777 | * ctx->curr_token updated by caller | |
3778 | */ | |
3779 | ||
3780 | comp_ctx->curr_func.led_count++; | |
3781 | ||
3782 | /* The token in the switch has already been eaten here */ | |
3783 | tk = &comp_ctx->prev_token; | |
3784 | tok = tk->t; | |
3785 | ||
3786 | DUK_DDD(DUK_DDDPRINT("duk__expr_led(), prev_token.t=%ld, allow_in=%ld, paren_level=%ld", | |
3787 | (long) tk->t, (long) comp_ctx->curr_func.allow_in, (long) comp_ctx->curr_func.paren_level)); | |
3788 | ||
3789 | /* XXX: default priority for infix operators is duk__expr_lbp(tok) -> get it here? */ | |
3790 | ||
3791 | switch (tok) { | |
3792 | ||
3793 | /* PRIMARY EXPRESSIONS */ | |
3794 | ||
3795 | case DUK_TOK_PERIOD: { | |
3796 | /* Property access expressions are critical for correct LHS ordering, | |
3797 | * see comments in duk__expr()! | |
11fdf7f2 TL |
3798 | * |
3799 | * A conservative approach would be to use duk__ivalue_totempconst() | |
3800 | * for 'left'. However, allowing a reg-bound variable seems safe here | |
3801 | * and is nice because "foo.bar" is a common expression. If the ivalue | |
3802 | * is used in an expression a GETPROP will occur before any changes to | |
3803 | * the base value can occur. If the ivalue is used as an assignment | |
3804 | * LHS, the assignment code will ensure the base value is safe from | |
3805 | * RHS mutation. | |
7c673cae FG |
3806 | */ |
3807 | ||
11fdf7f2 | 3808 | /* XXX: This now coerces an identifier into a GETVAR to a temp, which |
7c673cae FG |
3809 | * causes an extra LDREG in call setup. It's sufficient to coerce to a |
3810 | * unary ivalue? | |
3811 | */ | |
3812 | duk__ivalue_toplain(comp_ctx, left); | |
3813 | ||
3814 | /* NB: must accept reserved words as property name */ | |
3815 | if (comp_ctx->curr_token.t_nores != DUK_TOK_IDENTIFIER) { | |
11fdf7f2 | 3816 | DUK_ERROR_SYNTAX(thr, DUK_STR_EXPECTED_IDENTIFIER); |
7c673cae FG |
3817 | } |
3818 | ||
3819 | res->t = DUK_IVAL_PROP; | |
3820 | duk__copy_ispec(comp_ctx, &left->x1, &res->x1); /* left.x1 -> res.x1 */ | |
3821 | DUK_ASSERT(comp_ctx->curr_token.str1 != NULL); | |
3822 | duk_push_hstring(ctx, comp_ctx->curr_token.str1); | |
3823 | duk_replace(ctx, res->x2.valstack_idx); | |
3824 | res->x2.t = DUK_ISPEC_VALUE; | |
3825 | ||
3826 | /* special RegExp literal handling after IdentifierName */ | |
3827 | comp_ctx->curr_func.reject_regexp_in_adv = 1; | |
3828 | ||
3829 | duk__advance(comp_ctx); | |
3830 | return; | |
3831 | } | |
3832 | case DUK_TOK_LBRACKET: { | |
3833 | /* Property access expressions are critical for correct LHS ordering, | |
3834 | * see comments in duk__expr()! | |
3835 | */ | |
3836 | ||
3837 | /* XXX: optimize temp reg use */ | |
3838 | /* XXX: similar coercion issue as in DUK_TOK_PERIOD */ | |
7c673cae FG |
3839 | /* XXX: coerce to regs? it might be better for enumeration use, where the |
3840 | * same PROP ivalue is used multiple times. Or perhaps coerce PROP further | |
3841 | * there? | |
3842 | */ | |
11fdf7f2 TL |
3843 | /* XXX: for simple cases like x['y'] an unnecessary LDREG is |
3844 | * emitted for the base value; could avoid it if we knew that | |
3845 | * the key expression is safe (e.g. just a single literal). | |
3846 | */ | |
7c673cae | 3847 | |
11fdf7f2 TL |
3848 | /* The 'left' value must not be a register bound variable |
3849 | * because it may be mutated during the rest of the expression | |
3850 | * and E5.1 Section 11.2.1 specifies the order of evaluation | |
3851 | * so that the base value is evaluated first. | |
3852 | * See: test-bug-nested-prop-mutate.js. | |
3853 | */ | |
3854 | duk__ivalue_totempconst(comp_ctx, left); | |
7c673cae FG |
3855 | duk__expr_toplain(comp_ctx, res, DUK__BP_FOR_EXPR /*rbp_flags*/); /* Expression, ']' terminates */ |
3856 | duk__advance_expect(comp_ctx, DUK_TOK_RBRACKET); | |
3857 | ||
3858 | res->t = DUK_IVAL_PROP; | |
3859 | duk__copy_ispec(comp_ctx, &res->x1, &res->x2); /* res.x1 -> res.x2 */ | |
3860 | duk__copy_ispec(comp_ctx, &left->x1, &res->x1); /* left.x1 -> res.x1 */ | |
3861 | return; | |
3862 | } | |
3863 | case DUK_TOK_LPAREN: { | |
3864 | /* function call */ | |
3865 | duk_reg_t reg_cs = DUK__ALLOCTEMPS(comp_ctx, 2); | |
3866 | duk_int_t nargs; | |
3867 | duk_small_uint_t call_flags = 0; | |
3868 | ||
3869 | /* | |
3870 | * XXX: attempt to get the call result to "next temp" whenever | |
3871 | * possible to avoid unnecessary register shuffles. | |
3872 | * | |
3873 | * XXX: CSPROP (and CSREG) can overwrite the call target register, and save one temp, | |
3874 | * if the call target is a temporary register and at the top of the temp reg "stack". | |
3875 | */ | |
3876 | ||
3877 | /* | |
3878 | * Setup call: target and 'this' binding. Three cases: | |
3879 | * | |
3880 | * 1. Identifier base (e.g. "foo()") | |
3881 | * 2. Property base (e.g. "foo.bar()") | |
3882 | * 3. Register base (e.g. "foo()()"; i.e. when a return value is a function) | |
3883 | */ | |
3884 | ||
3885 | if (left->t == DUK_IVAL_VAR) { | |
3886 | duk_hstring *h_varname; | |
3887 | duk_reg_t reg_varbind; | |
3888 | duk_regconst_t rc_varname; | |
3889 | ||
3890 | DUK_DDD(DUK_DDDPRINT("function call with identifier base")); | |
3891 | ||
3892 | h_varname = duk_get_hstring(ctx, left->x1.valstack_idx); | |
3893 | DUK_ASSERT(h_varname != NULL); | |
3894 | if (h_varname == DUK_HTHREAD_STRING_EVAL(thr)) { | |
3895 | /* Potential direct eval call detected, flag the CALL | |
3896 | * so that a run-time "direct eval" check is made and | |
3897 | * special behavior may be triggered. Note that this | |
3898 | * does not prevent 'eval' from being register bound. | |
3899 | */ | |
3900 | DUK_DDD(DUK_DDDPRINT("function call with identifier 'eval' " | |
3901 | "-> enabling EVALCALL flag, marking function " | |
3902 | "as may_direct_eval")); | |
3903 | call_flags |= DUK_BC_CALL_FLAG_EVALCALL; | |
3904 | ||
3905 | comp_ctx->curr_func.may_direct_eval = 1; | |
3906 | } | |
3907 | ||
3908 | duk_dup(ctx, left->x1.valstack_idx); | |
3909 | if (duk__lookup_lhs(comp_ctx, ®_varbind, &rc_varname)) { | |
3910 | duk__emit_a_b(comp_ctx, | |
3911 | DUK_OP_CSREG, | |
3912 | (duk_regconst_t) (reg_cs + 0), | |
3913 | (duk_regconst_t) reg_varbind); | |
3914 | } else { | |
3915 | duk__emit_a_b(comp_ctx, | |
3916 | DUK_OP_CSVAR, | |
3917 | (duk_regconst_t) (reg_cs + 0), | |
3918 | rc_varname); | |
3919 | } | |
3920 | } else if (left->t == DUK_IVAL_PROP) { | |
3921 | DUK_DDD(DUK_DDDPRINT("function call with property base")); | |
3922 | ||
3923 | duk__ispec_toforcedreg(comp_ctx, &left->x1, reg_cs + 0); /* base */ | |
3924 | duk__ispec_toforcedreg(comp_ctx, &left->x2, reg_cs + 1); /* key */ | |
3925 | duk__emit_a_b_c(comp_ctx, | |
3926 | DUK_OP_CSPROP, | |
3927 | (duk_regconst_t) (reg_cs + 0), | |
3928 | (duk_regconst_t) (reg_cs + 0), | |
3929 | (duk_regconst_t) (reg_cs + 1)); /* in-place setup */ | |
3930 | } else { | |
3931 | DUK_DDD(DUK_DDDPRINT("function call with register base")); | |
3932 | ||
3933 | duk__ivalue_toforcedreg(comp_ctx, left, reg_cs + 0); | |
3934 | duk__emit_a_b(comp_ctx, | |
3935 | DUK_OP_CSREG, | |
3936 | (duk_regconst_t) (reg_cs + 0), | |
3937 | (duk_regconst_t) (reg_cs + 0)); /* in-place setup */ | |
3938 | } | |
3939 | ||
3940 | DUK__SETTEMP(comp_ctx, reg_cs + 2); | |
3941 | nargs = duk__parse_arguments(comp_ctx, res); /* parse args starting from "next temp" */ | |
3942 | ||
3943 | /* Tailcalls are handled by back-patching the TAILCALL flag to the | |
3944 | * already emitted instruction later (in return statement parser). | |
3945 | * Since A and C have a special meaning here, they cannot be "shuffled". | |
3946 | */ | |
3947 | ||
3948 | duk__emit_a_b_c(comp_ctx, | |
3949 | DUK_OP_CALL | DUK__EMIT_FLAG_NO_SHUFFLE_A | DUK__EMIT_FLAG_NO_SHUFFLE_C, | |
3950 | (duk_regconst_t) call_flags /*flags*/, | |
3951 | (duk_regconst_t) reg_cs /*basereg*/, | |
3952 | (duk_regconst_t) nargs /*numargs*/); | |
3953 | DUK__SETTEMP(comp_ctx, reg_cs + 1); /* result in csreg */ | |
3954 | ||
3955 | res->t = DUK_IVAL_PLAIN; | |
3956 | res->x1.t = DUK_ISPEC_REGCONST; | |
3957 | res->x1.regconst = (duk_regconst_t) reg_cs; | |
3958 | return; | |
3959 | } | |
3960 | ||
3961 | /* POSTFIX EXPRESSION */ | |
3962 | ||
3963 | case DUK_TOK_INCREMENT: { | |
3964 | args = (DUK_OP_POSTINCR << 8) + 0; | |
3965 | goto postincdec; | |
3966 | } | |
3967 | case DUK_TOK_DECREMENT: { | |
3968 | args = (DUK_OP_POSTDECR << 8) + 0; | |
3969 | goto postincdec; | |
3970 | } | |
3971 | ||
3972 | /* MULTIPLICATIVE EXPRESSION */ | |
3973 | ||
3974 | case DUK_TOK_MUL: { | |
3975 | args = (DUK_OP_MUL << 8) + DUK__BP_MULTIPLICATIVE; /* UnaryExpression */ | |
3976 | goto binary; | |
3977 | } | |
3978 | case DUK_TOK_DIV: { | |
3979 | args = (DUK_OP_DIV << 8) + DUK__BP_MULTIPLICATIVE; /* UnaryExpression */ | |
3980 | goto binary; | |
3981 | } | |
3982 | case DUK_TOK_MOD: { | |
3983 | args = (DUK_OP_MOD << 8) + DUK__BP_MULTIPLICATIVE; /* UnaryExpression */ | |
3984 | goto binary; | |
3985 | } | |
3986 | ||
3987 | /* ADDITIVE EXPRESSION */ | |
3988 | ||
3989 | case DUK_TOK_ADD: { | |
3990 | args = (DUK_OP_ADD << 8) + DUK__BP_ADDITIVE; /* MultiplicativeExpression */ | |
3991 | goto binary; | |
3992 | } | |
3993 | case DUK_TOK_SUB: { | |
3994 | args = (DUK_OP_SUB << 8) + DUK__BP_ADDITIVE; /* MultiplicativeExpression */ | |
3995 | goto binary; | |
3996 | } | |
3997 | ||
3998 | /* SHIFT EXPRESSION */ | |
3999 | ||
4000 | case DUK_TOK_ALSHIFT: { | |
4001 | /* << */ | |
4002 | args = (DUK_OP_BASL << 8) + DUK__BP_SHIFT; | |
4003 | goto binary; | |
4004 | } | |
4005 | case DUK_TOK_ARSHIFT: { | |
4006 | /* >> */ | |
4007 | args = (DUK_OP_BASR << 8) + DUK__BP_SHIFT; | |
4008 | goto binary; | |
4009 | } | |
4010 | case DUK_TOK_RSHIFT: { | |
4011 | /* >>> */ | |
4012 | args = (DUK_OP_BLSR << 8) + DUK__BP_SHIFT; | |
4013 | goto binary; | |
4014 | } | |
4015 | ||
4016 | /* RELATIONAL EXPRESSION */ | |
4017 | ||
4018 | case DUK_TOK_LT: { | |
4019 | /* < */ | |
4020 | args = (DUK_OP_LT << 8) + DUK__BP_RELATIONAL; | |
4021 | goto binary; | |
4022 | } | |
4023 | case DUK_TOK_GT: { | |
4024 | args = (DUK_OP_GT << 8) + DUK__BP_RELATIONAL; | |
4025 | goto binary; | |
4026 | } | |
4027 | case DUK_TOK_LE: { | |
4028 | args = (DUK_OP_LE << 8) + DUK__BP_RELATIONAL; | |
4029 | goto binary; | |
4030 | } | |
4031 | case DUK_TOK_GE: { | |
4032 | args = (DUK_OP_GE << 8) + DUK__BP_RELATIONAL; | |
4033 | goto binary; | |
4034 | } | |
4035 | case DUK_TOK_INSTANCEOF: { | |
4036 | args = (1 << 16 /*is_extra*/) + (DUK_EXTRAOP_INSTOF << 8) + DUK__BP_RELATIONAL; | |
4037 | goto binary; | |
4038 | } | |
4039 | case DUK_TOK_IN: { | |
4040 | args = (1 << 16 /*is_extra*/) + (DUK_EXTRAOP_IN << 8) + DUK__BP_RELATIONAL; | |
4041 | goto binary; | |
4042 | } | |
4043 | ||
4044 | /* EQUALITY EXPRESSION */ | |
4045 | ||
4046 | case DUK_TOK_EQ: { | |
4047 | args = (DUK_OP_EQ << 8) + DUK__BP_EQUALITY; | |
4048 | goto binary; | |
4049 | } | |
4050 | case DUK_TOK_NEQ: { | |
4051 | args = (DUK_OP_NEQ << 8) + DUK__BP_EQUALITY; | |
4052 | goto binary; | |
4053 | } | |
4054 | case DUK_TOK_SEQ: { | |
4055 | args = (DUK_OP_SEQ << 8) + DUK__BP_EQUALITY; | |
4056 | goto binary; | |
4057 | } | |
4058 | case DUK_TOK_SNEQ: { | |
4059 | args = (DUK_OP_SNEQ << 8) + DUK__BP_EQUALITY; | |
4060 | goto binary; | |
4061 | } | |
4062 | ||
4063 | /* BITWISE EXPRESSIONS */ | |
4064 | ||
4065 | case DUK_TOK_BAND: { | |
4066 | args = (DUK_OP_BAND << 8) + DUK__BP_BAND; | |
4067 | goto binary; | |
4068 | } | |
4069 | case DUK_TOK_BXOR: { | |
4070 | args = (DUK_OP_BXOR << 8) + DUK__BP_BXOR; | |
4071 | goto binary; | |
4072 | } | |
4073 | case DUK_TOK_BOR: { | |
4074 | args = (DUK_OP_BOR << 8) + DUK__BP_BOR; | |
4075 | goto binary; | |
4076 | } | |
4077 | ||
4078 | /* LOGICAL EXPRESSIONS */ | |
4079 | ||
4080 | case DUK_TOK_LAND: { | |
4081 | /* syntactically left-associative but parsed as right-associative */ | |
4082 | args = (1 << 8) + DUK__BP_LAND - 1; | |
4083 | goto binary_logical; | |
4084 | } | |
4085 | case DUK_TOK_LOR: { | |
4086 | /* syntactically left-associative but parsed as right-associative */ | |
4087 | args = (0 << 8) + DUK__BP_LOR - 1; | |
4088 | goto binary_logical; | |
4089 | } | |
4090 | ||
4091 | /* CONDITIONAL EXPRESSION */ | |
4092 | ||
4093 | case DUK_TOK_QUESTION: { | |
4094 | /* XXX: common reg allocation need is to reuse a sub-expression's temp reg, | |
4095 | * but only if it really is a temp. Nothing fancy here now. | |
4096 | */ | |
4097 | duk_reg_t reg_temp; | |
4098 | duk_int_t pc_jump1; | |
4099 | duk_int_t pc_jump2; | |
4100 | ||
4101 | reg_temp = DUK__ALLOCTEMP(comp_ctx); | |
4102 | duk__ivalue_toforcedreg(comp_ctx, left, reg_temp); | |
4103 | duk__emit_if_true_skip(comp_ctx, reg_temp); | |
4104 | pc_jump1 = duk__emit_jump_empty(comp_ctx); /* jump to false */ | |
4105 | duk__expr_toforcedreg(comp_ctx, res, DUK__BP_COMMA /*rbp_flags*/, reg_temp /*forced_reg*/); /* AssignmentExpression */ | |
4106 | duk__advance_expect(comp_ctx, DUK_TOK_COLON); | |
4107 | pc_jump2 = duk__emit_jump_empty(comp_ctx); /* jump to end */ | |
4108 | duk__patch_jump_here(comp_ctx, pc_jump1); | |
4109 | duk__expr_toforcedreg(comp_ctx, res, DUK__BP_COMMA /*rbp_flags*/, reg_temp /*forced_reg*/); /* AssignmentExpression */ | |
4110 | duk__patch_jump_here(comp_ctx, pc_jump2); | |
4111 | ||
4112 | DUK__SETTEMP(comp_ctx, reg_temp + 1); | |
4113 | res->t = DUK_IVAL_PLAIN; | |
4114 | res->x1.t = DUK_ISPEC_REGCONST; | |
4115 | res->x1.regconst = (duk_regconst_t) reg_temp; | |
4116 | return; | |
4117 | } | |
4118 | ||
4119 | /* ASSIGNMENT EXPRESSION */ | |
4120 | ||
4121 | case DUK_TOK_EQUALSIGN: { | |
4122 | /* | |
4123 | * Assignments are right associative, allows e.g. | |
4124 | * a = 5; | |
4125 | * a += b = 9; // same as a += (b = 9) | |
4126 | * -> expression value 14, a = 14, b = 9 | |
4127 | * | |
4128 | * Right associativiness is reflected in the BP for recursion, | |
4129 | * "-1" ensures assignment operations are allowed. | |
4130 | * | |
4131 | * XXX: just use DUK__BP_COMMA (i.e. no need for 2-step bp levels)? | |
4132 | */ | |
4133 | args = (DUK_OP_NONE << 8) + DUK__BP_ASSIGNMENT - 1; /* DUK_OP_NONE marks a 'plain' assignment */ | |
4134 | goto assign; | |
4135 | } | |
4136 | case DUK_TOK_ADD_EQ: { | |
4137 | /* right associative */ | |
4138 | args = (DUK_OP_ADD << 8) + DUK__BP_ASSIGNMENT - 1; | |
4139 | goto assign; | |
4140 | } | |
4141 | case DUK_TOK_SUB_EQ: { | |
4142 | /* right associative */ | |
4143 | args = (DUK_OP_SUB << 8) + DUK__BP_ASSIGNMENT - 1; | |
4144 | goto assign; | |
4145 | } | |
4146 | case DUK_TOK_MUL_EQ: { | |
4147 | /* right associative */ | |
4148 | args = (DUK_OP_MUL << 8) + DUK__BP_ASSIGNMENT - 1; | |
4149 | goto assign; | |
4150 | } | |
4151 | case DUK_TOK_DIV_EQ: { | |
4152 | /* right associative */ | |
4153 | args = (DUK_OP_DIV << 8) + DUK__BP_ASSIGNMENT - 1; | |
4154 | goto assign; | |
4155 | } | |
4156 | case DUK_TOK_MOD_EQ: { | |
4157 | /* right associative */ | |
4158 | args = (DUK_OP_MOD << 8) + DUK__BP_ASSIGNMENT - 1; | |
4159 | goto assign; | |
4160 | } | |
4161 | case DUK_TOK_ALSHIFT_EQ: { | |
4162 | /* right associative */ | |
4163 | args = (DUK_OP_BASL << 8) + DUK__BP_ASSIGNMENT - 1; | |
4164 | goto assign; | |
4165 | } | |
4166 | case DUK_TOK_ARSHIFT_EQ: { | |
4167 | /* right associative */ | |
4168 | args = (DUK_OP_BASR << 8) + DUK__BP_ASSIGNMENT - 1; | |
4169 | goto assign; | |
4170 | } | |
4171 | case DUK_TOK_RSHIFT_EQ: { | |
4172 | /* right associative */ | |
4173 | args = (DUK_OP_BLSR << 8) + DUK__BP_ASSIGNMENT - 1; | |
4174 | goto assign; | |
4175 | } | |
4176 | case DUK_TOK_BAND_EQ: { | |
4177 | /* right associative */ | |
4178 | args = (DUK_OP_BAND << 8) + DUK__BP_ASSIGNMENT - 1; | |
4179 | goto assign; | |
4180 | } | |
4181 | case DUK_TOK_BOR_EQ: { | |
4182 | /* right associative */ | |
4183 | args = (DUK_OP_BOR << 8) + DUK__BP_ASSIGNMENT - 1; | |
4184 | goto assign; | |
4185 | } | |
4186 | case DUK_TOK_BXOR_EQ: { | |
4187 | /* right associative */ | |
4188 | args = (DUK_OP_BXOR << 8) + DUK__BP_ASSIGNMENT - 1; | |
4189 | goto assign; | |
4190 | } | |
4191 | ||
4192 | /* COMMA */ | |
4193 | ||
4194 | case DUK_TOK_COMMA: { | |
4195 | /* right associative */ | |
4196 | ||
4197 | duk__ivalue_toplain_ignore(comp_ctx, left); /* need side effects, not value */ | |
4198 | duk__expr_toplain(comp_ctx, res, DUK__BP_COMMA - 1 /*rbp_flags*/); | |
4199 | ||
4200 | /* return 'res' (of right part) as our result */ | |
4201 | return; | |
4202 | } | |
4203 | ||
4204 | default: { | |
4205 | break; | |
4206 | } | |
4207 | } | |
4208 | ||
4209 | DUK_D(DUK_DPRINT("parse error: unexpected token: %ld", (long) tok)); | |
11fdf7f2 | 4210 | DUK_ERROR_SYNTAX(thr, DUK_STR_PARSE_ERROR); |
7c673cae FG |
4211 | return; |
4212 | ||
4213 | #if 0 | |
4214 | /* XXX: shared handling for 'duk__expr_lhs'? */ | |
4215 | if (comp_ctx->curr_func.paren_level == 0 && XXX) { | |
4216 | comp_ctx->curr_func.duk__expr_lhs = 0; | |
4217 | } | |
4218 | #endif | |
4219 | ||
4220 | binary: | |
4221 | /* | |
4222 | * Shared handling of binary operations | |
4223 | * | |
4224 | * args = (is_extraop << 16) + (opcode << 8) + rbp | |
4225 | */ | |
4226 | { | |
4227 | duk__ivalue_toplain(comp_ctx, left); | |
4228 | duk__expr_toplain(comp_ctx, res, args & 0xff /*rbp_flags*/); | |
4229 | ||
4230 | /* combine left->x1 and res->x1 (right->x1, really) -> (left->x1 OP res->x1) */ | |
4231 | DUK_ASSERT(left->t == DUK_IVAL_PLAIN); | |
4232 | DUK_ASSERT(res->t == DUK_IVAL_PLAIN); | |
4233 | ||
4234 | res->t = (args >> 16) ? DUK_IVAL_ARITH_EXTRAOP : DUK_IVAL_ARITH; | |
4235 | res->op = (args >> 8) & 0xff; | |
4236 | ||
4237 | res->x2.t = res->x1.t; | |
4238 | res->x2.regconst = res->x1.regconst; | |
4239 | duk_copy(ctx, res->x1.valstack_idx, res->x2.valstack_idx); | |
4240 | ||
4241 | res->x1.t = left->x1.t; | |
4242 | res->x1.regconst = left->x1.regconst; | |
4243 | duk_copy(ctx, left->x1.valstack_idx, res->x1.valstack_idx); | |
4244 | ||
4245 | DUK_DDD(DUK_DDDPRINT("binary op, res: t=%ld, x1.t=%ld, x1.regconst=0x%08lx, x2.t=%ld, x2.regconst=0x%08lx", | |
4246 | (long) res->t, (long) res->x1.t, (unsigned long) res->x1.regconst, (long) res->x2.t, (unsigned long) res->x2.regconst)); | |
4247 | return; | |
4248 | } | |
4249 | ||
4250 | binary_logical: | |
4251 | /* | |
4252 | * Shared handling for logical AND and logical OR. | |
4253 | * | |
4254 | * args = (truthval << 8) + rbp | |
4255 | * | |
4256 | * Truthval determines when to skip right-hand-side. | |
4257 | * For logical AND truthval=1, for logical OR truthval=0. | |
4258 | * | |
4259 | * See doc/compiler.rst for discussion on compiling logical | |
4260 | * AND and OR expressions. The approach here is very simplistic, | |
4261 | * generating extra jumps and multiple evaluations of truth values, | |
4262 | * but generates code on-the-fly with only local back-patching. | |
4263 | * | |
4264 | * Both logical AND and OR are syntactically left-associated. | |
4265 | * However, logical ANDs are compiled as right associative | |
4266 | * expressions, i.e. "A && B && C" as "A && (B && C)", to allow | |
4267 | * skip jumps to skip over the entire tail. Similarly for logical OR. | |
4268 | */ | |
4269 | ||
4270 | { | |
4271 | duk_reg_t reg_temp; | |
4272 | duk_int_t pc_jump; | |
4273 | duk_small_uint_t args_truthval = args >> 8; | |
4274 | duk_small_uint_t args_rbp = args & 0xff; | |
4275 | ||
4276 | /* XXX: unoptimal use of temps, resetting */ | |
4277 | ||
4278 | reg_temp = DUK__ALLOCTEMP(comp_ctx); | |
4279 | ||
4280 | duk__ivalue_toforcedreg(comp_ctx, left, reg_temp); | |
4281 | duk__emit_a_b(comp_ctx, | |
4282 | DUK_OP_IF | DUK__EMIT_FLAG_NO_SHUFFLE_A, | |
4283 | (duk_regconst_t) args_truthval, | |
4284 | (duk_regconst_t) reg_temp); /* skip jump conditionally */ | |
4285 | pc_jump = duk__emit_jump_empty(comp_ctx); | |
4286 | duk__expr_toforcedreg(comp_ctx, res, args_rbp /*rbp_flags*/, reg_temp /*forced_reg*/); | |
4287 | duk__patch_jump_here(comp_ctx, pc_jump); | |
4288 | ||
4289 | res->t = DUK_IVAL_PLAIN; | |
4290 | res->x1.t = DUK_ISPEC_REGCONST; | |
4291 | res->x1.regconst = (duk_regconst_t) reg_temp; | |
4292 | return; | |
4293 | } | |
4294 | ||
4295 | assign: | |
4296 | /* | |
4297 | * Shared assignment expression handling | |
4298 | * | |
4299 | * args = (opcode << 8) + rbp | |
4300 | * | |
4301 | * If 'opcode' is DUK_OP_NONE, plain assignment without arithmetic. | |
4302 | * Syntactically valid left-hand-side forms which are not accepted as | |
4303 | * left-hand-side values (e.g. as in "f() = 1") must NOT cause a | |
4304 | * SyntaxError, but rather a run-time ReferenceError. | |
11fdf7f2 TL |
4305 | * |
4306 | * When evaluating X <op>= Y, the LHS (X) is conceptually evaluated | |
4307 | * to a temporary first. The RHS is then evaluated. Finally, the | |
4308 | * <op> is applied to the initial value of RHS (not the value after | |
4309 | * RHS evaluation), and written to X. Doing so concretely generates | |
4310 | * inefficient code so we'd like to avoid the temporary when possible. | |
4311 | * See: https://github.com/svaarala/duktape/pull/992. | |
4312 | * | |
4313 | * The expression value (final LHS value, written to RHS) is | |
4314 | * conceptually copied into a fresh temporary so that it won't | |
4315 | * change even if the LHS/RHS values change in outer expressions. | |
4316 | * For example, it'd be generally incorrect for the expression value | |
4317 | * to be the RHS register binding, unless there's a guarantee that it | |
4318 | * won't change during further expression evaluation. Using the | |
4319 | * temporary concretely produces inefficient bytecode, so we try to | |
4320 | * avoid the extra temporary for some known-to-be-safe cases. | |
4321 | * Currently the only safe case we detect is a "top level assignment", | |
4322 | * for example "x = y + z;", where the assignment expression value is | |
4323 | * ignored. | |
4324 | * See: test-dev-assign-expr.js and test-bug-assign-mutate-gh381.js. | |
7c673cae FG |
4325 | */ |
4326 | ||
4327 | { | |
4328 | duk_small_uint_t args_op = args >> 8; | |
4329 | duk_small_uint_t args_rbp = args & 0xff; | |
11fdf7f2 | 4330 | duk_bool_t toplevel_assign; |
7c673cae FG |
4331 | |
4332 | /* XXX: here we need to know if 'left' is left-hand-side compatible. | |
4333 | * That information is no longer available from current expr parsing | |
4334 | * state; it would need to be carried into the 'left' ivalue or by | |
4335 | * some other means. | |
4336 | */ | |
4337 | ||
11fdf7f2 TL |
4338 | /* A top-level assignment is e.g. "x = y;". For these it's safe |
4339 | * to use the RHS as-is as the expression value, even if the RHS | |
4340 | * is a reg-bound identifier. The RHS ('res') is right associative | |
4341 | * so it has consumed all other assignment level operations; the | |
4342 | * only relevant lower binding power construct is comma operator | |
4343 | * which will ignore the expression value provided here. Usually | |
4344 | * the top level assignment expression value is ignored, but it | |
4345 | * is relevant for e.g. eval code. | |
4346 | */ | |
4347 | toplevel_assign = (comp_ctx->curr_func.nud_count == 1 && /* one token before */ | |
4348 | comp_ctx->curr_func.led_count == 1); /* one operator (= assign) */ | |
4349 | DUK_DDD(DUK_DDDPRINT("assignment: nud_count=%ld, led_count=%ld, toplevel_assign=%ld", | |
4350 | (long) comp_ctx->curr_func.nud_count, | |
4351 | (long) comp_ctx->curr_func.led_count, | |
4352 | (long) toplevel_assign)); | |
4353 | ||
7c673cae FG |
4354 | if (left->t == DUK_IVAL_VAR) { |
4355 | duk_hstring *h_varname; | |
4356 | duk_reg_t reg_varbind; | |
4357 | duk_regconst_t rc_varname; | |
7c673cae | 4358 | |
11fdf7f2 | 4359 | DUK_ASSERT(left->x1.t == DUK_ISPEC_VALUE); /* LHS is already side effect free */ |
7c673cae FG |
4360 | |
4361 | h_varname = duk_get_hstring(ctx, left->x1.valstack_idx); | |
4362 | DUK_ASSERT(h_varname != NULL); | |
7c673cae | 4363 | if (duk__hstring_is_eval_or_arguments_in_strict_mode(comp_ctx, h_varname)) { |
11fdf7f2 | 4364 | /* E5 Section 11.13.1 (and others for other assignments), step 4. */ |
7c673cae FG |
4365 | goto syntax_error_lvalue; |
4366 | } | |
7c673cae FG |
4367 | duk_dup(ctx, left->x1.valstack_idx); |
4368 | (void) duk__lookup_lhs(comp_ctx, ®_varbind, &rc_varname); | |
4369 | ||
7c673cae | 4370 | if (args_op == DUK_OP_NONE) { |
11fdf7f2 TL |
4371 | duk__expr(comp_ctx, res, args_rbp /*rbp_flags*/); |
4372 | if (toplevel_assign) { | |
4373 | /* Any 'res' will do. */ | |
4374 | DUK_DDD(DUK_DDDPRINT("plain assignment, toplevel assign, use as is")); | |
4375 | } else { | |
4376 | /* 'res' must be a plain ivalue, and not register-bound variable. */ | |
4377 | DUK_DDD(DUK_DDDPRINT("plain assignment, not toplevel assign, ensure not a reg-bound identifier")); | |
4378 | if (res->t != DUK_IVAL_PLAIN || (res->x1.t == DUK_ISPEC_REGCONST && | |
4379 | (res->x1.regconst & DUK__CONST_MARKER) == 0 && | |
4380 | !DUK__ISTEMP(comp_ctx, res->x1.regconst))) { | |
4381 | duk__ivalue_totempconst(comp_ctx, res); | |
4382 | } | |
4383 | } | |
7c673cae | 4384 | } else { |
11fdf7f2 TL |
4385 | /* For X <op>= Y we need to evaluate the pre-op |
4386 | * value of X before evaluating the RHS: the RHS | |
4387 | * can change X, but when we do <op> we must use | |
4388 | * the pre-op value. | |
4389 | */ | |
4390 | duk_reg_t reg_temp; | |
4391 | ||
7c673cae | 4392 | reg_temp = DUK__ALLOCTEMP(comp_ctx); |
11fdf7f2 | 4393 | |
7c673cae | 4394 | if (reg_varbind >= 0) { |
11fdf7f2 TL |
4395 | duk_reg_t reg_res; |
4396 | duk_reg_t reg_src; | |
4397 | duk_int_t pc_temp_load; | |
4398 | duk_int_t pc_before_rhs; | |
4399 | duk_int_t pc_after_rhs; | |
4400 | ||
4401 | if (toplevel_assign) { | |
4402 | /* 'reg_varbind' is the operation result and can also | |
4403 | * become the expression value for top level assignments | |
4404 | * such as: "var x; x += y;". | |
4405 | */ | |
4406 | DUK_DD(DUK_DDPRINT("<op>= expression is top level, write directly to reg_varbind")); | |
4407 | reg_res = reg_varbind; | |
4408 | } else { | |
4409 | /* Not safe to use 'reg_varbind' as assignment expression | |
4410 | * value, so go through a temp. | |
4411 | */ | |
4412 | DUK_DD(DUK_DDPRINT("<op>= expression is not top level, write to reg_temp")); | |
4413 | reg_res = reg_temp; /* reg_res should be smallest possible */ | |
4414 | reg_temp = DUK__ALLOCTEMP(comp_ctx); | |
4415 | } | |
4416 | ||
4417 | /* Try to optimize X <op>= Y for reg-bound | |
4418 | * variables. Detect side-effect free RHS | |
4419 | * narrowly by seeing whether it emits code. | |
4420 | * If not, rewind the code emitter and overwrite | |
4421 | * the unnecessary temp reg load. | |
4422 | */ | |
4423 | ||
4424 | pc_temp_load = duk__get_current_pc(comp_ctx); | |
4425 | duk__emit_a_bc(comp_ctx, | |
4426 | DUK_OP_LDREG, | |
4427 | (duk_regconst_t) reg_temp, | |
4428 | reg_varbind); | |
4429 | ||
4430 | pc_before_rhs = duk__get_current_pc(comp_ctx); | |
4431 | duk__expr_toregconst(comp_ctx, res, args_rbp /*rbp_flags*/); | |
4432 | DUK_ASSERT(res->t == DUK_IVAL_PLAIN && res->x1.t == DUK_ISPEC_REGCONST); | |
4433 | pc_after_rhs = duk__get_current_pc(comp_ctx); | |
4434 | ||
4435 | DUK_DD(DUK_DDPRINT("pc_temp_load=%ld, pc_before_rhs=%ld, pc_after_rhs=%ld", | |
4436 | (long) pc_temp_load, (long) pc_before_rhs, | |
4437 | (long) pc_after_rhs)); | |
4438 | ||
4439 | if (pc_after_rhs == pc_before_rhs) { | |
4440 | /* Note: if the reg_temp load generated shuffling | |
4441 | * instructions, we may need to rewind more than | |
4442 | * one instruction, so use explicit PC computation. | |
4443 | */ | |
4444 | DUK_DD(DUK_DDPRINT("rhs is side effect free, rewind and avoid unnecessary temp for reg-based <op>=")); | |
4445 | DUK_BW_ADD_PTR(comp_ctx->thr, &comp_ctx->curr_func.bw_code, (pc_temp_load - pc_before_rhs) * sizeof(duk_compiler_instr)); | |
4446 | reg_src = reg_varbind; | |
4447 | } else { | |
4448 | DUK_DD(DUK_DDPRINT("rhs evaluation emitted code, not sure if rhs is side effect free; use temp reg for LHS")); | |
4449 | reg_src = reg_temp; | |
4450 | } | |
4451 | ||
7c673cae FG |
4452 | duk__emit_a_b_c(comp_ctx, |
4453 | args_op, | |
11fdf7f2 TL |
4454 | (duk_regconst_t) reg_res, |
4455 | (duk_regconst_t) reg_src, | |
7c673cae | 4456 | res->x1.regconst); |
11fdf7f2 TL |
4457 | |
4458 | res->x1.regconst = (duk_regconst_t) reg_res; | |
4459 | ||
4460 | /* Ensure compact use of temps. */ | |
4461 | if (DUK__ISTEMP(comp_ctx, reg_res)) { | |
4462 | DUK__SETTEMP(comp_ctx, reg_res + 1); | |
4463 | } | |
7c673cae | 4464 | } else { |
11fdf7f2 TL |
4465 | /* When LHS is not register bound, always go through a |
4466 | * temporary. No optimization for top level assignment. | |
4467 | */ | |
4468 | ||
7c673cae FG |
4469 | duk__emit_a_bc(comp_ctx, |
4470 | DUK_OP_GETVAR, | |
4471 | (duk_regconst_t) reg_temp, | |
4472 | rc_varname); | |
11fdf7f2 TL |
4473 | |
4474 | duk__expr_toregconst(comp_ctx, res, args_rbp /*rbp_flags*/); | |
4475 | DUK_ASSERT(res->t == DUK_IVAL_PLAIN && res->x1.t == DUK_ISPEC_REGCONST); | |
4476 | ||
7c673cae FG |
4477 | duk__emit_a_b_c(comp_ctx, |
4478 | args_op, | |
4479 | (duk_regconst_t) reg_temp, | |
4480 | (duk_regconst_t) reg_temp, | |
4481 | res->x1.regconst); | |
11fdf7f2 | 4482 | res->x1.regconst = (duk_regconst_t) reg_temp; |
7c673cae | 4483 | } |
11fdf7f2 TL |
4484 | |
4485 | DUK_ASSERT(res->t == DUK_IVAL_PLAIN && res->x1.t == DUK_ISPEC_REGCONST); | |
7c673cae FG |
4486 | } |
4487 | ||
11fdf7f2 TL |
4488 | /* At this point 'res' holds the potential expression value. |
4489 | * It can be basically any ivalue here, including a reg-bound | |
4490 | * identifier (if code above deems it safe) or a unary/binary | |
4491 | * operation. Operations must be resolved to a side effect free | |
4492 | * plain value, and the side effects must happen exactly once. | |
4493 | */ | |
4494 | ||
7c673cae | 4495 | if (reg_varbind >= 0) { |
11fdf7f2 TL |
4496 | if (res->t != DUK_IVAL_PLAIN) { |
4497 | /* Resolve 'res' directly into the LHS binding, and use | |
4498 | * that as the expression value if safe. If not safe, | |
4499 | * resolve to a temp/const and copy to LHS. | |
4500 | */ | |
4501 | if (toplevel_assign) { | |
4502 | duk__ivalue_toforcedreg(comp_ctx, res, (duk_int_t) reg_varbind); | |
4503 | } else { | |
4504 | duk__ivalue_totempconst(comp_ctx, res); | |
4505 | duk__copy_ivalue(comp_ctx, res, left); /* use 'left' as a temp */ | |
4506 | duk__ivalue_toforcedreg(comp_ctx, left, (duk_int_t) reg_varbind); | |
4507 | } | |
4508 | } else { | |
4509 | /* Use 'res' as the expression value (it's side effect | |
4510 | * free and may be a plain value, a register, or a | |
4511 | * constant) and write it to the LHS binding too. | |
4512 | */ | |
4513 | duk__copy_ivalue(comp_ctx, res, left); /* use 'left' as a temp */ | |
4514 | duk__ivalue_toforcedreg(comp_ctx, left, (duk_int_t) reg_varbind); | |
4515 | } | |
7c673cae | 4516 | } else { |
11fdf7f2 TL |
4517 | /* Only a reg fits into 'A' so coerce 'res' into a register |
4518 | * for PUTVAR. | |
7c673cae FG |
4519 | * |
4520 | * XXX: here the current A/B/C split is suboptimal: we could | |
4521 | * just use 9 bits for reg_res (and support constants) and 17 | |
4522 | * instead of 18 bits for the varname const index. | |
4523 | */ | |
11fdf7f2 TL |
4524 | |
4525 | duk__ivalue_toreg(comp_ctx, res); | |
7c673cae FG |
4526 | duk__emit_a_bc(comp_ctx, |
4527 | DUK_OP_PUTVAR | DUK__EMIT_FLAG_A_IS_SOURCE, | |
11fdf7f2 | 4528 | res->x1.regconst, |
7c673cae FG |
4529 | rc_varname); |
4530 | } | |
4531 | ||
11fdf7f2 | 4532 | /* 'res' contains expression value */ |
7c673cae FG |
4533 | } else if (left->t == DUK_IVAL_PROP) { |
4534 | /* E5 Section 11.13.1 (and others) step 4 never matches for prop writes -> no check */ | |
4535 | duk_reg_t reg_obj; | |
4536 | duk_regconst_t rc_key; | |
4537 | duk_regconst_t rc_res; | |
4538 | duk_reg_t reg_temp; | |
4539 | ||
4540 | /* Property access expressions ('a[b]') are critical to correct | |
4541 | * LHS evaluation ordering, see test-dev-assign-eval-order*.js. | |
4542 | * We must make sure that the LHS target slot (base object and | |
4543 | * key) don't change during RHS evaluation. The only concrete | |
4544 | * problem is a register reference to a variable-bound register | |
4545 | * (i.e., non-temp). Require temp regs for both key and base. | |
4546 | * | |
4547 | * Don't allow a constant for the object (even for a number | |
4548 | * etc), as it goes into the 'A' field of the opcode. | |
4549 | */ | |
4550 | ||
4551 | reg_obj = duk__ispec_toregconst_raw(comp_ctx, | |
4552 | &left->x1, | |
4553 | -1 /*forced_reg*/, | |
4554 | DUK__IVAL_FLAG_REQUIRE_TEMP /*flags*/); | |
4555 | ||
4556 | rc_key = duk__ispec_toregconst_raw(comp_ctx, | |
4557 | &left->x2, | |
4558 | -1 /*forced_reg*/, | |
4559 | DUK__IVAL_FLAG_REQUIRE_TEMP | DUK__IVAL_FLAG_ALLOW_CONST /*flags*/); | |
4560 | ||
4561 | /* Evaluate RHS only when LHS is safe. */ | |
7c673cae FG |
4562 | |
4563 | if (args_op == DUK_OP_NONE) { | |
11fdf7f2 TL |
4564 | duk__expr_toregconst(comp_ctx, res, args_rbp /*rbp_flags*/); |
4565 | DUK_ASSERT(res->t == DUK_IVAL_PLAIN && res->x1.t == DUK_ISPEC_REGCONST); | |
7c673cae FG |
4566 | rc_res = res->x1.regconst; |
4567 | } else { | |
4568 | reg_temp = DUK__ALLOCTEMP(comp_ctx); | |
4569 | duk__emit_a_b_c(comp_ctx, | |
4570 | DUK_OP_GETPROP, | |
4571 | (duk_regconst_t) reg_temp, | |
4572 | (duk_regconst_t) reg_obj, | |
4573 | rc_key); | |
11fdf7f2 TL |
4574 | |
4575 | duk__expr_toregconst(comp_ctx, res, args_rbp /*rbp_flags*/); | |
4576 | DUK_ASSERT(res->t == DUK_IVAL_PLAIN && res->x1.t == DUK_ISPEC_REGCONST); | |
4577 | ||
7c673cae FG |
4578 | duk__emit_a_b_c(comp_ctx, |
4579 | args_op, | |
4580 | (duk_regconst_t) reg_temp, | |
4581 | (duk_regconst_t) reg_temp, | |
4582 | res->x1.regconst); | |
4583 | rc_res = (duk_regconst_t) reg_temp; | |
4584 | } | |
4585 | ||
4586 | duk__emit_a_b_c(comp_ctx, | |
4587 | DUK_OP_PUTPROP | DUK__EMIT_FLAG_A_IS_SOURCE, | |
4588 | (duk_regconst_t) reg_obj, | |
4589 | rc_key, | |
4590 | rc_res); | |
4591 | ||
4592 | res->t = DUK_IVAL_PLAIN; | |
4593 | res->x1.t = DUK_ISPEC_REGCONST; | |
4594 | res->x1.regconst = rc_res; | |
4595 | } else { | |
4596 | /* No support for lvalues returned from new or function call expressions. | |
4597 | * However, these must NOT cause compile-time SyntaxErrors, but run-time | |
4598 | * ReferenceErrors. Both left and right sides of the assignment must be | |
4599 | * evaluated before throwing a ReferenceError. For instance: | |
4600 | * | |
4601 | * f() = g(); | |
4602 | * | |
4603 | * must result in f() being evaluated, then g() being evaluated, and | |
4604 | * finally, a ReferenceError being thrown. See E5 Section 11.13.1. | |
4605 | */ | |
4606 | ||
4607 | duk_regconst_t rc_res; | |
4608 | ||
11fdf7f2 | 4609 | /* First evaluate LHS fully to ensure all side effects are out. */ |
7c673cae FG |
4610 | duk__ivalue_toplain_ignore(comp_ctx, left); |
4611 | ||
11fdf7f2 TL |
4612 | /* Then evaluate RHS fully (its value becomes the expression value too). |
4613 | * Technically we'd need the side effect safety check here too, but because | |
4614 | * we always throw using INVLHS the result doesn't matter. | |
4615 | */ | |
7c673cae FG |
4616 | rc_res = duk__expr_toregconst(comp_ctx, res, args_rbp /*rbp_flags*/); |
4617 | ||
4618 | duk__emit_extraop_only(comp_ctx, | |
4619 | DUK_EXTRAOP_INVLHS); | |
4620 | ||
7c673cae FG |
4621 | res->t = DUK_IVAL_PLAIN; |
4622 | res->x1.t = DUK_ISPEC_REGCONST; | |
4623 | res->x1.regconst = rc_res; | |
4624 | } | |
4625 | ||
4626 | return; | |
4627 | } | |
4628 | ||
4629 | postincdec: | |
4630 | { | |
4631 | /* | |
4632 | * Post-increment/decrement will return the original value as its | |
4633 | * result value. However, even that value will be coerced using | |
4634 | * ToNumber() which is quite awkward. Specific bytecode opcodes | |
4635 | * are used to handle these semantics. | |
4636 | * | |
4637 | * Note that post increment/decrement has a "no LineTerminator here" | |
4638 | * restriction. This is handled by duk__expr_lbp(), which forcibly terminates | |
4639 | * the previous expression if a LineTerminator occurs before '++'/'--'. | |
4640 | */ | |
4641 | ||
4642 | duk_reg_t reg_res; | |
4643 | duk_small_uint_t args_op = args >> 8; | |
4644 | ||
4645 | /* Specific assumptions for opcode numbering. */ | |
4646 | DUK_ASSERT(DUK_OP_POSTINCR + 4 == DUK_OP_POSTINCV); | |
4647 | DUK_ASSERT(DUK_OP_POSTDECR + 4 == DUK_OP_POSTDECV); | |
4648 | DUK_ASSERT(DUK_OP_POSTINCR + 8 == DUK_OP_POSTINCP); | |
4649 | DUK_ASSERT(DUK_OP_POSTDECR + 8 == DUK_OP_POSTDECP); | |
4650 | ||
4651 | reg_res = DUK__ALLOCTEMP(comp_ctx); | |
4652 | ||
4653 | if (left->t == DUK_IVAL_VAR) { | |
4654 | duk_hstring *h_varname; | |
4655 | duk_reg_t reg_varbind; | |
4656 | duk_regconst_t rc_varname; | |
4657 | ||
4658 | h_varname = duk_get_hstring(ctx, left->x1.valstack_idx); | |
4659 | DUK_ASSERT(h_varname != NULL); | |
4660 | ||
4661 | if (duk__hstring_is_eval_or_arguments_in_strict_mode(comp_ctx, h_varname)) { | |
4662 | goto syntax_error; | |
4663 | } | |
4664 | ||
4665 | duk_dup(ctx, left->x1.valstack_idx); | |
4666 | if (duk__lookup_lhs(comp_ctx, ®_varbind, &rc_varname)) { | |
4667 | duk__emit_a_bc(comp_ctx, | |
4668 | args_op, /* e.g. DUK_OP_POSTINCR */ | |
4669 | (duk_regconst_t) reg_res, | |
4670 | (duk_regconst_t) reg_varbind); | |
4671 | } else { | |
4672 | duk__emit_a_bc(comp_ctx, | |
4673 | args_op + 4, /* e.g. DUK_OP_POSTINCV */ | |
4674 | (duk_regconst_t) reg_res, | |
4675 | rc_varname); | |
4676 | } | |
4677 | ||
4678 | DUK_DDD(DUK_DDDPRINT("postincdec to '%!O' -> reg_varbind=%ld, rc_varname=%ld", | |
4679 | (duk_heaphdr *) h_varname, (long) reg_varbind, (long) rc_varname)); | |
4680 | } else if (left->t == DUK_IVAL_PROP) { | |
4681 | duk_reg_t reg_obj; /* allocate to reg only (not const) */ | |
4682 | duk_regconst_t rc_key; | |
4683 | ||
4684 | reg_obj = duk__ispec_toregconst_raw(comp_ctx, &left->x1, -1 /*forced_reg*/, 0 /*flags*/); /* don't allow const */ | |
4685 | rc_key = duk__ispec_toregconst_raw(comp_ctx, &left->x2, -1 /*forced_reg*/, DUK__IVAL_FLAG_ALLOW_CONST /*flags*/); | |
4686 | duk__emit_a_b_c(comp_ctx, | |
4687 | args_op + 8, /* e.g. DUK_OP_POSTINCP */ | |
4688 | (duk_regconst_t) reg_res, | |
4689 | (duk_regconst_t) reg_obj, | |
4690 | rc_key); | |
4691 | } else { | |
4692 | /* Technically return value is not needed because INVLHS will | |
4693 | * unconditially throw a ReferenceError. Coercion is necessary | |
4694 | * for proper semantics (consider ToNumber() called for an object). | |
4695 | * Use DUK_EXTRAOP_UNP with a dummy register to get ToNumber(). | |
4696 | */ | |
4697 | duk__ivalue_toforcedreg(comp_ctx, left, reg_res); | |
4698 | duk__emit_extraop_bc(comp_ctx, | |
4699 | DUK_EXTRAOP_UNP, | |
4700 | reg_res); /* for side effects, result ignored */ | |
4701 | duk__emit_extraop_only(comp_ctx, | |
4702 | DUK_EXTRAOP_INVLHS); | |
4703 | } | |
4704 | ||
4705 | res->t = DUK_IVAL_PLAIN; | |
4706 | res->x1.t = DUK_ISPEC_REGCONST; | |
4707 | res->x1.regconst = (duk_regconst_t) reg_res; | |
4708 | DUK__SETTEMP(comp_ctx, reg_res + 1); | |
4709 | return; | |
4710 | } | |
4711 | ||
4712 | syntax_error: | |
11fdf7f2 | 4713 | DUK_ERROR_SYNTAX(thr, DUK_STR_INVALID_EXPRESSION); |
7c673cae FG |
4714 | return; |
4715 | ||
4716 | syntax_error_lvalue: | |
11fdf7f2 | 4717 | DUK_ERROR_SYNTAX(thr, DUK_STR_INVALID_LVALUE); |
7c673cae FG |
4718 | return; |
4719 | } | |
4720 | ||
4721 | DUK_LOCAL duk_small_uint_t duk__expr_lbp(duk_compiler_ctx *comp_ctx) { | |
4722 | duk_small_int_t tok = comp_ctx->curr_token.t; | |
4723 | ||
4724 | DUK_ASSERT(tok >= DUK_TOK_MINVAL && tok <= DUK_TOK_MAXVAL); | |
4725 | DUK_ASSERT(sizeof(duk__token_lbp) == DUK_TOK_MAXVAL + 1); | |
4726 | ||
4727 | /* XXX: integrate support for this into led() instead? | |
4728 | * Similar issue as post-increment/post-decrement. | |
4729 | */ | |
4730 | ||
4731 | /* prevent duk__expr_led() by using a binding power less than anything valid */ | |
4732 | if (tok == DUK_TOK_IN && !comp_ctx->curr_func.allow_in) { | |
4733 | return 0; | |
4734 | } | |
4735 | ||
4736 | if ((tok == DUK_TOK_DECREMENT || tok == DUK_TOK_INCREMENT) && | |
4737 | (comp_ctx->curr_token.lineterm)) { | |
4738 | /* '++' or '--' in a post-increment/decrement position, | |
4739 | * and a LineTerminator occurs between the operator and | |
4740 | * the preceding expression. Force the previous expr | |
4741 | * to terminate, in effect treating e.g. "a,b\n++" as | |
4742 | * "a,b;++" (= SyntaxError). | |
4743 | */ | |
4744 | return 0; | |
4745 | } | |
4746 | ||
4747 | return DUK__TOKEN_LBP_GET_BP(duk__token_lbp[tok]); /* format is bit packed */ | |
4748 | } | |
4749 | ||
4750 | /* | |
4751 | * Expression parsing. | |
4752 | * | |
4753 | * Upon entry to 'expr' and its variants, 'curr_tok' is assumed to be the | |
4754 | * first token of the expression. Upon exit, 'curr_tok' will be the first | |
4755 | * token not part of the expression (e.g. semicolon terminating an expression | |
4756 | * statement). | |
4757 | */ | |
4758 | ||
4759 | #define DUK__EXPR_RBP_MASK 0xff | |
11fdf7f2 TL |
4760 | #define DUK__EXPR_FLAG_REJECT_IN (1 << 8) /* reject 'in' token (used for for-in) */ |
4761 | #define DUK__EXPR_FLAG_ALLOW_EMPTY (1 << 9) /* allow empty expression */ | |
4762 | #define DUK__EXPR_FLAG_REQUIRE_INIT (1 << 10) /* require initializer for var/const */ | |
7c673cae FG |
4763 | |
4764 | /* main expression parser function */ | |
4765 | DUK_LOCAL void duk__expr(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_small_uint_t rbp_flags) { | |
4766 | duk_hthread *thr = comp_ctx->thr; | |
4767 | duk_context *ctx = (duk_context *) thr; | |
4768 | duk_ivalue tmp_alloc; /* 'res' is used for "left", and 'tmp' for "right" */ | |
4769 | duk_ivalue *tmp = &tmp_alloc; | |
4770 | duk_small_uint_t rbp; | |
4771 | ||
4772 | DUK__RECURSION_INCREASE(comp_ctx, thr); | |
4773 | ||
4774 | duk_require_stack(ctx, DUK__PARSE_EXPR_SLOTS); | |
4775 | ||
4776 | /* filter out flags from exprtop rbp_flags here to save space */ | |
4777 | rbp = rbp_flags & DUK__EXPR_RBP_MASK; | |
4778 | ||
4779 | DUK_DDD(DUK_DDDPRINT("duk__expr(), rbp_flags=%ld, rbp=%ld, allow_in=%ld, paren_level=%ld", | |
4780 | (long) rbp_flags, (long) rbp, (long) comp_ctx->curr_func.allow_in, | |
4781 | (long) comp_ctx->curr_func.paren_level)); | |
4782 | ||
4783 | DUK_MEMZERO(&tmp_alloc, sizeof(tmp_alloc)); | |
4784 | tmp->x1.valstack_idx = duk_get_top(ctx); | |
4785 | tmp->x2.valstack_idx = tmp->x1.valstack_idx + 1; | |
4786 | duk_push_undefined(ctx); | |
4787 | duk_push_undefined(ctx); | |
4788 | ||
4789 | /* XXX: where to release temp regs in intermediate expressions? | |
4790 | * e.g. 1+2+3 -> don't inflate temp register count when parsing this. | |
4791 | * that particular expression temp regs can be forced here. | |
4792 | */ | |
4793 | ||
4794 | /* XXX: increase ctx->expr_tokens here for every consumed token | |
4795 | * (this would be a nice statistic)? | |
4796 | */ | |
4797 | ||
4798 | if (comp_ctx->curr_token.t == DUK_TOK_SEMICOLON || comp_ctx->curr_token.t == DUK_TOK_RPAREN) { | |
4799 | /* XXX: possibly incorrect handling of empty expression */ | |
4800 | DUK_DDD(DUK_DDDPRINT("empty expression")); | |
4801 | if (!(rbp_flags & DUK__EXPR_FLAG_ALLOW_EMPTY)) { | |
11fdf7f2 | 4802 | DUK_ERROR_SYNTAX(thr, DUK_STR_EMPTY_EXPR_NOT_ALLOWED); |
7c673cae FG |
4803 | } |
4804 | res->t = DUK_IVAL_PLAIN; | |
4805 | res->x1.t = DUK_ISPEC_VALUE; | |
4806 | duk_push_undefined(ctx); | |
4807 | duk_replace(ctx, res->x1.valstack_idx); | |
4808 | goto cleanup; | |
4809 | } | |
4810 | ||
4811 | duk__advance(comp_ctx); | |
4812 | duk__expr_nud(comp_ctx, res); /* reuse 'res' as 'left' */ | |
4813 | while (rbp < duk__expr_lbp(comp_ctx)) { | |
4814 | duk__advance(comp_ctx); | |
4815 | duk__expr_led(comp_ctx, res, tmp); | |
4816 | duk__copy_ivalue(comp_ctx, tmp, res); /* tmp -> res */ | |
4817 | } | |
4818 | ||
4819 | cleanup: | |
4820 | /* final result is already in 'res' */ | |
4821 | ||
4822 | duk_pop_2(ctx); | |
4823 | ||
4824 | DUK__RECURSION_DECREASE(comp_ctx, thr); | |
4825 | } | |
4826 | ||
4827 | DUK_LOCAL void duk__exprtop(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_small_uint_t rbp_flags) { | |
4828 | duk_hthread *thr = comp_ctx->thr; | |
4829 | ||
4830 | /* Note: these variables must reside in 'curr_func' instead of the global | |
4831 | * context: when parsing function expressions, expression parsing is nested. | |
4832 | */ | |
4833 | comp_ctx->curr_func.nud_count = 0; | |
4834 | comp_ctx->curr_func.led_count = 0; | |
4835 | comp_ctx->curr_func.paren_level = 0; | |
4836 | comp_ctx->curr_func.expr_lhs = 1; | |
4837 | comp_ctx->curr_func.allow_in = (rbp_flags & DUK__EXPR_FLAG_REJECT_IN ? 0 : 1); | |
4838 | ||
4839 | duk__expr(comp_ctx, res, rbp_flags); | |
4840 | ||
4841 | if (!(rbp_flags & DUK__EXPR_FLAG_ALLOW_EMPTY) && duk__expr_is_empty(comp_ctx)) { | |
11fdf7f2 | 4842 | DUK_ERROR_SYNTAX(thr, DUK_STR_EMPTY_EXPR_NOT_ALLOWED); |
7c673cae FG |
4843 | } |
4844 | } | |
4845 | ||
4846 | /* A bunch of helpers (for size optimization) that combine duk__expr()/duk__exprtop() | |
4847 | * and result conversions. | |
4848 | * | |
4849 | * Each helper needs at least 2-3 calls to make it worth while to wrap. | |
4850 | */ | |
4851 | ||
11fdf7f2 | 4852 | #if 0 /* unused */ |
7c673cae FG |
4853 | DUK_LOCAL duk_reg_t duk__expr_toreg(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_small_uint_t rbp_flags) { |
4854 | duk__expr(comp_ctx, res, rbp_flags); | |
4855 | return duk__ivalue_toreg(comp_ctx, res); | |
4856 | } | |
11fdf7f2 | 4857 | #endif |
7c673cae FG |
4858 | |
4859 | #if 0 /* unused */ | |
11fdf7f2 | 4860 | DUK_LOCAL duk_reg_t duk__expr_totemp(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_small_uint_t rbp_flags) { |
7c673cae | 4861 | duk__expr(comp_ctx, res, rbp_flags); |
11fdf7f2 | 4862 | return duk__ivalue_totemp(comp_ctx, res); |
7c673cae FG |
4863 | } |
4864 | #endif | |
4865 | ||
4866 | DUK_LOCAL void duk__expr_toforcedreg(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_small_uint_t rbp_flags, duk_reg_t forced_reg) { | |
4867 | DUK_ASSERT(forced_reg >= 0); | |
4868 | duk__expr(comp_ctx, res, rbp_flags); | |
4869 | duk__ivalue_toforcedreg(comp_ctx, res, forced_reg); | |
4870 | } | |
4871 | ||
4872 | DUK_LOCAL duk_regconst_t duk__expr_toregconst(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_small_uint_t rbp_flags) { | |
4873 | duk__expr(comp_ctx, res, rbp_flags); | |
4874 | return duk__ivalue_toregconst(comp_ctx, res); | |
4875 | } | |
4876 | ||
11fdf7f2 TL |
4877 | #if 0 /* unused */ |
4878 | DUK_LOCAL duk_regconst_t duk__expr_totempconst(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_small_uint_t rbp_flags) { | |
4879 | duk__expr(comp_ctx, res, rbp_flags); | |
4880 | return duk__ivalue_totempconst(comp_ctx, res); | |
4881 | } | |
4882 | #endif | |
4883 | ||
7c673cae FG |
4884 | DUK_LOCAL void duk__expr_toplain(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_small_uint_t rbp_flags) { |
4885 | duk__expr(comp_ctx, res, rbp_flags); | |
4886 | duk__ivalue_toplain(comp_ctx, res); | |
4887 | } | |
4888 | ||
4889 | DUK_LOCAL void duk__expr_toplain_ignore(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_small_uint_t rbp_flags) { | |
4890 | duk__expr(comp_ctx, res, rbp_flags); | |
4891 | duk__ivalue_toplain_ignore(comp_ctx, res); | |
4892 | } | |
4893 | ||
4894 | DUK_LOCAL duk_reg_t duk__exprtop_toreg(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_small_uint_t rbp_flags) { | |
4895 | duk__exprtop(comp_ctx, res, rbp_flags); | |
4896 | return duk__ivalue_toreg(comp_ctx, res); | |
4897 | } | |
4898 | ||
4899 | #if 0 /* unused */ | |
11fdf7f2 | 4900 | DUK_LOCAL duk_reg_t duk__exprtop_totemp(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_small_uint_t rbp_flags) { |
7c673cae | 4901 | duk__exprtop(comp_ctx, res, rbp_flags); |
11fdf7f2 | 4902 | return duk__ivalue_totemp(comp_ctx, res); |
7c673cae FG |
4903 | } |
4904 | #endif | |
4905 | ||
4906 | DUK_LOCAL void duk__exprtop_toforcedreg(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_small_uint_t rbp_flags, duk_reg_t forced_reg) { | |
4907 | DUK_ASSERT(forced_reg >= 0); | |
4908 | duk__exprtop(comp_ctx, res, rbp_flags); | |
4909 | duk__ivalue_toforcedreg(comp_ctx, res, forced_reg); | |
4910 | } | |
4911 | ||
4912 | DUK_LOCAL duk_regconst_t duk__exprtop_toregconst(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_small_uint_t rbp_flags) { | |
4913 | duk__exprtop(comp_ctx, res, rbp_flags); | |
4914 | return duk__ivalue_toregconst(comp_ctx, res); | |
4915 | } | |
4916 | ||
4917 | #if 0 /* unused */ | |
4918 | DUK_LOCAL void duk__exprtop_toplain_ignore(duk_compiler_ctx *comp_ctx, duk_ivalue *res, int rbp_flags) { | |
4919 | duk__exprtop(comp_ctx, res, rbp_flags); | |
4920 | duk__ivalue_toplain_ignore(comp_ctx, res); | |
4921 | } | |
4922 | #endif | |
4923 | ||
4924 | /* | |
4925 | * Parse an individual source element (top level statement) or a statement. | |
4926 | * | |
4927 | * Handles labeled statements automatically (peeling away labels before | |
4928 | * parsing an expression that follows the label(s)). | |
4929 | * | |
4930 | * Upon entry, 'curr_tok' contains the first token of the statement (parsed | |
4931 | * in "allow regexp literal" mode). Upon exit, 'curr_tok' contains the first | |
4932 | * token following the statement (if the statement has a terminator, this is | |
4933 | * the token after the terminator). | |
4934 | */ | |
4935 | ||
4936 | #ifdef DUK__HAS_VAL | |
4937 | #undef DUK__HAS_VAL | |
4938 | #endif | |
4939 | #ifdef DUK__HAS_TERM | |
4940 | #undef DUK__HAS_TERM | |
4941 | #endif | |
4942 | #ifdef DUK__ALLOW_AUTO_SEMI_ALWAYS | |
4943 | #undef DUK__ALLOW_AUTO_SEMI_ALWAYS | |
4944 | #endif | |
4945 | #ifdef DUK__STILL_PROLOGUE | |
4946 | #undef DUK__STILL_PROLOGUE | |
4947 | #endif | |
4948 | #ifdef DUK__IS_TERMINAL | |
4949 | #undef DUK__IS_TERMINAL | |
4950 | #endif | |
4951 | ||
4952 | #define DUK__HAS_VAL (1 << 0) /* stmt has non-empty value */ | |
4953 | #define DUK__HAS_TERM (1 << 1) /* stmt has explicit/implicit semicolon terminator */ | |
4954 | #define DUK__ALLOW_AUTO_SEMI_ALWAYS (1 << 2) /* allow automatic semicolon even without lineterm (compatibility) */ | |
4955 | #define DUK__STILL_PROLOGUE (1 << 3) /* statement does not terminate directive prologue */ | |
4956 | #define DUK__IS_TERMINAL (1 << 4) /* statement is guaranteed to be terminal (control doesn't flow to next statement) */ | |
4957 | ||
4958 | /* Parse a single variable declaration (e.g. "i" or "i=10"). A leading 'var' | |
4959 | * has already been eaten. These is no return value in 'res', it is used only | |
4960 | * as a temporary. | |
4961 | * | |
4962 | * When called from 'for-in' statement parser, the initializer expression must | |
4963 | * not allow the 'in' token. The caller supply additional expression parsing | |
4964 | * flags (like DUK__EXPR_FLAG_REJECT_IN) in 'expr_flags'. | |
4965 | * | |
4966 | * Finally, out_rc_varname and out_reg_varbind are updated to reflect where | |
4967 | * the identifier is bound: | |
4968 | * | |
4969 | * If register bound: out_reg_varbind >= 0, out_rc_varname == 0 (ignore) | |
4970 | * If not register bound: out_reg_varbind < 0, out_rc_varname >= 0 | |
4971 | * | |
4972 | * These allow the caller to use the variable for further assignment, e.g. | |
4973 | * as is done in 'for-in' parsing. | |
4974 | */ | |
4975 | ||
4976 | DUK_LOCAL 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) { | |
4977 | duk_hthread *thr = comp_ctx->thr; | |
4978 | duk_context *ctx = (duk_context *) thr; | |
4979 | duk_hstring *h_varname; | |
4980 | duk_reg_t reg_varbind; | |
4981 | duk_regconst_t rc_varname; | |
4982 | ||
4983 | /* assume 'var' has been eaten */ | |
4984 | ||
4985 | /* Note: Identifier rejects reserved words */ | |
4986 | if (comp_ctx->curr_token.t != DUK_TOK_IDENTIFIER) { | |
4987 | goto syntax_error; | |
4988 | } | |
4989 | h_varname = comp_ctx->curr_token.str1; | |
4990 | ||
4991 | DUK_ASSERT(h_varname != NULL); | |
4992 | ||
4993 | /* strict mode restrictions (E5 Section 12.2.1) */ | |
4994 | if (duk__hstring_is_eval_or_arguments_in_strict_mode(comp_ctx, h_varname)) { | |
4995 | goto syntax_error; | |
4996 | } | |
4997 | ||
4998 | /* register declarations in first pass */ | |
4999 | if (comp_ctx->curr_func.in_scanning) { | |
5000 | duk_uarridx_t n; | |
5001 | DUK_DDD(DUK_DDDPRINT("register variable declaration %!O in pass 1", | |
5002 | (duk_heaphdr *) h_varname)); | |
5003 | n = (duk_uarridx_t) duk_get_length(ctx, comp_ctx->curr_func.decls_idx); | |
5004 | duk_push_hstring(ctx, h_varname); | |
5005 | duk_put_prop_index(ctx, comp_ctx->curr_func.decls_idx, n); | |
5006 | duk_push_int(ctx, DUK_DECL_TYPE_VAR + (0 << 8)); | |
5007 | duk_put_prop_index(ctx, comp_ctx->curr_func.decls_idx, n + 1); | |
5008 | } | |
5009 | ||
5010 | duk_push_hstring(ctx, h_varname); /* push before advancing to keep reachable */ | |
5011 | ||
5012 | /* register binding lookup is based on varmap (even in first pass) */ | |
5013 | duk_dup_top(ctx); | |
5014 | (void) duk__lookup_lhs(comp_ctx, ®_varbind, &rc_varname); | |
5015 | ||
5016 | duk__advance(comp_ctx); /* eat identifier */ | |
5017 | ||
5018 | if (comp_ctx->curr_token.t == DUK_TOK_EQUALSIGN) { | |
5019 | duk__advance(comp_ctx); | |
5020 | ||
5021 | DUK_DDD(DUK_DDDPRINT("vardecl, assign to '%!O' -> reg_varbind=%ld, rc_varname=%ld", | |
5022 | (duk_heaphdr *) h_varname, (long) reg_varbind, (long) rc_varname)); | |
5023 | ||
5024 | duk__exprtop(comp_ctx, res, DUK__BP_COMMA | expr_flags /*rbp_flags*/); /* AssignmentExpression */ | |
5025 | ||
5026 | if (reg_varbind >= 0) { | |
5027 | duk__ivalue_toforcedreg(comp_ctx, res, reg_varbind); | |
5028 | } else { | |
5029 | duk_reg_t reg_val; | |
5030 | reg_val = duk__ivalue_toreg(comp_ctx, res); | |
5031 | duk__emit_a_bc(comp_ctx, | |
5032 | DUK_OP_PUTVAR | DUK__EMIT_FLAG_A_IS_SOURCE, | |
5033 | (duk_regconst_t) reg_val, | |
5034 | rc_varname); | |
5035 | } | |
11fdf7f2 TL |
5036 | } else { |
5037 | if (expr_flags & DUK__EXPR_FLAG_REQUIRE_INIT) { | |
5038 | /* Used for minimal 'const': initializer required. */ | |
5039 | goto syntax_error; | |
5040 | } | |
7c673cae FG |
5041 | } |
5042 | ||
5043 | duk_pop(ctx); /* pop varname */ | |
5044 | ||
5045 | *out_rc_varname = rc_varname; | |
5046 | *out_reg_varbind = reg_varbind; | |
5047 | ||
5048 | return; | |
5049 | ||
5050 | syntax_error: | |
11fdf7f2 | 5051 | DUK_ERROR_SYNTAX(thr, DUK_STR_INVALID_VAR_DECLARATION); |
7c673cae FG |
5052 | } |
5053 | ||
11fdf7f2 | 5054 | DUK_LOCAL void duk__parse_var_stmt(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_small_uint_t expr_flags) { |
7c673cae FG |
5055 | duk_reg_t reg_varbind; |
5056 | duk_regconst_t rc_varname; | |
5057 | ||
5058 | duk__advance(comp_ctx); /* eat 'var' */ | |
5059 | ||
5060 | for (;;) { | |
5061 | /* rc_varname and reg_varbind are ignored here */ | |
11fdf7f2 | 5062 | duk__parse_var_decl(comp_ctx, res, 0 | expr_flags, ®_varbind, &rc_varname); |
7c673cae FG |
5063 | |
5064 | if (comp_ctx->curr_token.t != DUK_TOK_COMMA) { | |
5065 | break; | |
5066 | } | |
5067 | duk__advance(comp_ctx); | |
5068 | } | |
5069 | } | |
5070 | ||
5071 | DUK_LOCAL void duk__parse_for_stmt(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_int_t pc_label_site) { | |
5072 | duk_hthread *thr = comp_ctx->thr; | |
5073 | duk_context *ctx = (duk_context *) thr; | |
5074 | duk_int_t pc_v34_lhs; /* start variant 3/4 left-hand-side code (L1 in doc/compiler.rst example) */ | |
5075 | duk_reg_t temp_reset; /* knock back "next temp" to this whenever possible */ | |
5076 | duk_reg_t reg_temps; /* preallocated temporaries (2) for variants 3 and 4 */ | |
5077 | ||
5078 | DUK_DDD(DUK_DDDPRINT("start parsing a for/for-in statement")); | |
5079 | ||
5080 | /* Two temporaries are preallocated here for variants 3 and 4 which need | |
5081 | * registers which are never clobbered by expressions in the loop | |
5082 | * (concretely: for the enumerator object and the next enumerated value). | |
5083 | * Variants 1 and 2 "release" these temps. | |
5084 | */ | |
5085 | ||
5086 | reg_temps = DUK__ALLOCTEMPS(comp_ctx, 2); | |
5087 | ||
5088 | temp_reset = DUK__GETTEMP(comp_ctx); | |
5089 | ||
5090 | /* | |
5091 | * For/for-in main variants are: | |
5092 | * | |
5093 | * 1. for (ExpressionNoIn_opt; Expression_opt; Expression_opt) Statement | |
5094 | * 2. for (var VariableDeclarationNoIn; Expression_opt; Expression_opt) Statement | |
5095 | * 3. for (LeftHandSideExpression in Expression) Statement | |
5096 | * 4. for (var VariableDeclarationNoIn in Expression) Statement | |
5097 | * | |
5098 | * Parsing these without arbitrary lookahead or backtracking is relatively | |
5099 | * tricky but we manage to do so for now. | |
5100 | * | |
5101 | * See doc/compiler.rst for a detailed discussion of control flow | |
5102 | * issues, evaluation order issues, etc. | |
5103 | */ | |
5104 | ||
5105 | duk__advance(comp_ctx); /* eat 'for' */ | |
5106 | duk__advance_expect(comp_ctx, DUK_TOK_LPAREN); | |
5107 | ||
5108 | DUK_DDD(DUK_DDDPRINT("detecting for/for-in loop variant, pc=%ld", (long) duk__get_current_pc(comp_ctx))); | |
5109 | ||
5110 | /* a label site has been emitted by duk__parse_stmt() automatically | |
5111 | * (it will also emit the ENDLABEL). | |
5112 | */ | |
5113 | ||
5114 | if (comp_ctx->curr_token.t == DUK_TOK_VAR) { | |
5115 | /* | |
5116 | * Variant 2 or 4 | |
5117 | */ | |
5118 | ||
5119 | duk_reg_t reg_varbind; /* variable binding register if register-bound (otherwise < 0) */ | |
5120 | duk_regconst_t rc_varname; /* variable name reg/const, if variable not register-bound */ | |
5121 | ||
5122 | duk__advance(comp_ctx); /* eat 'var' */ | |
5123 | duk__parse_var_decl(comp_ctx, res, DUK__EXPR_FLAG_REJECT_IN, ®_varbind, &rc_varname); | |
5124 | DUK__SETTEMP(comp_ctx, temp_reset); | |
5125 | ||
5126 | if (comp_ctx->curr_token.t == DUK_TOK_IN) { | |
5127 | /* | |
5128 | * Variant 4 | |
5129 | */ | |
5130 | ||
5131 | DUK_DDD(DUK_DDDPRINT("detected for variant 4: for (var VariableDeclarationNoIn in Expression) Statement")); | |
5132 | pc_v34_lhs = duk__get_current_pc(comp_ctx); /* jump is inserted here */ | |
5133 | if (reg_varbind >= 0) { | |
5134 | duk__emit_a_bc(comp_ctx, | |
5135 | DUK_OP_LDREG, | |
5136 | (duk_regconst_t) reg_varbind, | |
5137 | (duk_regconst_t) (reg_temps + 0)); | |
5138 | } else { | |
5139 | duk__emit_a_bc(comp_ctx, | |
5140 | DUK_OP_PUTVAR | DUK__EMIT_FLAG_A_IS_SOURCE, | |
5141 | (duk_regconst_t) (reg_temps + 0), | |
5142 | rc_varname); | |
5143 | } | |
5144 | goto parse_3_or_4; | |
5145 | } else { | |
5146 | /* | |
5147 | * Variant 2 | |
5148 | */ | |
5149 | ||
5150 | DUK_DDD(DUK_DDDPRINT("detected for variant 2: for (var VariableDeclarationNoIn; Expression_opt; Expression_opt) Statement")); | |
5151 | for (;;) { | |
5152 | /* more initializers */ | |
5153 | if (comp_ctx->curr_token.t != DUK_TOK_COMMA) { | |
5154 | break; | |
5155 | } | |
5156 | DUK_DDD(DUK_DDDPRINT("variant 2 has another variable initializer")); | |
5157 | ||
5158 | duk__advance(comp_ctx); /* eat comma */ | |
5159 | duk__parse_var_decl(comp_ctx, res, DUK__EXPR_FLAG_REJECT_IN, ®_varbind, &rc_varname); | |
5160 | } | |
5161 | goto parse_1_or_2; | |
5162 | } | |
5163 | } else { | |
5164 | /* | |
5165 | * Variant 1 or 3 | |
5166 | */ | |
5167 | ||
5168 | pc_v34_lhs = duk__get_current_pc(comp_ctx); /* jump is inserted here (variant 3) */ | |
5169 | ||
5170 | /* Note that duk__exprtop() here can clobber any reg above current temp_next, | |
5171 | * so any loop variables (e.g. enumerator) must be "preallocated". | |
5172 | */ | |
5173 | ||
5174 | /* don't coerce yet to a plain value (variant 3 needs special handling) */ | |
5175 | duk__exprtop(comp_ctx, res, DUK__BP_FOR_EXPR | DUK__EXPR_FLAG_REJECT_IN | DUK__EXPR_FLAG_ALLOW_EMPTY /*rbp_flags*/); /* Expression */ | |
5176 | if (comp_ctx->curr_token.t == DUK_TOK_IN) { | |
5177 | /* | |
5178 | * Variant 3 | |
5179 | */ | |
5180 | ||
5181 | /* XXX: need to determine LHS type, and check that it is LHS compatible */ | |
5182 | DUK_DDD(DUK_DDDPRINT("detected for variant 3: for (LeftHandSideExpression in Expression) Statement")); | |
5183 | if (duk__expr_is_empty(comp_ctx)) { | |
5184 | goto syntax_error; /* LeftHandSideExpression does not allow empty expression */ | |
5185 | } | |
5186 | ||
5187 | if (res->t == DUK_IVAL_VAR) { | |
5188 | duk_reg_t reg_varbind; | |
5189 | duk_regconst_t rc_varname; | |
5190 | ||
5191 | duk_dup(ctx, res->x1.valstack_idx); | |
5192 | if (duk__lookup_lhs(comp_ctx, ®_varbind, &rc_varname)) { | |
5193 | duk__emit_a_bc(comp_ctx, | |
5194 | DUK_OP_LDREG, | |
5195 | (duk_regconst_t) reg_varbind, | |
5196 | (duk_regconst_t) (reg_temps + 0)); | |
5197 | } else { | |
5198 | duk__emit_a_bc(comp_ctx, | |
5199 | DUK_OP_PUTVAR | DUK__EMIT_FLAG_A_IS_SOURCE, | |
5200 | (duk_regconst_t) (reg_temps + 0), | |
5201 | rc_varname); | |
5202 | } | |
5203 | } else if (res->t == DUK_IVAL_PROP) { | |
5204 | /* Don't allow a constant for the object (even for a number etc), as | |
5205 | * it goes into the 'A' field of the opcode. | |
5206 | */ | |
5207 | duk_reg_t reg_obj; | |
5208 | duk_regconst_t rc_key; | |
5209 | reg_obj = duk__ispec_toregconst_raw(comp_ctx, &res->x1, -1 /*forced_reg*/, 0 /*flags*/); /* don't allow const */ | |
5210 | rc_key = duk__ispec_toregconst_raw(comp_ctx, &res->x2, -1 /*forced_reg*/, DUK__IVAL_FLAG_ALLOW_CONST /*flags*/); | |
5211 | duk__emit_a_b_c(comp_ctx, | |
5212 | DUK_OP_PUTPROP | DUK__EMIT_FLAG_A_IS_SOURCE, | |
5213 | (duk_regconst_t) reg_obj, | |
5214 | rc_key, | |
5215 | (duk_regconst_t) (reg_temps + 0)); | |
5216 | } else { | |
5217 | duk__ivalue_toplain_ignore(comp_ctx, res); /* just in case */ | |
5218 | duk__emit_extraop_only(comp_ctx, | |
5219 | DUK_EXTRAOP_INVLHS); | |
5220 | } | |
5221 | goto parse_3_or_4; | |
5222 | } else { | |
5223 | /* | |
5224 | * Variant 1 | |
5225 | */ | |
5226 | ||
5227 | DUK_DDD(DUK_DDDPRINT("detected for variant 1: for (ExpressionNoIn_opt; Expression_opt; Expression_opt) Statement")); | |
5228 | duk__ivalue_toplain_ignore(comp_ctx, res); | |
5229 | goto parse_1_or_2; | |
5230 | } | |
5231 | } | |
5232 | ||
5233 | parse_1_or_2: | |
5234 | /* | |
5235 | * Parse variant 1 or 2. The first part expression (which differs | |
5236 | * in the variants) has already been parsed and its code emitted. | |
5237 | * | |
5238 | * reg_temps + 0: unused | |
5239 | * reg_temps + 1: unused | |
5240 | */ | |
5241 | { | |
5242 | duk_regconst_t rc_cond; | |
5243 | duk_int_t pc_l1, pc_l2, pc_l3, pc_l4; | |
5244 | duk_int_t pc_jumpto_l3, pc_jumpto_l4; | |
5245 | duk_bool_t expr_c_empty; | |
5246 | ||
5247 | DUK_DDD(DUK_DDDPRINT("shared code for parsing variants 1 and 2")); | |
5248 | ||
5249 | /* "release" preallocated temps since we won't need them */ | |
5250 | temp_reset = reg_temps + 0; | |
5251 | DUK__SETTEMP(comp_ctx, temp_reset); | |
5252 | ||
5253 | duk__advance_expect(comp_ctx, DUK_TOK_SEMICOLON); | |
5254 | ||
5255 | pc_l1 = duk__get_current_pc(comp_ctx); | |
5256 | duk__exprtop(comp_ctx, res, DUK__BP_FOR_EXPR | DUK__EXPR_FLAG_ALLOW_EMPTY /*rbp_flags*/); /* Expression_opt */ | |
5257 | if (duk__expr_is_empty(comp_ctx)) { | |
5258 | /* no need to coerce */ | |
5259 | pc_jumpto_l3 = duk__emit_jump_empty(comp_ctx); /* to body */ | |
5260 | pc_jumpto_l4 = -1; /* omitted */ | |
5261 | } else { | |
5262 | rc_cond = duk__ivalue_toregconst(comp_ctx, res); | |
5263 | duk__emit_if_false_skip(comp_ctx, rc_cond); | |
5264 | pc_jumpto_l3 = duk__emit_jump_empty(comp_ctx); /* to body */ | |
5265 | pc_jumpto_l4 = duk__emit_jump_empty(comp_ctx); /* to exit */ | |
5266 | } | |
5267 | DUK__SETTEMP(comp_ctx, temp_reset); | |
5268 | ||
5269 | duk__advance_expect(comp_ctx, DUK_TOK_SEMICOLON); | |
5270 | ||
5271 | pc_l2 = duk__get_current_pc(comp_ctx); | |
5272 | duk__exprtop(comp_ctx, res, DUK__BP_FOR_EXPR | DUK__EXPR_FLAG_ALLOW_EMPTY /*rbp_flags*/); /* Expression_opt */ | |
5273 | if (duk__expr_is_empty(comp_ctx)) { | |
5274 | /* no need to coerce */ | |
5275 | expr_c_empty = 1; | |
5276 | /* JUMP L1 omitted */ | |
5277 | } else { | |
5278 | duk__ivalue_toplain_ignore(comp_ctx, res); | |
5279 | expr_c_empty = 0; | |
5280 | duk__emit_jump(comp_ctx, pc_l1); | |
5281 | } | |
5282 | DUK__SETTEMP(comp_ctx, temp_reset); | |
5283 | ||
5284 | duk__advance_expect(comp_ctx, DUK_TOK_RPAREN); | |
5285 | ||
5286 | pc_l3 = duk__get_current_pc(comp_ctx); | |
5287 | duk__parse_stmt(comp_ctx, res, 0 /*allow_source_elem*/); | |
5288 | if (expr_c_empty) { | |
5289 | duk__emit_jump(comp_ctx, pc_l1); | |
5290 | } else { | |
5291 | duk__emit_jump(comp_ctx, pc_l2); | |
5292 | } | |
5293 | /* temp reset is not necessary after duk__parse_stmt(), which already does it */ | |
5294 | ||
5295 | pc_l4 = duk__get_current_pc(comp_ctx); | |
5296 | ||
5297 | DUK_DDD(DUK_DDDPRINT("patching jumps: jumpto_l3: %ld->%ld, jumpto_l4: %ld->%ld, " | |
5298 | "break: %ld->%ld, continue: %ld->%ld", | |
5299 | (long) pc_jumpto_l3, (long) pc_l3, (long) pc_jumpto_l4, (long) pc_l4, | |
5300 | (long) (pc_label_site + 1), (long) pc_l4, (long) (pc_label_site + 2), (long) pc_l2)); | |
5301 | ||
5302 | duk__patch_jump(comp_ctx, pc_jumpto_l3, pc_l3); | |
5303 | duk__patch_jump(comp_ctx, pc_jumpto_l4, pc_l4); | |
5304 | duk__patch_jump(comp_ctx, | |
5305 | pc_label_site + 1, | |
5306 | pc_l4); /* break jump */ | |
5307 | duk__patch_jump(comp_ctx, | |
5308 | pc_label_site + 2, | |
5309 | expr_c_empty ? pc_l1 : pc_l2); /* continue jump */ | |
5310 | } | |
5311 | goto finished; | |
5312 | ||
5313 | parse_3_or_4: | |
5314 | /* | |
5315 | * Parse variant 3 or 4. | |
5316 | * | |
5317 | * For variant 3 (e.g. "for (A in C) D;") the code for A (except the | |
5318 | * final property/variable write) has already been emitted. The first | |
5319 | * instruction of that code is at pc_v34_lhs; a JUMP needs to be inserted | |
5320 | * there to satisfy control flow needs. | |
5321 | * | |
5322 | * For variant 4, if the variable declaration had an initializer | |
5323 | * (e.g. "for (var A = B in C) D;") the code for the assignment | |
5324 | * (B) has already been emitted. | |
5325 | * | |
5326 | * Variables set before entering here: | |
5327 | * | |
5328 | * pc_v34_lhs: insert a "JUMP L2" here (see doc/compiler.rst example). | |
5329 | * reg_temps + 0: iteration target value (written to LHS) | |
5330 | * reg_temps + 1: enumerator object | |
5331 | */ | |
5332 | { | |
5333 | duk_int_t pc_l1, pc_l2, pc_l3, pc_l4, pc_l5; | |
5334 | duk_int_t pc_jumpto_l2, pc_jumpto_l3, pc_jumpto_l4, pc_jumpto_l5; | |
5335 | duk_reg_t reg_target; | |
5336 | ||
5337 | DUK_DDD(DUK_DDDPRINT("shared code for parsing variants 3 and 4, pc_v34_lhs=%ld", (long) pc_v34_lhs)); | |
5338 | ||
5339 | DUK__SETTEMP(comp_ctx, temp_reset); | |
5340 | ||
5341 | /* First we need to insert a jump in the middle of previously | |
5342 | * emitted code to get the control flow right. No jumps can | |
5343 | * cross the position where the jump is inserted. See doc/compiler.rst | |
5344 | * for discussion on the intricacies of control flow and side effects | |
5345 | * for variants 3 and 4. | |
5346 | */ | |
5347 | ||
5348 | duk__insert_jump_entry(comp_ctx, pc_v34_lhs); | |
5349 | pc_jumpto_l2 = pc_v34_lhs; /* inserted jump */ | |
5350 | pc_l1 = pc_v34_lhs + 1; /* +1, right after inserted jump */ | |
5351 | ||
5352 | /* The code for writing reg_temps + 0 to the left hand side has already | |
5353 | * been emitted. | |
5354 | */ | |
5355 | ||
5356 | pc_jumpto_l3 = duk__emit_jump_empty(comp_ctx); /* -> loop body */ | |
5357 | ||
5358 | duk__advance(comp_ctx); /* eat 'in' */ | |
5359 | ||
5360 | /* Parse enumeration target and initialize enumerator. For 'null' and 'undefined', | |
5361 | * INITENUM will creates a 'null' enumerator which works like an empty enumerator | |
5362 | * (E5 Section 12.6.4, step 3). Note that INITENUM requires the value to be in a | |
5363 | * register (constant not allowed). | |
5364 | */ | |
5365 | ||
5366 | pc_l2 = duk__get_current_pc(comp_ctx); | |
5367 | reg_target = duk__exprtop_toreg(comp_ctx, res, DUK__BP_FOR_EXPR /*rbp_flags*/); /* Expression */ | |
5368 | duk__emit_extraop_b_c(comp_ctx, | |
5369 | DUK_EXTRAOP_INITENUM | DUK__EMIT_FLAG_B_IS_TARGET, | |
5370 | (duk_regconst_t) (reg_temps + 1), | |
5371 | (duk_regconst_t) reg_target); | |
5372 | pc_jumpto_l4 = duk__emit_jump_empty(comp_ctx); | |
5373 | DUK__SETTEMP(comp_ctx, temp_reset); | |
5374 | ||
5375 | duk__advance_expect(comp_ctx, DUK_TOK_RPAREN); | |
5376 | ||
5377 | pc_l3 = duk__get_current_pc(comp_ctx); | |
5378 | duk__parse_stmt(comp_ctx, res, 0 /*allow_source_elem*/); | |
5379 | /* temp reset is not necessary after duk__parse_stmt(), which already does it */ | |
5380 | ||
5381 | /* NEXTENUM needs a jump slot right after the main opcode. | |
5382 | * We need the code emitter to reserve the slot: if there's | |
5383 | * target shuffling, the target shuffle opcodes must happen | |
5384 | * after the jump slot (for NEXTENUM the shuffle opcodes are | |
5385 | * not needed if the enum is finished). | |
5386 | */ | |
5387 | pc_l4 = duk__get_current_pc(comp_ctx); | |
5388 | duk__emit_extraop_b_c(comp_ctx, | |
5389 | DUK_EXTRAOP_NEXTENUM | DUK__EMIT_FLAG_B_IS_TARGET | DUK__EMIT_FLAG_RESERVE_JUMPSLOT, | |
5390 | (duk_regconst_t) (reg_temps + 0), | |
5391 | (duk_regconst_t) (reg_temps + 1)); | |
5392 | pc_jumpto_l5 = comp_ctx->emit_jumpslot_pc; /* NEXTENUM jump slot: executed when enum finished */ | |
5393 | duk__emit_jump(comp_ctx, pc_l1); /* jump to next loop, using reg_v34_iter as iterated value */ | |
5394 | ||
5395 | pc_l5 = duk__get_current_pc(comp_ctx); | |
5396 | ||
5397 | /* XXX: since the enumerator may be a memory expensive object, | |
5398 | * perhaps clear it explicitly here? If so, break jump must | |
5399 | * go through this clearing operation. | |
5400 | */ | |
5401 | ||
5402 | DUK_DDD(DUK_DDDPRINT("patching jumps: jumpto_l2: %ld->%ld, jumpto_l3: %ld->%ld, " | |
5403 | "jumpto_l4: %ld->%ld, jumpto_l5: %ld->%ld, " | |
5404 | "break: %ld->%ld, continue: %ld->%ld", | |
5405 | (long) pc_jumpto_l2, (long) pc_l2, (long) pc_jumpto_l3, (long) pc_l3, | |
5406 | (long) pc_jumpto_l4, (long) pc_l4, (long) pc_jumpto_l5, (long) pc_l5, | |
5407 | (long) (pc_label_site + 1), (long) pc_l5, (long) (pc_label_site + 2), (long) pc_l4)); | |
5408 | ||
5409 | duk__patch_jump(comp_ctx, pc_jumpto_l2, pc_l2); | |
5410 | duk__patch_jump(comp_ctx, pc_jumpto_l3, pc_l3); | |
5411 | duk__patch_jump(comp_ctx, pc_jumpto_l4, pc_l4); | |
5412 | duk__patch_jump(comp_ctx, pc_jumpto_l5, pc_l5); | |
5413 | duk__patch_jump(comp_ctx, pc_label_site + 1, pc_l5); /* break jump */ | |
5414 | duk__patch_jump(comp_ctx, pc_label_site + 2, pc_l4); /* continue jump */ | |
5415 | } | |
5416 | goto finished; | |
5417 | ||
5418 | finished: | |
5419 | DUK_DDD(DUK_DDDPRINT("end parsing a for/for-in statement")); | |
5420 | return; | |
5421 | ||
5422 | syntax_error: | |
11fdf7f2 | 5423 | DUK_ERROR_SYNTAX(thr, DUK_STR_INVALID_FOR); |
7c673cae FG |
5424 | } |
5425 | ||
5426 | DUK_LOCAL void duk__parse_switch_stmt(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_int_t pc_label_site) { | |
5427 | duk_hthread *thr = comp_ctx->thr; | |
5428 | duk_reg_t temp_at_loop; | |
5429 | duk_regconst_t rc_switch; /* reg/const for switch value */ | |
5430 | duk_regconst_t rc_case; /* reg/const for case value */ | |
5431 | duk_reg_t reg_temp; /* general temp register */ | |
5432 | duk_int_t pc_prevcase = -1; | |
5433 | duk_int_t pc_prevstmt = -1; | |
5434 | duk_int_t pc_default = -1; /* -1 == not set, -2 == pending (next statement list) */ | |
5435 | ||
5436 | /* Note: negative pc values are ignored when patching jumps, so no explicit checks needed */ | |
5437 | ||
5438 | /* | |
5439 | * Switch is pretty complicated because of several conflicting concerns: | |
5440 | * | |
5441 | * - Want to generate code without an intermediate representation, | |
5442 | * i.e., in one go | |
5443 | * | |
5444 | * - Case selectors are expressions, not values, and may thus e.g. throw | |
5445 | * exceptions (which causes evaluation order concerns) | |
5446 | * | |
5447 | * - Evaluation semantics of case selectors and default clause need to be | |
5448 | * carefully implemented to provide correct behavior even with case value | |
5449 | * side effects | |
5450 | * | |
5451 | * - Fall through case and default clauses; avoiding dead JUMPs if case | |
5452 | * ends with an unconditional jump (a break or a continue) | |
5453 | * | |
5454 | * - The same case value may occur multiple times, but evaluation rules | |
5455 | * only process the first match before switching to a "propagation" mode | |
5456 | * where case values are no longer evaluated | |
5457 | * | |
5458 | * See E5 Section 12.11. Also see doc/compiler.rst for compilation | |
5459 | * discussion. | |
5460 | */ | |
5461 | ||
5462 | duk__advance(comp_ctx); | |
5463 | duk__advance_expect(comp_ctx, DUK_TOK_LPAREN); | |
5464 | rc_switch = duk__exprtop_toregconst(comp_ctx, res, DUK__BP_FOR_EXPR /*rbp_flags*/); | |
5465 | duk__advance_expect(comp_ctx, DUK_TOK_RPAREN); | |
5466 | duk__advance_expect(comp_ctx, DUK_TOK_LCURLY); | |
5467 | ||
5468 | DUK_DDD(DUK_DDDPRINT("switch value in register %ld", (long) rc_switch)); | |
5469 | ||
5470 | temp_at_loop = DUK__GETTEMP(comp_ctx); | |
5471 | ||
5472 | for (;;) { | |
5473 | duk_int_t num_stmts; | |
5474 | duk_small_int_t tok; | |
5475 | ||
5476 | /* sufficient for keeping temp reg numbers in check */ | |
5477 | DUK__SETTEMP(comp_ctx, temp_at_loop); | |
5478 | ||
5479 | if (comp_ctx->curr_token.t == DUK_TOK_RCURLY) { | |
5480 | break; | |
5481 | } | |
5482 | ||
5483 | /* | |
5484 | * Parse a case or default clause. | |
5485 | */ | |
5486 | ||
5487 | if (comp_ctx->curr_token.t == DUK_TOK_CASE) { | |
5488 | /* | |
5489 | * Case clause. | |
5490 | * | |
5491 | * Note: cannot use reg_case as a temp register (for SEQ target) | |
5492 | * because it may be a constant. | |
5493 | */ | |
5494 | ||
5495 | duk__patch_jump_here(comp_ctx, pc_prevcase); /* chain jumps for case | |
5496 | * evaluation and checking | |
5497 | */ | |
5498 | ||
5499 | duk__advance(comp_ctx); | |
5500 | rc_case = duk__exprtop_toregconst(comp_ctx, res, DUK__BP_FOR_EXPR /*rbp_flags*/); | |
5501 | duk__advance_expect(comp_ctx, DUK_TOK_COLON); | |
5502 | ||
5503 | reg_temp = DUK__ALLOCTEMP(comp_ctx); | |
5504 | duk__emit_a_b_c(comp_ctx, | |
5505 | DUK_OP_SEQ, | |
5506 | (duk_regconst_t) reg_temp, | |
5507 | rc_switch, | |
5508 | rc_case); | |
5509 | duk__emit_if_true_skip(comp_ctx, (duk_regconst_t) reg_temp); | |
5510 | ||
5511 | /* jump to next case clause */ | |
5512 | pc_prevcase = duk__emit_jump_empty(comp_ctx); /* no match, next case */ | |
5513 | ||
5514 | /* statements go here (if any) on next loop */ | |
5515 | } else if (comp_ctx->curr_token.t == DUK_TOK_DEFAULT) { | |
5516 | /* | |
5517 | * Default clause. | |
5518 | */ | |
5519 | ||
5520 | if (pc_default >= 0) { | |
5521 | goto syntax_error; | |
5522 | } | |
5523 | duk__advance(comp_ctx); | |
5524 | duk__advance_expect(comp_ctx, DUK_TOK_COLON); | |
5525 | ||
5526 | /* Fix for https://github.com/svaarala/duktape/issues/155: | |
5527 | * If 'default' is first clause (detected by pc_prevcase < 0) | |
5528 | * we need to ensure we stay in the matching chain. | |
5529 | */ | |
5530 | if (pc_prevcase < 0) { | |
5531 | DUK_DD(DUK_DDPRINT("default clause is first, emit prevcase jump")); | |
5532 | pc_prevcase = duk__emit_jump_empty(comp_ctx); | |
5533 | } | |
5534 | ||
5535 | /* default clause matches next statement list (if any) */ | |
5536 | pc_default = -2; | |
5537 | } else { | |
5538 | /* Code is not accepted before the first case/default clause */ | |
5539 | goto syntax_error; | |
5540 | } | |
5541 | ||
5542 | /* | |
5543 | * Parse code after the clause. Possible terminators are | |
5544 | * 'case', 'default', and '}'. | |
5545 | * | |
5546 | * Note that there may be no code at all, not even an empty statement, | |
5547 | * between case clauses. This must be handled just like an empty statement | |
5548 | * (omitting seemingly pointless JUMPs), to avoid situations like | |
5549 | * test-bug-case-fallthrough.js. | |
5550 | */ | |
5551 | ||
5552 | num_stmts = 0; | |
5553 | if (pc_default == -2) { | |
5554 | pc_default = duk__get_current_pc(comp_ctx); | |
5555 | } | |
5556 | ||
5557 | /* Note: this is correct even for default clause statements: | |
5558 | * they participate in 'fall-through' behavior even if the | |
5559 | * default clause is in the middle. | |
5560 | */ | |
5561 | duk__patch_jump_here(comp_ctx, pc_prevstmt); /* chain jumps for 'fall-through' | |
5562 | * after a case matches. | |
5563 | */ | |
5564 | ||
5565 | for (;;) { | |
5566 | tok = comp_ctx->curr_token.t; | |
5567 | if (tok == DUK_TOK_CASE || tok == DUK_TOK_DEFAULT || | |
5568 | tok == DUK_TOK_RCURLY) { | |
5569 | break; | |
5570 | } | |
5571 | num_stmts++; | |
5572 | duk__parse_stmt(comp_ctx, res, 0 /*allow_source_elem*/); | |
5573 | } | |
5574 | ||
5575 | /* fall-through jump to next code of next case (backpatched) */ | |
5576 | pc_prevstmt = duk__emit_jump_empty(comp_ctx); | |
5577 | ||
5578 | /* XXX: would be nice to omit this jump when the jump is not | |
5579 | * reachable, at least in the obvious cases (such as the case | |
5580 | * ending with a 'break'. | |
5581 | * | |
5582 | * Perhaps duk__parse_stmt() could provide some info on whether | |
5583 | * the statement is a "dead end"? | |
5584 | * | |
5585 | * If implemented, just set pc_prevstmt to -1 when not needed. | |
5586 | */ | |
5587 | } | |
5588 | ||
5589 | DUK_ASSERT(comp_ctx->curr_token.t == DUK_TOK_RCURLY); | |
5590 | duk__advance(comp_ctx); | |
5591 | ||
5592 | /* default case control flow patchup; note that if pc_prevcase < 0 | |
5593 | * (i.e. no case clauses), control enters default case automatically. | |
5594 | */ | |
5595 | if (pc_default >= 0) { | |
5596 | /* default case exists: go there if no case matches */ | |
5597 | duk__patch_jump(comp_ctx, pc_prevcase, pc_default); | |
5598 | } else { | |
5599 | /* default case does not exist, or no statements present | |
5600 | * after default case: finish case evaluation | |
5601 | */ | |
5602 | duk__patch_jump_here(comp_ctx, pc_prevcase); | |
5603 | } | |
5604 | ||
5605 | /* fall-through control flow patchup; note that pc_prevstmt may be | |
5606 | * < 0 (i.e. no case clauses), in which case this is a no-op. | |
5607 | */ | |
5608 | duk__patch_jump_here(comp_ctx, pc_prevstmt); | |
5609 | ||
5610 | /* continue jump not patched, an INVALID opcode remains there */ | |
5611 | duk__patch_jump_here(comp_ctx, pc_label_site + 1); /* break jump */ | |
5612 | ||
5613 | /* Note: 'fast' breaks will jump to pc_label_site + 1, which will | |
5614 | * then jump here. The double jump will be eliminated by a | |
5615 | * peephole pass, resulting in an optimal jump here. The label | |
5616 | * site jumps will remain in bytecode and will waste code size. | |
5617 | */ | |
5618 | ||
5619 | return; | |
5620 | ||
5621 | syntax_error: | |
11fdf7f2 | 5622 | DUK_ERROR_SYNTAX(thr, DUK_STR_INVALID_SWITCH); |
7c673cae FG |
5623 | } |
5624 | ||
5625 | DUK_LOCAL void duk__parse_if_stmt(duk_compiler_ctx *comp_ctx, duk_ivalue *res) { | |
5626 | duk_reg_t temp_reset; | |
5627 | duk_regconst_t rc_cond; | |
5628 | duk_int_t pc_jump_false; | |
5629 | ||
5630 | DUK_DDD(DUK_DDDPRINT("begin parsing if statement")); | |
5631 | ||
5632 | temp_reset = DUK__GETTEMP(comp_ctx); | |
5633 | ||
5634 | duk__advance(comp_ctx); /* eat 'if' */ | |
5635 | duk__advance_expect(comp_ctx, DUK_TOK_LPAREN); | |
5636 | ||
5637 | rc_cond = duk__exprtop_toregconst(comp_ctx, res, DUK__BP_FOR_EXPR /*rbp_flags*/); | |
5638 | duk__emit_if_true_skip(comp_ctx, rc_cond); | |
5639 | pc_jump_false = duk__emit_jump_empty(comp_ctx); /* jump to end or else part */ | |
5640 | DUK__SETTEMP(comp_ctx, temp_reset); | |
5641 | ||
5642 | duk__advance_expect(comp_ctx, DUK_TOK_RPAREN); | |
5643 | ||
5644 | duk__parse_stmt(comp_ctx, res, 0 /*allow_source_elem*/); | |
5645 | ||
5646 | /* The 'else' ambiguity is resolved by 'else' binding to the innermost | |
5647 | * construct, so greedy matching is correct here. | |
5648 | */ | |
5649 | ||
5650 | if (comp_ctx->curr_token.t == DUK_TOK_ELSE) { | |
5651 | duk_int_t pc_jump_end; | |
5652 | ||
5653 | DUK_DDD(DUK_DDDPRINT("if has else part")); | |
5654 | ||
5655 | duk__advance(comp_ctx); | |
5656 | ||
5657 | pc_jump_end = duk__emit_jump_empty(comp_ctx); /* jump from true part to end */ | |
5658 | duk__patch_jump_here(comp_ctx, pc_jump_false); | |
5659 | ||
5660 | duk__parse_stmt(comp_ctx, res, 0 /*allow_source_elem*/); | |
5661 | ||
5662 | duk__patch_jump_here(comp_ctx, pc_jump_end); | |
5663 | } else { | |
5664 | DUK_DDD(DUK_DDDPRINT("if does not have else part")); | |
5665 | ||
5666 | duk__patch_jump_here(comp_ctx, pc_jump_false); | |
5667 | } | |
5668 | ||
5669 | DUK_DDD(DUK_DDDPRINT("end parsing if statement")); | |
5670 | } | |
5671 | ||
5672 | DUK_LOCAL void duk__parse_do_stmt(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_int_t pc_label_site) { | |
5673 | duk_regconst_t rc_cond; | |
5674 | duk_int_t pc_start; | |
5675 | ||
5676 | DUK_DDD(DUK_DDDPRINT("begin parsing do statement")); | |
5677 | ||
5678 | duk__advance(comp_ctx); /* eat 'do' */ | |
5679 | ||
5680 | pc_start = duk__get_current_pc(comp_ctx); | |
5681 | duk__parse_stmt(comp_ctx, res, 0 /*allow_source_elem*/); | |
5682 | duk__patch_jump_here(comp_ctx, pc_label_site + 2); /* continue jump */ | |
5683 | ||
5684 | duk__advance_expect(comp_ctx, DUK_TOK_WHILE); | |
5685 | duk__advance_expect(comp_ctx, DUK_TOK_LPAREN); | |
5686 | ||
5687 | rc_cond = duk__exprtop_toregconst(comp_ctx, res, DUK__BP_FOR_EXPR /*rbp_flags*/); | |
5688 | duk__emit_if_false_skip(comp_ctx, rc_cond); | |
5689 | duk__emit_jump(comp_ctx, pc_start); | |
5690 | /* no need to reset temps, as we're finished emitting code */ | |
5691 | ||
5692 | duk__advance_expect(comp_ctx, DUK_TOK_RPAREN); | |
5693 | ||
5694 | duk__patch_jump_here(comp_ctx, pc_label_site + 1); /* break jump */ | |
5695 | ||
5696 | DUK_DDD(DUK_DDDPRINT("end parsing do statement")); | |
5697 | } | |
5698 | ||
5699 | DUK_LOCAL void duk__parse_while_stmt(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_int_t pc_label_site) { | |
5700 | duk_reg_t temp_reset; | |
5701 | duk_regconst_t rc_cond; | |
5702 | duk_int_t pc_start; | |
5703 | duk_int_t pc_jump_false; | |
5704 | ||
5705 | DUK_DDD(DUK_DDDPRINT("begin parsing while statement")); | |
5706 | ||
5707 | temp_reset = DUK__GETTEMP(comp_ctx); | |
5708 | ||
5709 | duk__advance(comp_ctx); /* eat 'while' */ | |
5710 | ||
5711 | duk__advance_expect(comp_ctx, DUK_TOK_LPAREN); | |
5712 | ||
5713 | pc_start = duk__get_current_pc(comp_ctx); | |
5714 | duk__patch_jump_here(comp_ctx, pc_label_site + 2); /* continue jump */ | |
5715 | ||
5716 | rc_cond = duk__exprtop_toregconst(comp_ctx, res, DUK__BP_FOR_EXPR /*rbp_flags*/); | |
5717 | duk__emit_if_true_skip(comp_ctx, rc_cond); | |
5718 | pc_jump_false = duk__emit_jump_empty(comp_ctx); | |
5719 | DUK__SETTEMP(comp_ctx, temp_reset); | |
5720 | ||
5721 | duk__advance_expect(comp_ctx, DUK_TOK_RPAREN); | |
5722 | ||
5723 | duk__parse_stmt(comp_ctx, res, 0 /*allow_source_elem*/); | |
5724 | duk__emit_jump(comp_ctx, pc_start); | |
5725 | ||
5726 | duk__patch_jump_here(comp_ctx, pc_jump_false); | |
5727 | duk__patch_jump_here(comp_ctx, pc_label_site + 1); /* break jump */ | |
5728 | ||
5729 | DUK_DDD(DUK_DDDPRINT("end parsing while statement")); | |
5730 | } | |
5731 | ||
5732 | DUK_LOCAL void duk__parse_break_or_continue_stmt(duk_compiler_ctx *comp_ctx, duk_ivalue *res) { | |
5733 | duk_hthread *thr = comp_ctx->thr; | |
5734 | duk_bool_t is_break = (comp_ctx->curr_token.t == DUK_TOK_BREAK); | |
5735 | duk_int_t label_id; | |
5736 | duk_int_t label_catch_depth; | |
5737 | duk_int_t label_pc; /* points to LABEL; pc+1 = jump site for break; pc+2 = jump site for continue */ | |
5738 | duk_bool_t label_is_closest; | |
5739 | ||
5740 | DUK_UNREF(res); | |
5741 | ||
5742 | duk__advance(comp_ctx); /* eat 'break' or 'continue' */ | |
5743 | ||
5744 | if (comp_ctx->curr_token.t == DUK_TOK_SEMICOLON || /* explicit semi follows */ | |
5745 | comp_ctx->curr_token.lineterm || /* automatic semi will be inserted */ | |
5746 | comp_ctx->curr_token.allow_auto_semi) { /* automatic semi will be inserted */ | |
5747 | /* break/continue without label */ | |
5748 | ||
5749 | duk__lookup_active_label(comp_ctx, DUK_HTHREAD_STRING_EMPTY_STRING(thr), is_break, &label_id, &label_catch_depth, &label_pc, &label_is_closest); | |
5750 | } else if (comp_ctx->curr_token.t == DUK_TOK_IDENTIFIER) { | |
5751 | /* break/continue with label (label cannot be a reserved word, production is 'Identifier' */ | |
5752 | DUK_ASSERT(comp_ctx->curr_token.str1 != NULL); | |
5753 | duk__lookup_active_label(comp_ctx, comp_ctx->curr_token.str1, is_break, &label_id, &label_catch_depth, &label_pc, &label_is_closest); | |
5754 | duk__advance(comp_ctx); | |
5755 | } else { | |
11fdf7f2 | 5756 | DUK_ERROR_SYNTAX(thr, DUK_STR_INVALID_BREAK_CONT_LABEL); |
7c673cae FG |
5757 | } |
5758 | ||
5759 | /* Use a fast break/continue when possible. A fast break/continue is | |
5760 | * just a jump to the LABEL break/continue jump slot, which then jumps | |
5761 | * to an appropriate place (for break, going through ENDLABEL correctly). | |
5762 | * The peephole optimizer will optimize the jump to a direct one. | |
5763 | */ | |
5764 | ||
5765 | if (label_catch_depth == comp_ctx->curr_func.catch_depth && | |
5766 | label_is_closest) { | |
5767 | DUK_DDD(DUK_DDDPRINT("break/continue: is_break=%ld, label_id=%ld, label_is_closest=%ld, " | |
5768 | "label_catch_depth=%ld, catch_depth=%ld " | |
5769 | "-> use fast variant (direct jump)", | |
5770 | (long) is_break, (long) label_id, (long) label_is_closest, | |
5771 | (long) label_catch_depth, (long) comp_ctx->curr_func.catch_depth)); | |
5772 | ||
5773 | duk__emit_jump(comp_ctx, label_pc + (is_break ? 1 : 2)); | |
5774 | } else { | |
5775 | DUK_DDD(DUK_DDDPRINT("break/continue: is_break=%ld, label_id=%ld, label_is_closest=%ld, " | |
5776 | "label_catch_depth=%ld, catch_depth=%ld " | |
5777 | "-> use slow variant (longjmp)", | |
5778 | (long) is_break, (long) label_id, (long) label_is_closest, | |
5779 | (long) label_catch_depth, (long) comp_ctx->curr_func.catch_depth)); | |
5780 | ||
5781 | duk__emit_extraop_bc(comp_ctx, | |
5782 | is_break ? DUK_EXTRAOP_BREAK : DUK_EXTRAOP_CONTINUE, | |
5783 | (duk_regconst_t) label_id); | |
5784 | } | |
5785 | } | |
5786 | ||
5787 | DUK_LOCAL void duk__parse_return_stmt(duk_compiler_ctx *comp_ctx, duk_ivalue *res) { | |
5788 | duk_hthread *thr = comp_ctx->thr; | |
5789 | duk_regconst_t rc_val; | |
5790 | duk_small_uint_t ret_flags; | |
5791 | ||
5792 | duk__advance(comp_ctx); /* eat 'return' */ | |
5793 | ||
5794 | /* A 'return' statement is only allowed inside an actual function body, | |
5795 | * not as part of eval or global code. | |
5796 | */ | |
5797 | if (!comp_ctx->curr_func.is_function) { | |
11fdf7f2 | 5798 | DUK_ERROR_SYNTAX(thr, DUK_STR_INVALID_RETURN); |
7c673cae FG |
5799 | } |
5800 | ||
7c673cae FG |
5801 | ret_flags = 0; |
5802 | ||
5803 | if (comp_ctx->curr_token.t == DUK_TOK_SEMICOLON || /* explicit semi follows */ | |
5804 | comp_ctx->curr_token.lineterm || /* automatic semi will be inserted */ | |
5805 | comp_ctx->curr_token.allow_auto_semi) { /* automatic semi will be inserted */ | |
5806 | DUK_DDD(DUK_DDDPRINT("empty return value -> undefined")); | |
5807 | rc_val = 0; | |
5808 | } else { | |
5809 | duk_int_t pc_before_expr; | |
5810 | duk_int_t pc_after_expr; | |
5811 | ||
5812 | DUK_DDD(DUK_DDDPRINT("return with a value")); | |
5813 | ||
5814 | DUK_UNREF(pc_before_expr); | |
5815 | DUK_UNREF(pc_after_expr); | |
5816 | ||
5817 | pc_before_expr = duk__get_current_pc(comp_ctx); | |
5818 | rc_val = duk__exprtop_toregconst(comp_ctx, res, DUK__BP_FOR_EXPR /*rbp_flags*/); | |
5819 | pc_after_expr = duk__get_current_pc(comp_ctx); | |
5820 | ||
5821 | /* Tail call check: if last opcode emitted was CALL(I), and | |
5822 | * the context allows it, change the CALL(I) to a tail call. | |
5823 | * This doesn't guarantee that a tail call will be allowed at | |
5824 | * runtime, so the RETURN must still be emitted. (Duktape | |
5825 | * 0.10.0 avoided this and simulated a RETURN if a tail call | |
5826 | * couldn't be used at runtime; but this didn't work | |
5827 | * correctly with a thread yield/resume, see | |
5828 | * test-bug-tailcall-thread-yield-resume.js for discussion.) | |
5829 | * | |
5830 | * In addition to the last opcode being CALL, we also need to | |
5831 | * be sure that 'rc_val' is the result register of the CALL(I). | |
5832 | * For instance, for the expression 'return 0, (function () | |
5833 | * { return 1; }), 2' the last opcode emitted is CALL (no | |
5834 | * bytecode is emitted for '2') but 'rc_val' indicates | |
5835 | * constant '2'. Similarly if '2' is replaced by a register | |
5836 | * bound variable, no opcodes are emitted but tail call would | |
5837 | * be incorrect. | |
5838 | * | |
5839 | * This is tricky and easy to get wrong. It would be best to | |
5840 | * track enough expression metadata to check that 'rc_val' came | |
5841 | * from that last CALL instruction. We don't have that metadata | |
5842 | * now, so we check that 'rc_val' is a temporary register result | |
5843 | * (not a constant or a register bound variable). There should | |
5844 | * be no way currently for 'rc_val' to be a temporary for an | |
5845 | * expression following the CALL instruction without emitting | |
5846 | * some opcodes following the CALL. This proxy check is used | |
5847 | * below. | |
5848 | * | |
5849 | * See: test-bug-comma-expr-gh131.js. | |
5850 | * | |
5851 | * The non-standard 'caller' property disables tail calls | |
5852 | * because they pose some special cases which haven't been | |
5853 | * fixed yet. | |
5854 | */ | |
5855 | ||
5856 | #if defined(DUK_USE_TAILCALL) | |
5857 | if (comp_ctx->curr_func.catch_depth == 0 && /* no catchers */ | |
5858 | pc_after_expr > pc_before_expr) { /* at least one opcode emitted */ | |
5859 | duk_compiler_instr *instr; | |
5860 | duk_small_uint_t op; | |
5861 | ||
5862 | instr = duk__get_instr_ptr(comp_ctx, pc_after_expr - 1); | |
5863 | DUK_ASSERT(instr != NULL); | |
5864 | ||
5865 | op = (duk_small_uint_t) DUK_DEC_OP(instr->ins); | |
5866 | if ((op == DUK_OP_CALL || op == DUK_OP_CALLI) && | |
5867 | DUK__ISTEMP(comp_ctx, rc_val) /* see above */) { | |
5868 | DUK_DDD(DUK_DDDPRINT("return statement detected a tail call opportunity: " | |
5869 | "catch depth is 0, duk__exprtop() emitted >= 1 instructions, " | |
5870 | "and last instruction is a CALL " | |
5871 | "-> set TAILCALL flag")); | |
5872 | /* Just flip the single bit. */ | |
5873 | instr->ins |= DUK_ENC_OP_A_B_C(0, DUK_BC_CALL_FLAG_TAILCALL, 0, 0); | |
5874 | } | |
5875 | } | |
5876 | #endif /* DUK_USE_TAILCALL */ | |
5877 | ||
5878 | ret_flags = DUK_BC_RETURN_FLAG_HAVE_RETVAL; | |
5879 | } | |
5880 | ||
7c673cae FG |
5881 | duk__emit_a_b(comp_ctx, |
5882 | DUK_OP_RETURN | DUK__EMIT_FLAG_NO_SHUFFLE_A, | |
5883 | (duk_regconst_t) ret_flags /*flags*/, | |
5884 | rc_val /*reg*/); | |
5885 | } | |
5886 | ||
5887 | DUK_LOCAL void duk__parse_throw_stmt(duk_compiler_ctx *comp_ctx, duk_ivalue *res) { | |
5888 | duk_reg_t reg_val; | |
5889 | ||
5890 | duk__advance(comp_ctx); /* eat 'throw' */ | |
5891 | ||
5892 | /* Unlike break/continue, throw statement does not allow an empty value. */ | |
5893 | ||
5894 | if (comp_ctx->curr_token.lineterm) { | |
11fdf7f2 | 5895 | DUK_ERROR_SYNTAX(comp_ctx->thr, DUK_STR_INVALID_THROW); |
7c673cae FG |
5896 | } |
5897 | ||
5898 | reg_val = duk__exprtop_toreg(comp_ctx, res, DUK__BP_FOR_EXPR /*rbp_flags*/); | |
5899 | duk__emit_extraop_bc(comp_ctx, | |
5900 | DUK_EXTRAOP_THROW, | |
5901 | (duk_regconst_t) reg_val); | |
5902 | } | |
5903 | ||
5904 | DUK_LOCAL void duk__parse_try_stmt(duk_compiler_ctx *comp_ctx, duk_ivalue *res) { | |
5905 | duk_hthread *thr = comp_ctx->thr; | |
5906 | duk_context *ctx = (duk_context *) thr; | |
5907 | duk_reg_t reg_catch; /* reg_catch+0 and reg_catch+1 are reserved for TRYCATCH */ | |
5908 | duk_regconst_t rc_varname = 0; | |
5909 | duk_small_uint_t trycatch_flags = 0; | |
5910 | duk_int_t pc_ldconst = -1; | |
5911 | duk_int_t pc_trycatch = -1; | |
5912 | duk_int_t pc_catch = -1; | |
5913 | duk_int_t pc_finally = -1; | |
5914 | ||
5915 | DUK_UNREF(res); | |
5916 | ||
5917 | /* | |
5918 | * See the following documentation for discussion: | |
5919 | * | |
5920 | * doc/execution.rst: control flow details | |
5921 | * | |
5922 | * Try, catch, and finally "parts" are Blocks, not Statements, so | |
5923 | * they must always be delimited by curly braces. This is unlike e.g. | |
5924 | * the if statement, which accepts any Statement. This eliminates any | |
5925 | * questions of matching parts of nested try statements. The Block | |
5926 | * parsing is implemented inline here (instead of calling out). | |
5927 | * | |
5928 | * Finally part has a 'let scoped' variable, which requires a few kinks | |
5929 | * here. | |
5930 | */ | |
5931 | ||
5932 | comp_ctx->curr_func.catch_depth++; | |
5933 | ||
5934 | duk__advance(comp_ctx); /* eat 'try' */ | |
5935 | ||
5936 | reg_catch = DUK__ALLOCTEMPS(comp_ctx, 2); | |
5937 | ||
5938 | /* The target for this LDCONST may need output shuffling, but we assume | |
5939 | * that 'pc_ldconst' will be the LDCONST that we can patch later. This | |
5940 | * should be the case because there's no input shuffling. (If there's | |
5941 | * no catch clause, this LDCONST will be replaced with a NOP.) | |
5942 | */ | |
5943 | pc_ldconst = duk__get_current_pc(comp_ctx); | |
5944 | duk__emit_a_bc(comp_ctx, DUK_OP_LDCONST, reg_catch, 0 /*patched later*/); | |
5945 | ||
5946 | pc_trycatch = duk__get_current_pc(comp_ctx); | |
5947 | duk__emit_invalid(comp_ctx); /* TRYCATCH, cannot emit now (not enough info) */ | |
5948 | duk__emit_invalid(comp_ctx); /* jump for 'catch' case */ | |
5949 | duk__emit_invalid(comp_ctx); /* jump for 'finally' case or end (if no finally) */ | |
5950 | ||
5951 | /* try part */ | |
5952 | duk__advance_expect(comp_ctx, DUK_TOK_LCURLY); | |
5953 | duk__parse_stmts(comp_ctx, 0 /*allow_source_elem*/, 0 /*expect_eof*/); | |
5954 | /* the DUK_TOK_RCURLY is eaten by duk__parse_stmts() */ | |
5955 | duk__emit_extraop_only(comp_ctx, | |
5956 | DUK_EXTRAOP_ENDTRY); | |
5957 | ||
5958 | if (comp_ctx->curr_token.t == DUK_TOK_CATCH) { | |
5959 | /* | |
5960 | * The catch variable must be updated to reflect the new allocated | |
5961 | * register for the duration of the catch clause. We need to store | |
5962 | * and restore the original value for the varmap entry (if any). | |
5963 | */ | |
5964 | ||
5965 | /* | |
5966 | * Note: currently register bindings must be fixed for the entire | |
5967 | * function. So, even though the catch variable is in a register | |
5968 | * we know, we must use an explicit environment record and slow path | |
5969 | * accesses to read/write the catch binding to make closures created | |
5970 | * within the catch clause work correctly. This restriction should | |
5971 | * be fixable (at least in common cases) later. | |
5972 | * | |
5973 | * See: test-bug-catch-binding-2.js. | |
5974 | * | |
5975 | * XXX: improve to get fast path access to most catch clauses. | |
5976 | */ | |
5977 | ||
5978 | duk_hstring *h_var; | |
5979 | duk_int_t varmap_value; /* for storing/restoring the varmap binding for catch variable */ | |
5980 | ||
5981 | DUK_DDD(DUK_DDDPRINT("stack top at start of catch clause: %ld", (long) duk_get_top(ctx))); | |
5982 | ||
5983 | trycatch_flags |= DUK_BC_TRYCATCH_FLAG_HAVE_CATCH; | |
5984 | ||
5985 | pc_catch = duk__get_current_pc(comp_ctx); | |
5986 | ||
5987 | duk__advance(comp_ctx); | |
5988 | duk__advance_expect(comp_ctx, DUK_TOK_LPAREN); | |
5989 | ||
5990 | if (comp_ctx->curr_token.t != DUK_TOK_IDENTIFIER) { | |
5991 | /* Identifier, i.e. don't allow reserved words */ | |
5992 | goto syntax_error; | |
5993 | } | |
5994 | h_var = comp_ctx->curr_token.str1; | |
5995 | DUK_ASSERT(h_var != NULL); | |
5996 | ||
5997 | duk_push_hstring(ctx, h_var); /* keep in on valstack, use borrowed ref below */ | |
5998 | ||
5999 | if (comp_ctx->curr_func.is_strict && | |
6000 | ((h_var == DUK_HTHREAD_STRING_EVAL(thr)) || | |
6001 | (h_var == DUK_HTHREAD_STRING_LC_ARGUMENTS(thr)))) { | |
6002 | DUK_DDD(DUK_DDDPRINT("catch identifier 'eval' or 'arguments' in strict mode -> SyntaxError")); | |
6003 | goto syntax_error; | |
6004 | } | |
6005 | ||
6006 | duk_dup_top(ctx); | |
6007 | rc_varname = duk__getconst(comp_ctx); | |
6008 | DUK_DDD(DUK_DDDPRINT("catch clause, rc_varname=0x%08lx (%ld)", | |
6009 | (unsigned long) rc_varname, (long) rc_varname)); | |
6010 | ||
6011 | duk__advance(comp_ctx); | |
6012 | duk__advance_expect(comp_ctx, DUK_TOK_RPAREN); | |
6013 | ||
6014 | duk__advance_expect(comp_ctx, DUK_TOK_LCURLY); | |
6015 | ||
6016 | DUK_DDD(DUK_DDDPRINT("varmap before modifying for catch clause: %!iT", | |
6017 | (duk_tval *) duk_get_tval(ctx, comp_ctx->curr_func.varmap_idx))); | |
6018 | ||
6019 | duk_dup_top(ctx); | |
6020 | duk_get_prop(ctx, comp_ctx->curr_func.varmap_idx); | |
6021 | if (duk_is_undefined(ctx, -1)) { | |
6022 | varmap_value = -2; | |
6023 | } else if (duk_is_null(ctx, -1)) { | |
6024 | varmap_value = -1; | |
6025 | } else { | |
6026 | DUK_ASSERT(duk_is_number(ctx, -1)); | |
6027 | varmap_value = duk_get_int(ctx, -1); | |
6028 | DUK_ASSERT(varmap_value >= 0); | |
6029 | } | |
6030 | duk_pop(ctx); | |
6031 | ||
6032 | #if 0 | |
6033 | /* It'd be nice to do something like this - but it doesn't | |
6034 | * work for closures created inside the catch clause. | |
6035 | */ | |
6036 | duk_dup_top(ctx); | |
6037 | duk_push_int(ctx, (duk_int_t) (reg_catch + 0)); | |
6038 | duk_put_prop(ctx, comp_ctx->curr_func.varmap_idx); | |
6039 | #endif | |
6040 | duk_dup_top(ctx); | |
6041 | duk_push_null(ctx); | |
6042 | duk_put_prop(ctx, comp_ctx->curr_func.varmap_idx); | |
6043 | ||
6044 | duk__emit_a_bc(comp_ctx, | |
6045 | DUK_OP_PUTVAR | DUK__EMIT_FLAG_A_IS_SOURCE, | |
6046 | (duk_regconst_t) (reg_catch + 0) /*value*/, | |
6047 | rc_varname /*varname*/); | |
6048 | ||
6049 | DUK_DDD(DUK_DDDPRINT("varmap before parsing catch clause: %!iT", | |
6050 | (duk_tval *) duk_get_tval(ctx, comp_ctx->curr_func.varmap_idx))); | |
6051 | ||
6052 | duk__parse_stmts(comp_ctx, 0 /*allow_source_elem*/, 0 /*expect_eof*/); | |
6053 | /* the DUK_TOK_RCURLY is eaten by duk__parse_stmts() */ | |
6054 | ||
6055 | if (varmap_value == -2) { | |
6056 | /* not present */ | |
6057 | duk_del_prop(ctx, comp_ctx->curr_func.varmap_idx); | |
6058 | } else { | |
6059 | if (varmap_value == -1) { | |
6060 | duk_push_null(ctx); | |
6061 | } else { | |
6062 | DUK_ASSERT(varmap_value >= 0); | |
6063 | duk_push_int(ctx, varmap_value); | |
6064 | } | |
6065 | duk_put_prop(ctx, comp_ctx->curr_func.varmap_idx); | |
6066 | } | |
6067 | /* varname is popped by above code */ | |
6068 | ||
6069 | DUK_DDD(DUK_DDDPRINT("varmap after restore catch clause: %!iT", | |
6070 | (duk_tval *) duk_get_tval(ctx, comp_ctx->curr_func.varmap_idx))); | |
6071 | ||
6072 | duk__emit_extraop_only(comp_ctx, | |
6073 | DUK_EXTRAOP_ENDCATCH); | |
6074 | ||
6075 | /* | |
6076 | * XXX: for now, indicate that an expensive catch binding | |
6077 | * declarative environment is always needed. If we don't | |
6078 | * need it, we don't need the const_varname either. | |
6079 | */ | |
6080 | ||
6081 | trycatch_flags |= DUK_BC_TRYCATCH_FLAG_CATCH_BINDING; | |
6082 | ||
6083 | DUK_DDD(DUK_DDDPRINT("stack top at end of catch clause: %ld", (long) duk_get_top(ctx))); | |
6084 | } | |
6085 | ||
6086 | if (comp_ctx->curr_token.t == DUK_TOK_FINALLY) { | |
6087 | trycatch_flags |= DUK_BC_TRYCATCH_FLAG_HAVE_FINALLY; | |
6088 | ||
6089 | pc_finally = duk__get_current_pc(comp_ctx); | |
6090 | ||
6091 | duk__advance(comp_ctx); | |
6092 | ||
6093 | duk__advance_expect(comp_ctx, DUK_TOK_LCURLY); | |
6094 | duk__parse_stmts(comp_ctx, 0 /*allow_source_elem*/, 0 /*expect_eof*/); | |
6095 | /* the DUK_TOK_RCURLY is eaten by duk__parse_stmts() */ | |
6096 | duk__emit_extraop_b(comp_ctx, | |
6097 | DUK_EXTRAOP_ENDFIN, | |
6098 | reg_catch); /* rethrow */ | |
6099 | } | |
6100 | ||
6101 | if (!(trycatch_flags & DUK_BC_TRYCATCH_FLAG_HAVE_CATCH) && | |
6102 | !(trycatch_flags & DUK_BC_TRYCATCH_FLAG_HAVE_FINALLY)) { | |
6103 | /* must have catch and/or finally */ | |
6104 | goto syntax_error; | |
6105 | } | |
6106 | ||
6107 | /* If there's no catch block, rc_varname will be 0 and duk__patch_trycatch() | |
6108 | * will replace the LDCONST with a NOP. For any actual constant (including | |
6109 | * constant 0) the DUK__CONST_MARKER flag will be set in rc_varname. | |
6110 | */ | |
6111 | ||
6112 | duk__patch_trycatch(comp_ctx, | |
6113 | pc_ldconst, | |
6114 | pc_trycatch, | |
6115 | reg_catch, | |
6116 | rc_varname, | |
6117 | trycatch_flags); | |
6118 | ||
6119 | if (trycatch_flags & DUK_BC_TRYCATCH_FLAG_HAVE_CATCH) { | |
6120 | DUK_ASSERT(pc_catch >= 0); | |
6121 | duk__patch_jump(comp_ctx, pc_trycatch + 1, pc_catch); | |
6122 | } | |
6123 | ||
6124 | if (trycatch_flags & DUK_BC_TRYCATCH_FLAG_HAVE_FINALLY) { | |
6125 | DUK_ASSERT(pc_finally >= 0); | |
6126 | duk__patch_jump(comp_ctx, pc_trycatch + 2, pc_finally); | |
6127 | } else { | |
6128 | /* without finally, the second jump slot is used to jump to end of stmt */ | |
6129 | duk__patch_jump_here(comp_ctx, pc_trycatch + 2); | |
6130 | } | |
6131 | ||
6132 | comp_ctx->curr_func.catch_depth--; | |
6133 | return; | |
6134 | ||
6135 | syntax_error: | |
11fdf7f2 | 6136 | DUK_ERROR_SYNTAX(thr, DUK_STR_INVALID_TRY); |
7c673cae FG |
6137 | } |
6138 | ||
6139 | DUK_LOCAL void duk__parse_with_stmt(duk_compiler_ctx *comp_ctx, duk_ivalue *res) { | |
6140 | duk_int_t pc_trycatch; | |
6141 | duk_int_t pc_finished; | |
6142 | duk_reg_t reg_catch; | |
6143 | duk_small_uint_t trycatch_flags; | |
6144 | ||
6145 | if (comp_ctx->curr_func.is_strict) { | |
11fdf7f2 | 6146 | DUK_ERROR_SYNTAX(comp_ctx->thr, DUK_STR_WITH_IN_STRICT_MODE); |
7c673cae FG |
6147 | } |
6148 | ||
6149 | comp_ctx->curr_func.catch_depth++; | |
6150 | ||
6151 | duk__advance(comp_ctx); /* eat 'with' */ | |
6152 | ||
6153 | reg_catch = DUK__ALLOCTEMPS(comp_ctx, 2); | |
6154 | ||
6155 | duk__advance_expect(comp_ctx, DUK_TOK_LPAREN); | |
6156 | duk__exprtop_toforcedreg(comp_ctx, res, DUK__BP_FOR_EXPR /*rbp_flags*/, reg_catch); | |
6157 | duk__advance_expect(comp_ctx, DUK_TOK_RPAREN); | |
6158 | ||
6159 | pc_trycatch = duk__get_current_pc(comp_ctx); | |
6160 | trycatch_flags = DUK_BC_TRYCATCH_FLAG_WITH_BINDING; | |
6161 | duk__emit_a_bc(comp_ctx, | |
6162 | DUK_OP_TRYCATCH | DUK__EMIT_FLAG_NO_SHUFFLE_A, | |
6163 | (duk_regconst_t) trycatch_flags /*a*/, | |
6164 | (duk_regconst_t) reg_catch /*bc*/); | |
6165 | duk__emit_invalid(comp_ctx); /* catch jump */ | |
6166 | duk__emit_invalid(comp_ctx); /* finished jump */ | |
6167 | ||
6168 | duk__parse_stmt(comp_ctx, res, 0 /*allow_source_elem*/); | |
6169 | duk__emit_extraop_only(comp_ctx, | |
6170 | DUK_EXTRAOP_ENDTRY); | |
6171 | ||
6172 | pc_finished = duk__get_current_pc(comp_ctx); | |
6173 | ||
6174 | duk__patch_jump(comp_ctx, pc_trycatch + 2, pc_finished); | |
6175 | ||
6176 | comp_ctx->curr_func.catch_depth--; | |
6177 | } | |
6178 | ||
6179 | DUK_LOCAL duk_int_t duk__stmt_label_site(duk_compiler_ctx *comp_ctx, duk_int_t label_id) { | |
6180 | /* if a site already exists, nop: max one label site per statement */ | |
6181 | if (label_id >= 0) { | |
6182 | return label_id; | |
6183 | } | |
6184 | ||
6185 | label_id = comp_ctx->curr_func.label_next++; | |
6186 | DUK_DDD(DUK_DDDPRINT("allocated new label id for label site: %ld", (long) label_id)); | |
6187 | ||
6188 | duk__emit_extraop_bc(comp_ctx, | |
6189 | DUK_EXTRAOP_LABEL, | |
6190 | (duk_regconst_t) label_id); | |
6191 | duk__emit_invalid(comp_ctx); | |
6192 | duk__emit_invalid(comp_ctx); | |
6193 | ||
6194 | return label_id; | |
6195 | } | |
6196 | ||
6197 | /* Parse a single statement. | |
6198 | * | |
6199 | * Creates a label site (with an empty label) automatically for iteration | |
6200 | * statements. Also "peels off" any label statements for explicit labels. | |
6201 | */ | |
6202 | DUK_LOCAL void duk__parse_stmt(duk_compiler_ctx *comp_ctx, duk_ivalue *res, duk_bool_t allow_source_elem) { | |
6203 | duk_hthread *thr = comp_ctx->thr; | |
6204 | duk_context *ctx = (duk_context *) thr; | |
6205 | duk_bool_t dir_prol_at_entry; /* directive prologue status at entry */ | |
6206 | duk_reg_t temp_at_entry; | |
6207 | duk_uarridx_t labels_len_at_entry; | |
6208 | duk_int_t pc_at_entry; /* assumed to also be PC of "LABEL" */ | |
6209 | duk_int_t stmt_id; | |
6210 | duk_small_uint_t stmt_flags = 0; | |
6211 | duk_int_t label_id = -1; | |
6212 | duk_small_uint_t tok; | |
6213 | ||
6214 | DUK__RECURSION_INCREASE(comp_ctx, thr); | |
6215 | ||
6216 | temp_at_entry = DUK__GETTEMP(comp_ctx); | |
6217 | pc_at_entry = duk__get_current_pc(comp_ctx); | |
6218 | labels_len_at_entry = (duk_uarridx_t) duk_get_length(ctx, comp_ctx->curr_func.labelnames_idx); | |
6219 | stmt_id = comp_ctx->curr_func.stmt_next++; | |
6220 | dir_prol_at_entry = comp_ctx->curr_func.in_directive_prologue; | |
6221 | ||
6222 | DUK_UNREF(stmt_id); | |
6223 | ||
6224 | DUK_DDD(DUK_DDDPRINT("parsing a statement, stmt_id=%ld, temp_at_entry=%ld, labels_len_at_entry=%ld, " | |
6225 | "is_strict=%ld, in_directive_prologue=%ld, catch_depth=%ld", | |
6226 | (long) stmt_id, (long) temp_at_entry, (long) labels_len_at_entry, | |
6227 | (long) comp_ctx->curr_func.is_strict, (long) comp_ctx->curr_func.in_directive_prologue, | |
6228 | (long) comp_ctx->curr_func.catch_depth)); | |
6229 | ||
6230 | /* The directive prologue flag is cleared by default so that it is | |
6231 | * unset for any recursive statement parsing. It is only "revived" | |
6232 | * if a directive is detected. (We could also make directives only | |
6233 | * allowed if 'allow_source_elem' was true.) | |
6234 | */ | |
6235 | comp_ctx->curr_func.in_directive_prologue = 0; | |
6236 | ||
6237 | retry_parse: | |
6238 | ||
6239 | DUK_DDD(DUK_DDDPRINT("try stmt parse, stmt_id=%ld, label_id=%ld, allow_source_elem=%ld, catch_depth=%ld", | |
6240 | (long) stmt_id, (long) label_id, (long) allow_source_elem, | |
6241 | (long) comp_ctx->curr_func.catch_depth)); | |
6242 | ||
6243 | /* | |
6244 | * Detect iteration statements; if encountered, establish an | |
6245 | * empty label. | |
6246 | */ | |
6247 | ||
6248 | tok = comp_ctx->curr_token.t; | |
6249 | if (tok == DUK_TOK_FOR || tok == DUK_TOK_DO || tok == DUK_TOK_WHILE || | |
6250 | tok == DUK_TOK_SWITCH) { | |
6251 | DUK_DDD(DUK_DDDPRINT("iteration/switch statement -> add empty label")); | |
6252 | ||
6253 | label_id = duk__stmt_label_site(comp_ctx, label_id); | |
6254 | duk__add_label(comp_ctx, | |
6255 | DUK_HTHREAD_STRING_EMPTY_STRING(thr), | |
6256 | pc_at_entry /*pc_label*/, | |
6257 | label_id); | |
6258 | } | |
6259 | ||
6260 | /* | |
6261 | * Main switch for statement / source element type. | |
6262 | */ | |
6263 | ||
6264 | switch (comp_ctx->curr_token.t) { | |
6265 | case DUK_TOK_FUNCTION: { | |
6266 | /* | |
6267 | * Function declaration, function expression, or (non-standard) | |
6268 | * function statement. | |
6269 | * | |
6270 | * The E5 specification only allows function declarations at | |
6271 | * the top level (in "source elements"). An ExpressionStatement | |
6272 | * is explicitly not allowed to begin with a "function" keyword | |
6273 | * (E5 Section 12.4). Hence any non-error semantics for such | |
6274 | * non-top-level statements are non-standard. Duktape semantics | |
6275 | * for function statements are modelled after V8, see | |
6276 | * test-dev-func-decl-outside-top.js. | |
6277 | */ | |
6278 | ||
6279 | #if defined(DUK_USE_NONSTD_FUNC_STMT) | |
6280 | /* Lenient: allow function declarations outside top level in | |
6281 | * non-strict mode but reject them in strict mode. | |
6282 | */ | |
6283 | if (allow_source_elem || !comp_ctx->curr_func.is_strict) | |
6284 | #else /* DUK_USE_NONSTD_FUNC_STMT */ | |
6285 | /* Strict: never allow function declarations outside top level. */ | |
6286 | if (allow_source_elem) | |
6287 | #endif /* DUK_USE_NONSTD_FUNC_STMT */ | |
6288 | { | |
6289 | /* FunctionDeclaration: not strictly a statement but handled as such. | |
6290 | * | |
6291 | * O(depth^2) parse count for inner functions is handled by recording a | |
6292 | * lexer offset on the first compilation pass, so that the function can | |
6293 | * be efficiently skipped on the second pass. This is encapsulated into | |
6294 | * duk__parse_func_like_fnum(). | |
6295 | */ | |
6296 | ||
6297 | duk_int_t fnum; | |
6298 | ||
6299 | DUK_DDD(DUK_DDDPRINT("function declaration statement")); | |
6300 | ||
6301 | duk__advance(comp_ctx); /* eat 'function' */ | |
6302 | fnum = duk__parse_func_like_fnum(comp_ctx, 1 /*is_decl*/, 0 /*is_setget*/); | |
6303 | ||
6304 | if (comp_ctx->curr_func.in_scanning) { | |
6305 | duk_uarridx_t n; | |
6306 | duk_hstring *h_funcname; | |
6307 | ||
6308 | duk_get_prop_index(ctx, comp_ctx->curr_func.funcs_idx, fnum * 3); | |
6309 | duk_get_prop_stridx(ctx, -1, DUK_STRIDX_NAME); /* -> [ ... func name ] */ | |
6310 | h_funcname = duk_get_hstring(ctx, -1); | |
6311 | DUK_ASSERT(h_funcname != NULL); | |
6312 | ||
6313 | DUK_DDD(DUK_DDDPRINT("register function declaration %!O in pass 1, fnum %ld", | |
6314 | (duk_heaphdr *) h_funcname, (long) fnum)); | |
6315 | n = (duk_uarridx_t) duk_get_length(ctx, comp_ctx->curr_func.decls_idx); | |
6316 | duk_push_hstring(ctx, h_funcname); | |
6317 | duk_put_prop_index(ctx, comp_ctx->curr_func.decls_idx, n); | |
6318 | duk_push_int(ctx, (duk_int_t) (DUK_DECL_TYPE_FUNC + (fnum << 8))); | |
6319 | duk_put_prop_index(ctx, comp_ctx->curr_func.decls_idx, n + 1); | |
6320 | ||
6321 | duk_pop_n(ctx, 2); | |
6322 | } | |
6323 | ||
6324 | /* no statement value (unlike function expression) */ | |
6325 | stmt_flags = 0; | |
6326 | break; | |
6327 | } else { | |
11fdf7f2 | 6328 | DUK_ERROR_SYNTAX(thr, DUK_STR_FUNC_STMT_NOT_ALLOWED); |
7c673cae FG |
6329 | } |
6330 | break; | |
6331 | } | |
6332 | case DUK_TOK_LCURLY: { | |
6333 | DUK_DDD(DUK_DDDPRINT("block statement")); | |
6334 | duk__advance(comp_ctx); | |
6335 | duk__parse_stmts(comp_ctx, 0 /*allow_source_elem*/, 0 /*expect_eof*/); | |
6336 | /* the DUK_TOK_RCURLY is eaten by duk__parse_stmts() */ | |
6337 | if (label_id >= 0) { | |
6338 | duk__patch_jump_here(comp_ctx, pc_at_entry + 1); /* break jump */ | |
6339 | } | |
6340 | stmt_flags = 0; | |
6341 | break; | |
6342 | } | |
11fdf7f2 TL |
6343 | case DUK_TOK_CONST: { |
6344 | DUK_DDD(DUK_DDDPRINT("constant declaration statement")); | |
6345 | duk__parse_var_stmt(comp_ctx, res, DUK__EXPR_FLAG_REQUIRE_INIT /*expr_flags*/); | |
6346 | stmt_flags = DUK__HAS_TERM; | |
6347 | break; | |
6348 | } | |
7c673cae FG |
6349 | case DUK_TOK_VAR: { |
6350 | DUK_DDD(DUK_DDDPRINT("variable declaration statement")); | |
11fdf7f2 | 6351 | duk__parse_var_stmt(comp_ctx, res, 0 /*expr_flags*/); |
7c673cae FG |
6352 | stmt_flags = DUK__HAS_TERM; |
6353 | break; | |
6354 | } | |
6355 | case DUK_TOK_SEMICOLON: { | |
6356 | /* empty statement with an explicit semicolon */ | |
6357 | DUK_DDD(DUK_DDDPRINT("empty statement")); | |
6358 | stmt_flags = DUK__HAS_TERM; | |
6359 | break; | |
6360 | } | |
6361 | case DUK_TOK_IF: { | |
6362 | DUK_DDD(DUK_DDDPRINT("if statement")); | |
6363 | duk__parse_if_stmt(comp_ctx, res); | |
6364 | if (label_id >= 0) { | |
6365 | duk__patch_jump_here(comp_ctx, pc_at_entry + 1); /* break jump */ | |
6366 | } | |
6367 | stmt_flags = 0; | |
6368 | break; | |
6369 | } | |
6370 | case DUK_TOK_DO: { | |
6371 | /* | |
6372 | * Do-while statement is mostly trivial, but there is special | |
6373 | * handling for automatic semicolon handling (triggered by the | |
6374 | * DUK__ALLOW_AUTO_SEMI_ALWAYS) flag related to a bug filed at: | |
6375 | * | |
6376 | * https://bugs.ecmascript.org/show_bug.cgi?id=8 | |
6377 | * | |
6378 | * See doc/compiler.rst for details. | |
6379 | */ | |
6380 | DUK_DDD(DUK_DDDPRINT("do statement")); | |
6381 | DUK_ASSERT(label_id >= 0); | |
6382 | duk__update_label_flags(comp_ctx, | |
6383 | label_id, | |
6384 | DUK_LABEL_FLAG_ALLOW_BREAK | DUK_LABEL_FLAG_ALLOW_CONTINUE); | |
6385 | duk__parse_do_stmt(comp_ctx, res, pc_at_entry); | |
6386 | stmt_flags = DUK__HAS_TERM | DUK__ALLOW_AUTO_SEMI_ALWAYS; /* DUK__ALLOW_AUTO_SEMI_ALWAYS workaround */ | |
6387 | break; | |
6388 | } | |
6389 | case DUK_TOK_WHILE: { | |
6390 | DUK_DDD(DUK_DDDPRINT("while statement")); | |
6391 | DUK_ASSERT(label_id >= 0); | |
6392 | duk__update_label_flags(comp_ctx, | |
6393 | label_id, | |
6394 | DUK_LABEL_FLAG_ALLOW_BREAK | DUK_LABEL_FLAG_ALLOW_CONTINUE); | |
6395 | duk__parse_while_stmt(comp_ctx, res, pc_at_entry); | |
6396 | stmt_flags = 0; | |
6397 | break; | |
6398 | } | |
6399 | case DUK_TOK_FOR: { | |
6400 | /* | |
6401 | * For/for-in statement is complicated to parse because | |
6402 | * determining the statement type (three-part for vs. a | |
6403 | * for-in) requires potential backtracking. | |
6404 | * | |
6405 | * See the helper for the messy stuff. | |
6406 | */ | |
6407 | DUK_DDD(DUK_DDDPRINT("for/for-in statement")); | |
6408 | DUK_ASSERT(label_id >= 0); | |
6409 | duk__update_label_flags(comp_ctx, | |
6410 | label_id, | |
6411 | DUK_LABEL_FLAG_ALLOW_BREAK | DUK_LABEL_FLAG_ALLOW_CONTINUE); | |
6412 | duk__parse_for_stmt(comp_ctx, res, pc_at_entry); | |
6413 | stmt_flags = 0; | |
6414 | break; | |
6415 | } | |
6416 | case DUK_TOK_CONTINUE: | |
6417 | case DUK_TOK_BREAK: { | |
6418 | DUK_DDD(DUK_DDDPRINT("break/continue statement")); | |
6419 | duk__parse_break_or_continue_stmt(comp_ctx, res); | |
6420 | stmt_flags = DUK__HAS_TERM | DUK__IS_TERMINAL; | |
6421 | break; | |
6422 | } | |
6423 | case DUK_TOK_RETURN: { | |
6424 | DUK_DDD(DUK_DDDPRINT("return statement")); | |
6425 | duk__parse_return_stmt(comp_ctx, res); | |
6426 | stmt_flags = DUK__HAS_TERM | DUK__IS_TERMINAL; | |
6427 | break; | |
6428 | } | |
6429 | case DUK_TOK_WITH: { | |
6430 | DUK_DDD(DUK_DDDPRINT("with statement")); | |
6431 | comp_ctx->curr_func.with_depth++; | |
6432 | duk__parse_with_stmt(comp_ctx, res); | |
6433 | if (label_id >= 0) { | |
6434 | duk__patch_jump_here(comp_ctx, pc_at_entry + 1); /* break jump */ | |
6435 | } | |
6436 | comp_ctx->curr_func.with_depth--; | |
6437 | stmt_flags = 0; | |
6438 | break; | |
6439 | } | |
6440 | case DUK_TOK_SWITCH: { | |
6441 | /* | |
6442 | * The switch statement is pretty messy to compile. | |
6443 | * See the helper for details. | |
6444 | */ | |
6445 | DUK_DDD(DUK_DDDPRINT("switch statement")); | |
6446 | DUK_ASSERT(label_id >= 0); | |
6447 | duk__update_label_flags(comp_ctx, | |
6448 | label_id, | |
6449 | DUK_LABEL_FLAG_ALLOW_BREAK); /* don't allow continue */ | |
6450 | duk__parse_switch_stmt(comp_ctx, res, pc_at_entry); | |
6451 | stmt_flags = 0; | |
6452 | break; | |
6453 | } | |
6454 | case DUK_TOK_THROW: { | |
6455 | DUK_DDD(DUK_DDDPRINT("throw statement")); | |
6456 | duk__parse_throw_stmt(comp_ctx, res); | |
6457 | stmt_flags = DUK__HAS_TERM | DUK__IS_TERMINAL; | |
6458 | break; | |
6459 | } | |
6460 | case DUK_TOK_TRY: { | |
6461 | DUK_DDD(DUK_DDDPRINT("try statement")); | |
6462 | duk__parse_try_stmt(comp_ctx, res); | |
6463 | stmt_flags = 0; | |
6464 | break; | |
6465 | } | |
6466 | case DUK_TOK_DEBUGGER: { | |
11fdf7f2 | 6467 | duk__advance(comp_ctx); |
7c673cae FG |
6468 | #if defined(DUK_USE_DEBUGGER_SUPPORT) |
6469 | DUK_DDD(DUK_DDDPRINT("debugger statement: debugging enabled, emit debugger opcode")); | |
6470 | duk__emit_extraop_only(comp_ctx, DUK_EXTRAOP_DEBUGGER); | |
6471 | #else | |
6472 | DUK_DDD(DUK_DDDPRINT("debugger statement: ignored")); | |
6473 | #endif | |
7c673cae FG |
6474 | stmt_flags = DUK__HAS_TERM; |
6475 | break; | |
6476 | } | |
6477 | default: { | |
6478 | /* | |
6479 | * Else, must be one of: | |
6480 | * - ExpressionStatement, possibly a directive (String) | |
6481 | * - LabelledStatement (Identifier followed by ':') | |
6482 | * | |
6483 | * Expressions beginning with 'function' keyword are covered by a case | |
6484 | * above (such expressions are not allowed in standard E5 anyway). | |
6485 | * Also expressions starting with '{' are interpreted as block | |
6486 | * statements. See E5 Section 12.4. | |
6487 | * | |
6488 | * Directive detection is tricky; see E5 Section 14.1 on directive | |
6489 | * prologue. A directive is an expression statement with a single | |
6490 | * string literal and an explicit or automatic semicolon. Escape | |
6491 | * characters are significant and no parens etc are allowed: | |
6492 | * | |
6493 | * 'use strict'; // valid 'use strict' directive | |
6494 | * 'use\u0020strict'; // valid directive, not a 'use strict' directive | |
6495 | * ('use strict'); // not a valid directive | |
6496 | * | |
6497 | * The expression is determined to consist of a single string literal | |
6498 | * based on duk__expr_nud() and duk__expr_led() call counts. The string literal | |
6499 | * of a 'use strict' directive is determined to lack any escapes based | |
6500 | * num_escapes count from the lexer. Note that other directives may be | |
6501 | * allowed to contain escapes, so a directive with escapes does not | |
6502 | * terminate a directive prologue. | |
6503 | * | |
6504 | * We rely on the fact that the expression parser will not emit any | |
6505 | * code for a single token expression. However, it will generate an | |
6506 | * intermediate value which we will then successfully ignore. | |
6507 | * | |
6508 | * A similar approach is used for labels. | |
6509 | */ | |
6510 | ||
6511 | duk_bool_t single_token; | |
6512 | ||
6513 | DUK_DDD(DUK_DDDPRINT("expression statement")); | |
6514 | duk__exprtop(comp_ctx, res, DUK__BP_FOR_EXPR /*rbp_flags*/); | |
6515 | ||
6516 | single_token = (comp_ctx->curr_func.nud_count == 1 && /* one token */ | |
6517 | comp_ctx->curr_func.led_count == 0); /* no operators */ | |
6518 | ||
6519 | if (single_token && | |
6520 | comp_ctx->prev_token.t == DUK_TOK_IDENTIFIER && | |
6521 | comp_ctx->curr_token.t == DUK_TOK_COLON) { | |
6522 | /* | |
6523 | * Detected label | |
6524 | */ | |
6525 | ||
6526 | duk_hstring *h_lab; | |
6527 | ||
6528 | /* expected ival */ | |
6529 | DUK_ASSERT(res->t == DUK_IVAL_VAR); | |
6530 | DUK_ASSERT(res->x1.t == DUK_ISPEC_VALUE); | |
6531 | DUK_ASSERT(DUK_TVAL_IS_STRING(duk_get_tval(ctx, res->x1.valstack_idx))); | |
6532 | h_lab = comp_ctx->prev_token.str1; | |
6533 | DUK_ASSERT(h_lab != NULL); | |
6534 | ||
6535 | DUK_DDD(DUK_DDDPRINT("explicit label site for label '%!O'", | |
6536 | (duk_heaphdr *) h_lab)); | |
6537 | ||
6538 | duk__advance(comp_ctx); /* eat colon */ | |
6539 | ||
6540 | label_id = duk__stmt_label_site(comp_ctx, label_id); | |
6541 | ||
6542 | duk__add_label(comp_ctx, | |
6543 | h_lab, | |
6544 | pc_at_entry /*pc_label*/, | |
6545 | label_id); | |
6546 | ||
6547 | /* a statement following a label cannot be a source element | |
6548 | * (a function declaration). | |
6549 | */ | |
6550 | allow_source_elem = 0; | |
6551 | ||
6552 | DUK_DDD(DUK_DDDPRINT("label handled, retry statement parsing")); | |
6553 | goto retry_parse; | |
6554 | } | |
6555 | ||
6556 | stmt_flags = 0; | |
6557 | ||
6558 | if (dir_prol_at_entry && /* still in prologue */ | |
6559 | single_token && /* single string token */ | |
6560 | comp_ctx->prev_token.t == DUK_TOK_STRING) { | |
6561 | /* | |
6562 | * Detected a directive | |
6563 | */ | |
6564 | duk_hstring *h_dir; | |
6565 | ||
6566 | /* expected ival */ | |
6567 | DUK_ASSERT(res->t == DUK_IVAL_PLAIN); | |
6568 | DUK_ASSERT(res->x1.t == DUK_ISPEC_VALUE); | |
6569 | DUK_ASSERT(DUK_TVAL_IS_STRING(duk_get_tval(ctx, res->x1.valstack_idx))); | |
6570 | h_dir = comp_ctx->prev_token.str1; | |
6571 | DUK_ASSERT(h_dir != NULL); | |
6572 | ||
6573 | DUK_DDD(DUK_DDDPRINT("potential directive: %!O", h_dir)); | |
6574 | ||
6575 | stmt_flags |= DUK__STILL_PROLOGUE; | |
6576 | ||
6577 | /* Note: escaped characters differentiate directives */ | |
6578 | ||
6579 | if (comp_ctx->prev_token.num_escapes > 0) { | |
6580 | DUK_DDD(DUK_DDDPRINT("directive contains escapes: valid directive " | |
6581 | "but we ignore such directives")); | |
6582 | } else { | |
6583 | /* | |
6584 | * The length comparisons are present to handle | |
6585 | * strings like "use strict\u0000foo" as required. | |
6586 | */ | |
6587 | ||
6588 | if (DUK_HSTRING_GET_BYTELEN(h_dir) == 10 && | |
6589 | DUK_STRNCMP((const char *) DUK_HSTRING_GET_DATA(h_dir), "use strict", 10) == 0) { | |
6590 | #if defined(DUK_USE_STRICT_DECL) | |
6591 | DUK_DDD(DUK_DDDPRINT("use strict directive detected: strict flag %ld -> %ld", | |
6592 | (long) comp_ctx->curr_func.is_strict, (long) 1)); | |
6593 | comp_ctx->curr_func.is_strict = 1; | |
6594 | #else | |
6595 | DUK_DDD(DUK_DDDPRINT("use strict detected but strict declarations disabled, ignoring")); | |
6596 | #endif | |
6597 | } else if (DUK_HSTRING_GET_BYTELEN(h_dir) == 14 && | |
6598 | DUK_STRNCMP((const char *) DUK_HSTRING_GET_DATA(h_dir), "use duk notail", 14) == 0) { | |
6599 | DUK_DDD(DUK_DDDPRINT("use duk notail directive detected: notail flag %ld -> %ld", | |
6600 | (long) comp_ctx->curr_func.is_notail, (long) 1)); | |
6601 | comp_ctx->curr_func.is_notail = 1; | |
6602 | } else { | |
6603 | DUK_DD(DUK_DDPRINT("unknown directive: '%!O', ignoring but not terminating " | |
6604 | "directive prologue", (duk_hobject *) h_dir)); | |
6605 | } | |
6606 | } | |
6607 | } else { | |
6608 | DUK_DDD(DUK_DDDPRINT("non-directive expression statement or no longer in prologue; " | |
6609 | "prologue terminated if still active")); | |
6610 | } | |
6611 | ||
6612 | stmt_flags |= DUK__HAS_VAL | DUK__HAS_TERM; | |
6613 | } | |
6614 | } /* end switch (tok) */ | |
6615 | ||
6616 | /* | |
6617 | * Statement value handling. | |
6618 | * | |
6619 | * Global code and eval code has an implicit return value | |
6620 | * which comes from the last statement with a value | |
6621 | * (technically a non-"empty" continuation, which is | |
6622 | * different from an empty statement). | |
6623 | * | |
6624 | * Since we don't know whether a later statement will | |
6625 | * override the value of the current statement, we need | |
6626 | * to coerce the statement value to a register allocated | |
6627 | * for implicit return values. In other cases we need | |
6628 | * to coerce the statement value to a plain value to get | |
6629 | * any side effects out (consider e.g. "foo.bar;"). | |
6630 | */ | |
6631 | ||
6632 | /* XXX: what about statements which leave a half-cooked value in 'res' | |
6633 | * but have no stmt value? Any such statements? | |
6634 | */ | |
6635 | ||
6636 | if (stmt_flags & DUK__HAS_VAL) { | |
6637 | duk_reg_t reg_stmt_value = comp_ctx->curr_func.reg_stmt_value; | |
6638 | if (reg_stmt_value >= 0) { | |
6639 | duk__ivalue_toforcedreg(comp_ctx, res, reg_stmt_value); | |
6640 | } else { | |
6641 | duk__ivalue_toplain_ignore(comp_ctx, res); | |
6642 | } | |
6643 | } else { | |
6644 | ; | |
6645 | } | |
6646 | ||
6647 | /* | |
6648 | * Statement terminator check, including automatic semicolon | |
6649 | * handling. After this step, 'curr_tok' should be the first | |
6650 | * token after a possible statement terminator. | |
6651 | */ | |
6652 | ||
6653 | if (stmt_flags & DUK__HAS_TERM) { | |
6654 | if (comp_ctx->curr_token.t == DUK_TOK_SEMICOLON) { | |
6655 | DUK_DDD(DUK_DDDPRINT("explicit semicolon terminates statement")); | |
6656 | duk__advance(comp_ctx); | |
6657 | } else { | |
6658 | if (comp_ctx->curr_token.allow_auto_semi) { | |
6659 | DUK_DDD(DUK_DDDPRINT("automatic semicolon terminates statement")); | |
6660 | } else if (stmt_flags & DUK__ALLOW_AUTO_SEMI_ALWAYS) { | |
6661 | /* XXX: make this lenience dependent on flags or strictness? */ | |
6662 | DUK_DDD(DUK_DDDPRINT("automatic semicolon terminates statement (allowed for compatibility " | |
6663 | "even though no lineterm present before next token)")); | |
6664 | } else { | |
11fdf7f2 | 6665 | DUK_ERROR_SYNTAX(thr, DUK_STR_UNTERMINATED_STMT); |
7c673cae FG |
6666 | } |
6667 | } | |
6668 | } else { | |
6669 | DUK_DDD(DUK_DDDPRINT("statement has no terminator")); | |
6670 | } | |
6671 | ||
6672 | /* | |
6673 | * Directive prologue tracking. | |
6674 | */ | |
6675 | ||
6676 | if (stmt_flags & DUK__STILL_PROLOGUE) { | |
6677 | DUK_DDD(DUK_DDDPRINT("setting in_directive_prologue")); | |
6678 | comp_ctx->curr_func.in_directive_prologue = 1; | |
6679 | } | |
6680 | ||
6681 | /* | |
6682 | * Cleanups (all statement parsing flows through here). | |
6683 | * | |
6684 | * Pop label site and reset labels. Reset 'next temp' to value at | |
6685 | * entry to reuse temps. | |
6686 | */ | |
6687 | ||
6688 | if (label_id >= 0) { | |
6689 | duk__emit_extraop_bc(comp_ctx, | |
6690 | DUK_EXTRAOP_ENDLABEL, | |
6691 | (duk_regconst_t) label_id); | |
6692 | } | |
6693 | ||
6694 | DUK__SETTEMP(comp_ctx, temp_at_entry); | |
6695 | ||
6696 | duk__reset_labels_to_length(comp_ctx, labels_len_at_entry); | |
6697 | ||
6698 | /* XXX: return indication of "terminalness" (e.g. a 'throw' is terminal) */ | |
6699 | ||
6700 | DUK__RECURSION_DECREASE(comp_ctx, thr); | |
6701 | } | |
6702 | ||
6703 | #undef DUK__HAS_VAL | |
6704 | #undef DUK__HAS_TERM | |
6705 | #undef DUK__ALLOW_AUTO_SEMI_ALWAYS | |
6706 | ||
6707 | /* | |
6708 | * Parse a statement list. | |
6709 | * | |
6710 | * Handles automatic semicolon insertion and implicit return value. | |
6711 | * | |
6712 | * Upon entry, 'curr_tok' should contain the first token of the first | |
6713 | * statement (parsed in the "allow regexp literal" mode). Upon exit, | |
6714 | * 'curr_tok' contains the token following the statement list terminator | |
6715 | * (EOF or closing brace). | |
6716 | */ | |
6717 | ||
6718 | DUK_LOCAL void duk__parse_stmts(duk_compiler_ctx *comp_ctx, duk_bool_t allow_source_elem, duk_bool_t expect_eof) { | |
6719 | duk_hthread *thr = comp_ctx->thr; | |
6720 | duk_context *ctx = (duk_context *) thr; | |
6721 | duk_ivalue res_alloc; | |
6722 | duk_ivalue *res = &res_alloc; | |
6723 | ||
6724 | /* Setup state. Initial ivalue is 'undefined'. */ | |
6725 | ||
6726 | duk_require_stack(ctx, DUK__PARSE_STATEMENTS_SLOTS); | |
6727 | ||
6728 | /* XXX: 'res' setup can be moved to function body level; in fact, two 'res' | |
6729 | * intermediate values suffice for parsing of each function. Nesting is needed | |
6730 | * for nested functions (which may occur inside expressions). | |
6731 | */ | |
6732 | ||
6733 | DUK_MEMZERO(&res_alloc, sizeof(res_alloc)); | |
6734 | res->t = DUK_IVAL_PLAIN; | |
6735 | res->x1.t = DUK_ISPEC_VALUE; | |
6736 | res->x1.valstack_idx = duk_get_top(ctx); | |
6737 | res->x2.valstack_idx = res->x1.valstack_idx + 1; | |
6738 | duk_push_undefined(ctx); | |
6739 | duk_push_undefined(ctx); | |
6740 | ||
6741 | /* Parse statements until a closing token (EOF or '}') is found. */ | |
6742 | ||
6743 | for (;;) { | |
6744 | /* Check whether statement list ends. */ | |
6745 | ||
6746 | if (expect_eof) { | |
6747 | if (comp_ctx->curr_token.t == DUK_TOK_EOF) { | |
6748 | break; | |
6749 | } | |
6750 | } else { | |
6751 | if (comp_ctx->curr_token.t == DUK_TOK_RCURLY) { | |
6752 | break; | |
6753 | } | |
6754 | } | |
6755 | ||
6756 | /* Check statement type based on the first token type. | |
6757 | * | |
6758 | * Note: expression parsing helpers expect 'curr_tok' to | |
6759 | * contain the first token of the expression upon entry. | |
6760 | */ | |
6761 | ||
6762 | DUK_DDD(DUK_DDDPRINT("TOKEN %ld (non-whitespace, non-comment)", (long) comp_ctx->curr_token.t)); | |
6763 | ||
6764 | duk__parse_stmt(comp_ctx, res, allow_source_elem); | |
6765 | } | |
6766 | ||
6767 | duk__advance(comp_ctx); | |
6768 | ||
6769 | /* Tear down state. */ | |
6770 | ||
6771 | duk_pop_2(ctx); | |
6772 | } | |
6773 | ||
6774 | /* | |
6775 | * Declaration binding instantiation conceptually happens when calling a | |
6776 | * function; for us it essentially means that function prologue. The | |
6777 | * conceptual process is described in E5 Section 10.5. | |
6778 | * | |
6779 | * We need to keep track of all encountered identifiers to (1) create an | |
6780 | * identifier-to-register map ("varmap"); and (2) detect duplicate | |
6781 | * declarations. Identifiers which are not bound to registers still need | |
6782 | * to be tracked for detecting duplicates. Currently such identifiers | |
6783 | * are put into the varmap with a 'null' value, which is later cleaned up. | |
6784 | * | |
6785 | * To support functions with a large number of variable and function | |
6786 | * declarations, registers are not allocated beyond a certain limit; | |
6787 | * after that limit, variables and functions need slow path access. | |
6788 | * Arguments are currently always register bound, which imposes a hard | |
6789 | * (and relatively small) argument count limit. | |
6790 | * | |
6791 | * Some bindings in E5 are not configurable (= deletable) and almost all | |
6792 | * are mutable (writable). Exceptions are: | |
6793 | * | |
6794 | * - The 'arguments' binding, established only if no shadowing argument | |
6795 | * or function declaration exists. We handle 'arguments' creation | |
6796 | * and binding through an explicit slow path environment record. | |
6797 | * | |
6798 | * - The "name" binding for a named function expression. This is also | |
6799 | * handled through an explicit slow path environment record. | |
6800 | */ | |
6801 | ||
6802 | /* XXX: add support for variables to not be register bound always, to | |
6803 | * handle cases with a very large number of variables? | |
6804 | */ | |
6805 | ||
6806 | DUK_LOCAL void duk__init_varmap_and_prologue_for_pass2(duk_compiler_ctx *comp_ctx, duk_reg_t *out_stmt_value_reg) { | |
6807 | duk_hthread *thr = comp_ctx->thr; | |
6808 | duk_context *ctx = (duk_context *) thr; | |
6809 | duk_hstring *h_name; | |
6810 | duk_bool_t configurable_bindings; | |
6811 | duk_uarridx_t num_args; | |
6812 | duk_uarridx_t num_decls; | |
6813 | duk_regconst_t rc_name; | |
6814 | duk_small_uint_t declvar_flags; | |
6815 | duk_uarridx_t i; | |
6816 | #ifdef DUK_USE_ASSERTIONS | |
6817 | duk_idx_t entry_top; | |
6818 | #endif | |
6819 | ||
6820 | #ifdef DUK_USE_ASSERTIONS | |
6821 | entry_top = duk_get_top(ctx); | |
6822 | #endif | |
6823 | ||
6824 | /* | |
6825 | * Preliminaries | |
6826 | */ | |
6827 | ||
6828 | configurable_bindings = comp_ctx->curr_func.is_eval; | |
6829 | DUK_DDD(DUK_DDDPRINT("configurable_bindings=%ld", (long) configurable_bindings)); | |
6830 | ||
6831 | /* varmap is already in comp_ctx->curr_func.varmap_idx */ | |
6832 | ||
6833 | /* | |
6834 | * Function formal arguments, always bound to registers | |
6835 | * (there's no support for shuffling them now). | |
6836 | */ | |
6837 | ||
6838 | num_args = (duk_uarridx_t) duk_get_length(ctx, comp_ctx->curr_func.argnames_idx); | |
6839 | DUK_DDD(DUK_DDDPRINT("num_args=%ld", (long) num_args)); | |
6840 | /* XXX: check num_args */ | |
6841 | ||
6842 | for (i = 0; i < num_args; i++) { | |
6843 | duk_get_prop_index(ctx, comp_ctx->curr_func.argnames_idx, i); | |
6844 | h_name = duk_get_hstring(ctx, -1); | |
6845 | DUK_ASSERT(h_name != NULL); | |
6846 | ||
6847 | if (comp_ctx->curr_func.is_strict) { | |
6848 | if (duk__hstring_is_eval_or_arguments(comp_ctx, h_name)) { | |
6849 | DUK_DDD(DUK_DDDPRINT("arg named 'eval' or 'arguments' in strict mode -> SyntaxError")); | |
6850 | goto error_argname; | |
6851 | } | |
6852 | duk_dup_top(ctx); | |
6853 | if (duk_has_prop(ctx, comp_ctx->curr_func.varmap_idx)) { | |
6854 | DUK_DDD(DUK_DDDPRINT("duplicate arg name in strict mode -> SyntaxError")); | |
6855 | goto error_argname; | |
6856 | } | |
6857 | ||
6858 | /* Ensure argument name is not a reserved word in current | |
6859 | * (final) strictness. Formal argument parsing may not | |
6860 | * catch reserved names if strictness changes during | |
6861 | * parsing. | |
6862 | * | |
6863 | * We only need to do this in strict mode because non-strict | |
6864 | * keyword are always detected in formal argument parsing. | |
6865 | */ | |
6866 | ||
6867 | if (DUK_HSTRING_HAS_STRICT_RESERVED_WORD(h_name)) { | |
6868 | goto error_argname; | |
6869 | } | |
6870 | } | |
6871 | ||
6872 | /* overwrite any previous binding of the same name; the effect is | |
6873 | * that last argument of a certain name wins. | |
6874 | */ | |
6875 | ||
6876 | /* only functions can have arguments */ | |
6877 | DUK_ASSERT(comp_ctx->curr_func.is_function); | |
6878 | duk_push_uarridx(ctx, i); /* -> [ ... name index ] */ | |
6879 | duk_put_prop(ctx, comp_ctx->curr_func.varmap_idx); /* -> [ ... ] */ | |
6880 | ||
6881 | /* no code needs to be emitted, the regs already have values */ | |
6882 | } | |
6883 | ||
6884 | /* use temp_next for tracking register allocations */ | |
6885 | DUK__SETTEMP_CHECKMAX(comp_ctx, (duk_reg_t) num_args); | |
6886 | ||
6887 | /* | |
6888 | * After arguments, allocate special registers (like shuffling temps) | |
6889 | */ | |
6890 | ||
6891 | if (out_stmt_value_reg) { | |
6892 | *out_stmt_value_reg = DUK__ALLOCTEMP(comp_ctx); | |
6893 | } | |
6894 | if (comp_ctx->curr_func.needs_shuffle) { | |
6895 | duk_reg_t shuffle_base = DUK__ALLOCTEMPS(comp_ctx, 3); | |
6896 | comp_ctx->curr_func.shuffle1 = shuffle_base; | |
6897 | comp_ctx->curr_func.shuffle2 = shuffle_base + 1; | |
6898 | comp_ctx->curr_func.shuffle3 = shuffle_base + 2; | |
6899 | DUK_D(DUK_DPRINT("shuffle registers needed by function, allocated: %ld %ld %ld", | |
6900 | (long) comp_ctx->curr_func.shuffle1, | |
6901 | (long) comp_ctx->curr_func.shuffle2, | |
6902 | (long) comp_ctx->curr_func.shuffle3)); | |
6903 | } | |
6904 | if (comp_ctx->curr_func.temp_next > 0x100) { | |
6905 | DUK_D(DUK_DPRINT("not enough 8-bit regs: temp_next=%ld", (long) comp_ctx->curr_func.temp_next)); | |
6906 | goto error_outofregs; | |
6907 | } | |
6908 | ||
6909 | /* | |
6910 | * Function declarations | |
6911 | */ | |
6912 | ||
6913 | num_decls = (duk_uarridx_t) duk_get_length(ctx, comp_ctx->curr_func.decls_idx); | |
6914 | DUK_DDD(DUK_DDDPRINT("num_decls=%ld -> %!T", | |
6915 | (long) num_decls, | |
6916 | (duk_tval *) duk_get_tval(ctx, comp_ctx->curr_func.decls_idx))); | |
6917 | for (i = 0; i < num_decls; i += 2) { | |
6918 | duk_int_t decl_type; | |
6919 | duk_int_t fnum; | |
6920 | ||
6921 | duk_get_prop_index(ctx, comp_ctx->curr_func.decls_idx, i + 1); /* decl type */ | |
6922 | decl_type = duk_to_int(ctx, -1); | |
6923 | fnum = decl_type >> 8; /* XXX: macros */ | |
6924 | decl_type = decl_type & 0xff; | |
6925 | duk_pop(ctx); | |
6926 | ||
6927 | if (decl_type != DUK_DECL_TYPE_FUNC) { | |
6928 | continue; | |
6929 | } | |
6930 | ||
6931 | duk_get_prop_index(ctx, comp_ctx->curr_func.decls_idx, i); /* decl name */ | |
6932 | ||
6933 | /* XXX: spilling */ | |
6934 | if (comp_ctx->curr_func.is_function) { | |
6935 | duk_reg_t reg_bind; | |
6936 | duk_dup_top(ctx); | |
6937 | if (duk_has_prop(ctx, comp_ctx->curr_func.varmap_idx)) { | |
6938 | /* shadowed; update value */ | |
6939 | duk_dup_top(ctx); | |
6940 | duk_get_prop(ctx, comp_ctx->curr_func.varmap_idx); | |
6941 | reg_bind = duk_to_int(ctx, -1); /* [ ... name reg_bind ] */ | |
6942 | duk__emit_a_bc(comp_ctx, | |
6943 | DUK_OP_CLOSURE, | |
6944 | (duk_regconst_t) reg_bind, | |
6945 | (duk_regconst_t) fnum); | |
6946 | } else { | |
6947 | /* function: always register bound */ | |
6948 | reg_bind = DUK__ALLOCTEMP(comp_ctx); | |
6949 | duk__emit_a_bc(comp_ctx, | |
6950 | DUK_OP_CLOSURE, | |
6951 | (duk_regconst_t) reg_bind, | |
6952 | (duk_regconst_t) fnum); | |
6953 | duk_push_int(ctx, (duk_int_t) reg_bind); | |
6954 | } | |
6955 | } else { | |
6956 | /* Function declaration for global/eval code is emitted even | |
6957 | * for duplicates, because of E5 Section 10.5, step 5.e of | |
6958 | * E5.1 (special behavior for variable bound to global object). | |
6959 | * | |
6960 | * DECLVAR will not re-declare a variable as such, but will | |
6961 | * update the binding value. | |
6962 | */ | |
6963 | ||
6964 | duk_reg_t reg_temp = DUK__ALLOCTEMP(comp_ctx); | |
6965 | duk_dup_top(ctx); | |
6966 | rc_name = duk__getconst(comp_ctx); | |
6967 | duk_push_null(ctx); | |
6968 | ||
6969 | duk__emit_a_bc(comp_ctx, | |
6970 | DUK_OP_CLOSURE, | |
6971 | (duk_regconst_t) reg_temp, | |
6972 | (duk_regconst_t) fnum); | |
6973 | ||
6974 | declvar_flags = DUK_PROPDESC_FLAG_WRITABLE | | |
6975 | DUK_PROPDESC_FLAG_ENUMERABLE | | |
6976 | DUK_BC_DECLVAR_FLAG_FUNC_DECL; | |
6977 | ||
6978 | if (configurable_bindings) { | |
6979 | declvar_flags |= DUK_PROPDESC_FLAG_CONFIGURABLE; | |
6980 | } | |
6981 | ||
6982 | duk__emit_a_b_c(comp_ctx, | |
6983 | DUK_OP_DECLVAR | DUK__EMIT_FLAG_NO_SHUFFLE_A, | |
6984 | (duk_regconst_t) declvar_flags /*flags*/, | |
6985 | rc_name /*name*/, | |
6986 | (duk_regconst_t) reg_temp /*value*/); | |
6987 | ||
6988 | DUK__SETTEMP(comp_ctx, reg_temp); /* forget temp */ | |
6989 | } | |
6990 | ||
6991 | DUK_DDD(DUK_DDDPRINT("function declaration to varmap: %!T -> %!T", | |
6992 | (duk_tval *) duk_get_tval(ctx, -2), | |
6993 | (duk_tval *) duk_get_tval(ctx, -1))); | |
6994 | ||
6995 | duk_put_prop(ctx, comp_ctx->curr_func.varmap_idx); /* [ ... name reg/null ] -> [ ... ] */ | |
6996 | } | |
6997 | ||
6998 | /* | |
6999 | * 'arguments' binding is special; if a shadowing argument or | |
7000 | * function declaration exists, an arguments object will | |
7001 | * definitely not be needed, regardless of whether the identifier | |
7002 | * 'arguments' is referenced inside the function body. | |
7003 | */ | |
7004 | ||
7005 | if (duk_has_prop_stridx(ctx, comp_ctx->curr_func.varmap_idx, DUK_STRIDX_LC_ARGUMENTS)) { | |
7006 | DUK_DDD(DUK_DDDPRINT("'arguments' is shadowed by argument or function declaration " | |
7007 | "-> arguments object creation can be skipped")); | |
7008 | comp_ctx->curr_func.is_arguments_shadowed = 1; | |
7009 | } | |
7010 | ||
7011 | /* | |
7012 | * Variable declarations. | |
7013 | * | |
7014 | * Unlike function declarations, variable declaration values don't get | |
7015 | * assigned on entry. If a binding of the same name already exists, just | |
7016 | * ignore it silently. | |
7017 | */ | |
7018 | ||
7019 | for (i = 0; i < num_decls; i += 2) { | |
7020 | duk_int_t decl_type; | |
7021 | ||
7022 | duk_get_prop_index(ctx, comp_ctx->curr_func.decls_idx, i + 1); /* decl type */ | |
7023 | decl_type = duk_to_int(ctx, -1); | |
7024 | decl_type = decl_type & 0xff; | |
7025 | duk_pop(ctx); | |
7026 | ||
7027 | if (decl_type != DUK_DECL_TYPE_VAR) { | |
7028 | continue; | |
7029 | } | |
7030 | ||
7031 | duk_get_prop_index(ctx, comp_ctx->curr_func.decls_idx, i); /* decl name */ | |
7032 | ||
7033 | if (duk_has_prop(ctx, comp_ctx->curr_func.varmap_idx)) { | |
7034 | /* shadowed, ignore */ | |
7035 | } else { | |
7036 | duk_get_prop_index(ctx, comp_ctx->curr_func.decls_idx, i); /* decl name */ | |
7037 | h_name = duk_get_hstring(ctx, -1); | |
7038 | DUK_ASSERT(h_name != NULL); | |
7039 | ||
7040 | if (h_name == DUK_HTHREAD_STRING_LC_ARGUMENTS(thr) && | |
7041 | !comp_ctx->curr_func.is_arguments_shadowed) { | |
7042 | /* E5 Section steps 7-8 */ | |
7043 | DUK_DDD(DUK_DDDPRINT("'arguments' not shadowed by a function declaration, " | |
7044 | "but appears as a variable declaration -> treat as " | |
7045 | "a no-op for variable declaration purposes")); | |
7046 | duk_pop(ctx); | |
7047 | continue; | |
7048 | } | |
7049 | ||
7050 | /* XXX: spilling */ | |
7051 | if (comp_ctx->curr_func.is_function) { | |
7052 | duk_reg_t reg_bind = DUK__ALLOCTEMP(comp_ctx); | |
7053 | /* no need to init reg, it will be undefined on entry */ | |
7054 | duk_push_int(ctx, (duk_int_t) reg_bind); | |
7055 | } else { | |
7056 | duk_dup_top(ctx); | |
7057 | rc_name = duk__getconst(comp_ctx); | |
7058 | duk_push_null(ctx); | |
7059 | ||
7060 | declvar_flags = DUK_PROPDESC_FLAG_WRITABLE | | |
7061 | DUK_PROPDESC_FLAG_ENUMERABLE | | |
7062 | DUK_BC_DECLVAR_FLAG_UNDEF_VALUE; | |
7063 | if (configurable_bindings) { | |
7064 | declvar_flags |= DUK_PROPDESC_FLAG_CONFIGURABLE; | |
7065 | } | |
7066 | ||
7067 | duk__emit_a_b_c(comp_ctx, | |
7068 | DUK_OP_DECLVAR | DUK__EMIT_FLAG_NO_SHUFFLE_A, | |
7069 | (duk_regconst_t) declvar_flags /*flags*/, | |
7070 | rc_name /*name*/, | |
7071 | (duk_regconst_t) 0 /*value*/); | |
7072 | } | |
7073 | ||
7074 | duk_put_prop(ctx, comp_ctx->curr_func.varmap_idx); /* [ ... name reg/null ] -> [ ... ] */ | |
7075 | } | |
7076 | } | |
7077 | ||
7078 | /* | |
7079 | * Wrap up | |
7080 | */ | |
7081 | ||
7082 | DUK_DDD(DUK_DDDPRINT("varmap: %!T, is_arguments_shadowed=%ld", | |
7083 | (duk_tval *) duk_get_tval(ctx, comp_ctx->curr_func.varmap_idx), | |
7084 | (long) comp_ctx->curr_func.is_arguments_shadowed)); | |
7085 | ||
7086 | DUK_ASSERT_TOP(ctx, entry_top); | |
7087 | return; | |
7088 | ||
7089 | error_outofregs: | |
11fdf7f2 | 7090 | DUK_ERROR_RANGE(thr, DUK_STR_REG_LIMIT); |
7c673cae FG |
7091 | DUK_UNREACHABLE(); |
7092 | return; | |
7093 | ||
7094 | error_argname: | |
11fdf7f2 | 7095 | DUK_ERROR_SYNTAX(thr, DUK_STR_INVALID_ARG_NAME); |
7c673cae FG |
7096 | DUK_UNREACHABLE(); |
7097 | return; | |
7098 | } | |
7099 | ||
7100 | /* | |
7101 | * Parse a function-body-like expression (FunctionBody or Program | |
7102 | * in E5 grammar) using a two-pass parse. The productions appear | |
7103 | * in the following contexts: | |
7104 | * | |
7105 | * - function expression | |
7106 | * - function statement | |
7107 | * - function declaration | |
7108 | * - getter in object literal | |
7109 | * - setter in object literal | |
7110 | * - global code | |
7111 | * - eval code | |
7112 | * - Function constructor body | |
7113 | * | |
7114 | * This function only parses the statement list of the body; the argument | |
7115 | * list and possible function name must be initialized by the caller. | |
7116 | * For instance, for Function constructor, the argument names are originally | |
7117 | * on the value stack. The parsing of statements ends either at an EOF or | |
7118 | * a closing brace; this is controlled by an input flag. | |
7119 | * | |
7120 | * Note that there are many differences affecting parsing and even code | |
7121 | * generation: | |
7122 | * | |
7123 | * - Global and eval code have an implicit return value generated | |
7124 | * by the last statement; function code does not | |
7125 | * | |
7126 | * - Global code, eval code, and Function constructor body end in | |
7127 | * an EOF, other bodies in a closing brace ('}') | |
7128 | * | |
7129 | * Upon entry, 'curr_tok' is ignored and the function will pull in the | |
7130 | * first token on its own. Upon exit, 'curr_tok' is the terminating | |
7131 | * token (EOF or closing brace). | |
7132 | */ | |
7133 | ||
7134 | DUK_LOCAL 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) { | |
7135 | duk_compiler_func *func; | |
7136 | duk_hthread *thr; | |
7137 | duk_context *ctx; | |
7138 | duk_reg_t reg_stmt_value = -1; | |
7139 | duk_lexer_point lex_pt; | |
7140 | duk_reg_t temp_first; | |
7141 | duk_small_int_t compile_round = 1; | |
7142 | ||
7143 | DUK_ASSERT(comp_ctx != NULL); | |
7144 | ||
7145 | thr = comp_ctx->thr; | |
7146 | ctx = (duk_context *) thr; | |
7147 | DUK_ASSERT(thr != NULL); | |
7148 | ||
7149 | func = &comp_ctx->curr_func; | |
7150 | DUK_ASSERT(func != NULL); | |
7151 | ||
7152 | DUK__RECURSION_INCREASE(comp_ctx, thr); | |
7153 | ||
7154 | duk_require_stack(ctx, DUK__FUNCTION_BODY_REQUIRE_SLOTS); | |
7155 | ||
7156 | /* | |
7157 | * Store lexer position for a later rewind | |
7158 | */ | |
7159 | ||
7160 | DUK_LEXER_GETPOINT(&comp_ctx->lex, &lex_pt); | |
7161 | ||
7162 | /* | |
7163 | * Program code (global and eval code) has an implicit return value | |
7164 | * from the last statement value (e.g. eval("1; 2+3;") returns 3). | |
7165 | * This is not the case with functions. If implicit statement return | |
7166 | * value is requested, all statements are coerced to a register | |
7167 | * allocated here, and used in the implicit return statement below. | |
7168 | */ | |
7169 | ||
7170 | /* XXX: this is pointless here because pass 1 is throw-away */ | |
7171 | if (implicit_return_value) { | |
7172 | reg_stmt_value = DUK__ALLOCTEMP(comp_ctx); | |
7173 | ||
7174 | /* If an implicit return value is needed by caller, it must be | |
7175 | * initialized to 'undefined' because we don't know whether any | |
7176 | * non-empty (where "empty" is a continuation type, and different | |
7177 | * from an empty statement) statements will be executed. | |
7178 | * | |
7179 | * However, since 1st pass is a throwaway one, no need to emit | |
7180 | * it here. | |
7181 | */ | |
7182 | #if 0 | |
7183 | duk__emit_extraop_bc(comp_ctx, | |
7184 | DUK_EXTRAOP_LDUNDEF, | |
7185 | 0); | |
7186 | #endif | |
7187 | } | |
7188 | ||
7189 | /* | |
7190 | * First pass. | |
7191 | * | |
7192 | * Gather variable/function declarations needed for second pass. | |
7193 | * Code generated is dummy and discarded. | |
7194 | */ | |
7195 | ||
7196 | func->in_directive_prologue = 1; | |
7197 | func->in_scanning = 1; | |
7198 | func->may_direct_eval = 0; | |
7199 | func->id_access_arguments = 0; | |
7200 | func->id_access_slow = 0; | |
7201 | func->reg_stmt_value = reg_stmt_value; | |
7202 | #if defined(DUK_USE_DEBUGGER_SUPPORT) | |
7203 | func->min_line = DUK_INT_MAX; | |
7204 | func->max_line = 0; | |
7205 | #endif | |
7206 | ||
7207 | /* duk__parse_stmts() expects curr_tok to be set; parse in "allow regexp literal" mode with current strictness */ | |
7208 | if (expect_token >= 0) { | |
7209 | /* Eating a left curly; regexp mode is allowed by left curly | |
7210 | * based on duk__token_lbp[] automatically. | |
7211 | */ | |
7212 | DUK_ASSERT(expect_token == DUK_TOK_LCURLY); | |
7213 | duk__update_lineinfo_currtoken(comp_ctx); | |
7214 | duk__advance_expect(comp_ctx, expect_token); | |
7215 | } else { | |
7216 | /* Need to set curr_token.t because lexing regexp mode depends on current | |
7217 | * token type. Zero value causes "allow regexp" mode. | |
7218 | */ | |
7219 | comp_ctx->curr_token.t = 0; | |
7220 | duk__advance(comp_ctx); | |
7221 | } | |
7222 | ||
7223 | DUK_DDD(DUK_DDDPRINT("begin 1st pass")); | |
7224 | duk__parse_stmts(comp_ctx, | |
7225 | 1, /* allow source elements */ | |
7226 | expect_eof); /* expect EOF instead of } */ | |
7227 | DUK_DDD(DUK_DDDPRINT("end 1st pass")); | |
7228 | ||
7229 | /* | |
7230 | * Second (and possibly third) pass. | |
7231 | * | |
7232 | * Generate actual code. In most cases the need for shuffle | |
7233 | * registers is detected during pass 1, but in some corner cases | |
7234 | * we'll only detect it during pass 2 and a third pass is then | |
7235 | * needed (see GH-115). | |
7236 | */ | |
7237 | ||
7238 | for (;;) { | |
7239 | duk_bool_t needs_shuffle_before = comp_ctx->curr_func.needs_shuffle; | |
7240 | compile_round++; | |
7241 | ||
7242 | /* | |
7243 | * Rewind lexer. | |
7244 | * | |
7245 | * duk__parse_stmts() expects curr_tok to be set; parse in "allow regexp | |
7246 | * literal" mode with current strictness. | |
7247 | * | |
7248 | * curr_token line number info should be initialized for pass 2 before | |
7249 | * generating prologue, to ensure prologue bytecode gets nice line numbers. | |
7250 | */ | |
7251 | ||
7252 | DUK_DDD(DUK_DDDPRINT("rewind lexer")); | |
7253 | DUK_LEXER_SETPOINT(&comp_ctx->lex, &lex_pt); | |
7254 | comp_ctx->curr_token.t = 0; /* this is needed for regexp mode */ | |
7255 | comp_ctx->curr_token.start_line = 0; /* needed for line number tracking (becomes prev_token.start_line) */ | |
7256 | duk__advance(comp_ctx); | |
7257 | ||
7258 | /* | |
7259 | * Reset function state and perform register allocation, which creates | |
7260 | * 'varmap' for second pass. Function prologue for variable declarations, | |
7261 | * binding value initializations etc is emitted as a by-product. | |
7262 | * | |
7263 | * Strict mode restrictions for duplicate and invalid argument | |
7264 | * names are checked here now that we know whether the function | |
7265 | * is actually strict. See: test-dev-strict-mode-boundary.js. | |
7266 | * | |
7267 | * Inner functions are compiled during pass 1 and are not reset. | |
7268 | */ | |
7269 | ||
7270 | duk__reset_func_for_pass2(comp_ctx); | |
7271 | func->in_directive_prologue = 1; | |
7272 | func->in_scanning = 0; | |
7273 | ||
7274 | /* must be able to emit code, alloc consts, etc. */ | |
7275 | ||
7276 | duk__init_varmap_and_prologue_for_pass2(comp_ctx, | |
7277 | (implicit_return_value ? ®_stmt_value : NULL)); | |
7278 | func->reg_stmt_value = reg_stmt_value; | |
7279 | ||
7280 | temp_first = DUK__GETTEMP(comp_ctx); | |
7281 | ||
7282 | func->temp_first = temp_first; | |
7283 | func->temp_next = temp_first; | |
7284 | func->stmt_next = 0; | |
7285 | func->label_next = 0; | |
7286 | ||
7287 | /* XXX: init or assert catch depth etc -- all values */ | |
7288 | func->id_access_arguments = 0; | |
7289 | func->id_access_slow = 0; | |
7290 | ||
7291 | /* | |
7292 | * Check function name validity now that we know strictness. | |
7293 | * This only applies to function declarations and expressions, | |
7294 | * not setter/getter name. | |
7295 | * | |
7296 | * See: test-dev-strict-mode-boundary.js | |
7297 | */ | |
7298 | ||
7299 | if (func->is_function && !func->is_setget && func->h_name != NULL) { | |
7300 | if (func->is_strict) { | |
7301 | if (duk__hstring_is_eval_or_arguments(comp_ctx, func->h_name)) { | |
7302 | DUK_DDD(DUK_DDDPRINT("func name is 'eval' or 'arguments' in strict mode")); | |
7303 | goto error_funcname; | |
7304 | } | |
7305 | if (DUK_HSTRING_HAS_STRICT_RESERVED_WORD(func->h_name)) { | |
7306 | DUK_DDD(DUK_DDDPRINT("func name is a reserved word in strict mode")); | |
7307 | goto error_funcname; | |
7308 | } | |
7309 | } else { | |
7310 | if (DUK_HSTRING_HAS_RESERVED_WORD(func->h_name) && | |
7311 | !DUK_HSTRING_HAS_STRICT_RESERVED_WORD(func->h_name)) { | |
7312 | DUK_DDD(DUK_DDDPRINT("func name is a reserved word in non-strict mode")); | |
7313 | goto error_funcname; | |
7314 | } | |
7315 | } | |
7316 | } | |
7317 | ||
7318 | /* | |
7319 | * Second pass parsing. | |
7320 | */ | |
7321 | ||
7322 | if (implicit_return_value) { | |
7323 | /* Default implicit return value. */ | |
7324 | duk__emit_extraop_bc(comp_ctx, | |
7325 | DUK_EXTRAOP_LDUNDEF, | |
7326 | 0); | |
7327 | } | |
7328 | ||
7329 | DUK_DDD(DUK_DDDPRINT("begin 2nd pass")); | |
7330 | duk__parse_stmts(comp_ctx, | |
7331 | 1, /* allow source elements */ | |
7332 | expect_eof); /* expect EOF instead of } */ | |
7333 | DUK_DDD(DUK_DDDPRINT("end 2nd pass")); | |
7334 | ||
7335 | duk__update_lineinfo_currtoken(comp_ctx); | |
7336 | ||
7337 | if (needs_shuffle_before == comp_ctx->curr_func.needs_shuffle) { | |
7338 | /* Shuffle decision not changed. */ | |
7339 | break; | |
7340 | } | |
7341 | if (compile_round >= 3) { | |
7342 | /* Should never happen but avoid infinite loop just in case. */ | |
7343 | DUK_D(DUK_DPRINT("more than 3 compile passes needed, should never happen")); | |
11fdf7f2 | 7344 | DUK_ERROR_INTERNAL_DEFMSG(thr); |
7c673cae FG |
7345 | } |
7346 | DUK_D(DUK_DPRINT("need additional round to compile function, round now %d", (int) compile_round)); | |
7347 | } | |
7348 | ||
7349 | /* | |
7350 | * Emit a final RETURN. | |
7351 | * | |
7352 | * It would be nice to avoid emitting an unnecessary "return" opcode | |
7353 | * if the current PC is not reachable. However, this cannot be reliably | |
7354 | * detected; even if the previous instruction is an unconditional jump, | |
7355 | * there may be a previous jump which jumps to current PC (which is the | |
7356 | * case for iteration and conditional statements, for instance). | |
7357 | */ | |
7358 | ||
7359 | /* XXX: request a "last statement is terminal" from duk__parse_stmt() and duk__parse_stmts(); | |
7360 | * we could avoid the last RETURN if we could ensure there is no way to get here | |
7361 | * (directly or via a jump) | |
7362 | */ | |
7363 | ||
11fdf7f2 | 7364 | DUK_ASSERT(comp_ctx->curr_func.catch_depth == 0); |
7c673cae FG |
7365 | if (reg_stmt_value >= 0) { |
7366 | duk__emit_a_b(comp_ctx, | |
7367 | DUK_OP_RETURN | DUK__EMIT_FLAG_NO_SHUFFLE_A, | |
11fdf7f2 | 7368 | (duk_regconst_t) DUK_BC_RETURN_FLAG_HAVE_RETVAL /*flags*/, |
7c673cae FG |
7369 | (duk_regconst_t) reg_stmt_value /*reg*/); |
7370 | } else { | |
7371 | duk__emit_a_b(comp_ctx, | |
7372 | DUK_OP_RETURN | DUK__EMIT_FLAG_NO_SHUFFLE_A, | |
11fdf7f2 | 7373 | (duk_regconst_t) 0 /*flags*/, |
7c673cae FG |
7374 | (duk_regconst_t) 0 /*reg(ignored)*/); |
7375 | } | |
7376 | ||
7377 | /* | |
7378 | * Peephole optimize JUMP chains. | |
7379 | */ | |
7380 | ||
7381 | duk__peephole_optimize_bytecode(comp_ctx); | |
7382 | ||
7383 | /* | |
7384 | * comp_ctx->curr_func is now ready to be converted into an actual | |
7385 | * function template. | |
7386 | */ | |
7387 | ||
7388 | DUK__RECURSION_DECREASE(comp_ctx, thr); | |
7389 | return; | |
7390 | ||
7391 | error_funcname: | |
11fdf7f2 | 7392 | DUK_ERROR_SYNTAX(thr, DUK_STR_INVALID_FUNC_NAME); |
7c673cae FG |
7393 | } |
7394 | ||
7395 | /* | |
7396 | * Parse a function-like expression: | |
7397 | * | |
7398 | * - function expression | |
7399 | * - function declaration | |
7400 | * - function statement (non-standard) | |
7401 | * - setter/getter | |
7402 | * | |
7403 | * Adds the function to comp_ctx->curr_func function table and returns the | |
7404 | * function number. | |
7405 | * | |
7406 | * On entry, curr_token points to: | |
7407 | * | |
7408 | * - the token after 'function' for function expression/declaration/statement | |
7409 | * - the token after 'set' or 'get' for setter/getter | |
7410 | */ | |
7411 | ||
7412 | /* Parse formals. */ | |
7413 | DUK_LOCAL void duk__parse_func_formals(duk_compiler_ctx *comp_ctx) { | |
7414 | duk_hthread *thr = comp_ctx->thr; | |
7415 | duk_context *ctx = (duk_context *) thr; | |
7416 | duk_bool_t first = 1; | |
7417 | duk_uarridx_t n; | |
7418 | ||
7419 | for (;;) { | |
7420 | if (comp_ctx->curr_token.t == DUK_TOK_RPAREN) { | |
7421 | break; | |
7422 | } | |
7423 | ||
7424 | if (first) { | |
7425 | /* no comma */ | |
7426 | first = 0; | |
7427 | } else { | |
7428 | duk__advance_expect(comp_ctx, DUK_TOK_COMMA); | |
7429 | } | |
7430 | ||
7431 | /* Note: when parsing a formal list in non-strict context, e.g. | |
7432 | * "implements" is parsed as an identifier. When the function is | |
7433 | * later detected to be strict, the argument list must be rechecked | |
7434 | * against a larger set of reserved words (that of strict mode). | |
7435 | * This is handled by duk__parse_func_body(). Here we recognize | |
7436 | * whatever tokens are considered reserved in current strictness | |
7437 | * (which is not always enough). | |
7438 | */ | |
7439 | ||
7440 | if (comp_ctx->curr_token.t != DUK_TOK_IDENTIFIER) { | |
11fdf7f2 | 7441 | DUK_ERROR_SYNTAX(thr, "expected identifier"); |
7c673cae FG |
7442 | } |
7443 | DUK_ASSERT(comp_ctx->curr_token.t == DUK_TOK_IDENTIFIER); | |
7444 | DUK_ASSERT(comp_ctx->curr_token.str1 != NULL); | |
7445 | DUK_DDD(DUK_DDDPRINT("formal argument: %!O", | |
7446 | (duk_heaphdr *) comp_ctx->curr_token.str1)); | |
7447 | ||
7448 | /* XXX: append primitive */ | |
7449 | duk_push_hstring(ctx, comp_ctx->curr_token.str1); | |
7450 | n = (duk_uarridx_t) duk_get_length(ctx, comp_ctx->curr_func.argnames_idx); | |
7451 | duk_put_prop_index(ctx, comp_ctx->curr_func.argnames_idx, n); | |
7452 | ||
7453 | duk__advance(comp_ctx); /* eat identifier */ | |
7454 | } | |
7455 | } | |
7456 | ||
7457 | /* Parse a function-like expression, assuming that 'comp_ctx->curr_func' is | |
7458 | * correctly set up. Assumes that curr_token is just after 'function' (or | |
7459 | * 'set'/'get' etc). | |
7460 | */ | |
7461 | DUK_LOCAL void duk__parse_func_like_raw(duk_compiler_ctx *comp_ctx, duk_bool_t is_decl, duk_bool_t is_setget) { | |
7462 | duk_hthread *thr = comp_ctx->thr; | |
7463 | duk_context *ctx = (duk_context *) thr; | |
7464 | ||
7465 | DUK_ASSERT(comp_ctx->curr_func.num_formals == 0); | |
7466 | DUK_ASSERT(comp_ctx->curr_func.is_function == 1); | |
7467 | DUK_ASSERT(comp_ctx->curr_func.is_eval == 0); | |
7468 | DUK_ASSERT(comp_ctx->curr_func.is_global == 0); | |
7469 | DUK_ASSERT(comp_ctx->curr_func.is_setget == is_setget); | |
7470 | DUK_ASSERT(comp_ctx->curr_func.is_decl == is_decl); | |
7471 | ||
7472 | duk__update_lineinfo_currtoken(comp_ctx); | |
7473 | ||
7474 | /* | |
7475 | * Function name (if any) | |
7476 | * | |
7477 | * We don't check for prohibited names here, because we don't | |
7478 | * yet know whether the function will be strict. Function body | |
7479 | * parsing handles this retroactively. | |
7480 | * | |
7481 | * For function expressions and declarations function name must | |
7482 | * be an Identifer (excludes reserved words). For setter/getter | |
7483 | * it is a PropertyName which allows reserved words and also | |
7484 | * strings and numbers (e.g. "{ get 1() { ... } }"). | |
7485 | */ | |
7486 | ||
7487 | if (is_setget) { | |
7488 | /* PropertyName -> IdentifierName | StringLiteral | NumericLiteral */ | |
7489 | if (comp_ctx->curr_token.t_nores == DUK_TOK_IDENTIFIER || | |
7490 | comp_ctx->curr_token.t == DUK_TOK_STRING) { | |
7491 | duk_push_hstring(ctx, comp_ctx->curr_token.str1); /* keep in valstack */ | |
7492 | } else if (comp_ctx->curr_token.t == DUK_TOK_NUMBER) { | |
7493 | duk_push_number(ctx, comp_ctx->curr_token.num); | |
7494 | duk_to_string(ctx, -1); | |
7495 | } else { | |
11fdf7f2 | 7496 | DUK_ERROR_SYNTAX(thr, DUK_STR_INVALID_GETSET_NAME); |
7c673cae FG |
7497 | } |
7498 | comp_ctx->curr_func.h_name = duk_get_hstring(ctx, -1); /* borrowed reference */ | |
7499 | DUK_ASSERT(comp_ctx->curr_func.h_name != NULL); | |
7500 | duk__advance(comp_ctx); | |
7501 | } else { | |
7502 | /* Function name is an Identifier (not IdentifierName), but we get | |
7503 | * the raw name (not recognizing keywords) here and perform the name | |
7504 | * checks only after pass 1. | |
7505 | */ | |
7506 | if (comp_ctx->curr_token.t_nores == DUK_TOK_IDENTIFIER) { | |
7507 | duk_push_hstring(ctx, comp_ctx->curr_token.str1); /* keep in valstack */ | |
7508 | comp_ctx->curr_func.h_name = duk_get_hstring(ctx, -1); /* borrowed reference */ | |
7509 | DUK_ASSERT(comp_ctx->curr_func.h_name != NULL); | |
7510 | duk__advance(comp_ctx); | |
7511 | } else { | |
7512 | /* valstack will be unbalanced, which is OK */ | |
7513 | DUK_ASSERT(!is_setget); | |
7514 | if (is_decl) { | |
11fdf7f2 | 7515 | DUK_ERROR_SYNTAX(thr, DUK_STR_FUNC_NAME_REQUIRED); |
7c673cae FG |
7516 | } |
7517 | } | |
7518 | } | |
7519 | ||
7520 | DUK_DDD(DUK_DDDPRINT("function name: %!O", | |
7521 | (duk_heaphdr *) comp_ctx->curr_func.h_name)); | |
7522 | ||
7523 | /* | |
7524 | * Formal argument list | |
7525 | * | |
7526 | * We don't check for prohibited names or for duplicate argument | |
7527 | * names here, becase we don't yet know whether the function will | |
7528 | * be strict. Function body parsing handles this retroactively. | |
7529 | */ | |
7530 | ||
7531 | duk__advance_expect(comp_ctx, DUK_TOK_LPAREN); | |
7532 | ||
7533 | duk__parse_func_formals(comp_ctx); | |
7534 | ||
7535 | DUK_ASSERT(comp_ctx->curr_token.t == DUK_TOK_RPAREN); | |
7536 | duk__advance(comp_ctx); | |
7537 | ||
7538 | /* | |
7539 | * Parse function body | |
7540 | */ | |
7541 | ||
7542 | duk__parse_func_body(comp_ctx, | |
7543 | 0, /* expect_eof */ | |
7544 | 0, /* implicit_return_value */ | |
7545 | DUK_TOK_LCURLY); /* expect_token */ | |
7546 | ||
7547 | /* | |
7548 | * Convert duk_compiler_func to a function template and add it | |
7549 | * to the parent function table. | |
7550 | */ | |
7551 | ||
7552 | duk__convert_to_func_template(comp_ctx, is_setget /*force_no_namebind*/); /* -> [ ... func ] */ | |
7553 | } | |
7554 | ||
7555 | /* Parse an inner function, adding the function template to the current function's | |
7556 | * function table. Return a function number to be used by the outer function. | |
7557 | * | |
7558 | * Avoiding O(depth^2) inner function parsing is handled here. On the first pass, | |
7559 | * compile and register the function normally into the 'funcs' array, also recording | |
7560 | * a lexer point (offset/line) to the closing brace of the function. On the second | |
7561 | * pass, skip the function and return the same 'fnum' as on the first pass by using | |
7562 | * a running counter. | |
7563 | * | |
7564 | * An unfortunate side effect of this is that when parsing the inner function, almost | |
7565 | * nothing is known of the outer function, i.e. the inner function's scope. We don't | |
7566 | * need that information at the moment, but it would allow some optimizations if it | |
7567 | * were used. | |
7568 | */ | |
7569 | DUK_LOCAL duk_int_t duk__parse_func_like_fnum(duk_compiler_ctx *comp_ctx, duk_bool_t is_decl, duk_bool_t is_setget) { | |
7570 | duk_hthread *thr = comp_ctx->thr; | |
7571 | duk_context *ctx = (duk_context *) thr; | |
7572 | duk_compiler_func old_func; | |
7573 | duk_idx_t entry_top; | |
7574 | duk_int_t fnum; | |
7575 | ||
7576 | /* | |
7577 | * On second pass, skip the function. | |
7578 | */ | |
7579 | ||
7580 | if (!comp_ctx->curr_func.in_scanning) { | |
7581 | duk_lexer_point lex_pt; | |
7582 | ||
7583 | fnum = comp_ctx->curr_func.fnum_next++; | |
7584 | duk_get_prop_index(ctx, comp_ctx->curr_func.funcs_idx, (duk_uarridx_t) (fnum * 3 + 1)); | |
7585 | lex_pt.offset = duk_to_int(ctx, -1); | |
7586 | duk_pop(ctx); | |
7587 | duk_get_prop_index(ctx, comp_ctx->curr_func.funcs_idx, (duk_uarridx_t) (fnum * 3 + 2)); | |
7588 | lex_pt.line = duk_to_int(ctx, -1); | |
7589 | duk_pop(ctx); | |
7590 | ||
7591 | DUK_DDD(DUK_DDDPRINT("second pass of an inner func, skip the function, reparse closing brace; lex offset=%ld, line=%ld", | |
7592 | (long) lex_pt.offset, (long) lex_pt.line)); | |
7593 | ||
7594 | DUK_LEXER_SETPOINT(&comp_ctx->lex, &lex_pt); | |
7595 | comp_ctx->curr_token.t = 0; /* this is needed for regexp mode */ | |
7596 | comp_ctx->curr_token.start_line = 0; /* needed for line number tracking (becomes prev_token.start_line) */ | |
7597 | duk__advance(comp_ctx); | |
7598 | duk__advance_expect(comp_ctx, DUK_TOK_RCURLY); | |
7599 | ||
7600 | return fnum; | |
7601 | } | |
7602 | ||
7603 | /* | |
7604 | * On first pass, perform actual parsing. Remember valstack top on entry | |
7605 | * to restore it later, and switch to using a new function in comp_ctx. | |
7606 | */ | |
7607 | ||
7608 | entry_top = duk_get_top(ctx); | |
7609 | DUK_DDD(DUK_DDDPRINT("before func: entry_top=%ld, curr_tok.start_offset=%ld", | |
7610 | (long) entry_top, (long) comp_ctx->curr_token.start_offset)); | |
7611 | ||
7612 | DUK_MEMCPY(&old_func, &comp_ctx->curr_func, sizeof(duk_compiler_func)); | |
7613 | ||
7614 | DUK_MEMZERO(&comp_ctx->curr_func, sizeof(duk_compiler_func)); | |
7615 | duk__init_func_valstack_slots(comp_ctx); | |
7616 | DUK_ASSERT(comp_ctx->curr_func.num_formals == 0); | |
7617 | ||
7618 | /* inherit initial strictness from parent */ | |
7619 | comp_ctx->curr_func.is_strict = old_func.is_strict; | |
7620 | ||
7621 | DUK_ASSERT(comp_ctx->curr_func.is_notail == 0); | |
7622 | comp_ctx->curr_func.is_function = 1; | |
7623 | DUK_ASSERT(comp_ctx->curr_func.is_eval == 0); | |
7624 | DUK_ASSERT(comp_ctx->curr_func.is_global == 0); | |
7625 | comp_ctx->curr_func.is_setget = is_setget; | |
7626 | comp_ctx->curr_func.is_decl = is_decl; | |
7627 | ||
7628 | /* | |
7629 | * Parse inner function | |
7630 | */ | |
7631 | ||
7632 | duk__parse_func_like_raw(comp_ctx, is_decl, is_setget); /* pushes function template */ | |
7633 | ||
7634 | /* prev_token.start_offset points to the closing brace here; when skipping | |
7635 | * we're going to reparse the closing brace to ensure semicolon insertion | |
7636 | * etc work as expected. | |
7637 | */ | |
7638 | DUK_DDD(DUK_DDDPRINT("after func: prev_tok.start_offset=%ld, curr_tok.start_offset=%ld", | |
7639 | (long) comp_ctx->prev_token.start_offset, (long) comp_ctx->curr_token.start_offset)); | |
7640 | DUK_ASSERT(comp_ctx->lex.input[comp_ctx->prev_token.start_offset] == (duk_uint8_t) DUK_ASC_RCURLY); | |
7641 | ||
7642 | /* XXX: append primitive */ | |
7643 | DUK_ASSERT(duk_get_length(ctx, old_func.funcs_idx) == (duk_size_t) (old_func.fnum_next * 3)); | |
7644 | fnum = old_func.fnum_next++; | |
7645 | ||
7646 | if (fnum > DUK__MAX_FUNCS) { | |
11fdf7f2 | 7647 | DUK_ERROR_RANGE(comp_ctx->thr, DUK_STR_FUNC_LIMIT); |
7c673cae FG |
7648 | } |
7649 | ||
7650 | /* array writes autoincrement length */ | |
7651 | (void) duk_put_prop_index(ctx, old_func.funcs_idx, (duk_uarridx_t) (fnum * 3)); | |
7652 | duk_push_size_t(ctx, comp_ctx->prev_token.start_offset); | |
7653 | (void) duk_put_prop_index(ctx, old_func.funcs_idx, (duk_uarridx_t) (fnum * 3 + 1)); | |
7654 | duk_push_int(ctx, comp_ctx->prev_token.start_line); | |
7655 | (void) duk_put_prop_index(ctx, old_func.funcs_idx, (duk_uarridx_t) (fnum * 3 + 2)); | |
7656 | ||
7657 | /* | |
7658 | * Cleanup: restore original function, restore valstack state. | |
7659 | */ | |
7660 | ||
7661 | DUK_MEMCPY((void *) &comp_ctx->curr_func, (void *) &old_func, sizeof(duk_compiler_func)); | |
7662 | duk_set_top(ctx, entry_top); | |
7663 | ||
7664 | DUK_ASSERT_TOP(ctx, entry_top); | |
7665 | ||
7666 | return fnum; | |
7667 | } | |
7668 | ||
7669 | /* | |
7670 | * Compile input string into an executable function template without | |
7671 | * arguments. | |
7672 | * | |
7673 | * The string is parsed as the "Program" production of Ecmascript E5. | |
7674 | * Compilation context can be either global code or eval code (see E5 | |
7675 | * Sections 14 and 15.1.2.1). | |
7676 | * | |
7677 | * Input stack: [ ... filename ] | |
7678 | * Output stack: [ ... func_template ] | |
7679 | */ | |
7680 | ||
7681 | /* XXX: source code property */ | |
7682 | ||
7683 | DUK_LOCAL duk_ret_t duk__js_compile_raw(duk_context *ctx) { | |
7684 | duk_hthread *thr = (duk_hthread *) ctx; | |
7685 | duk_hstring *h_filename; | |
7686 | duk__compiler_stkstate *comp_stk; | |
7687 | duk_compiler_ctx *comp_ctx; | |
7688 | duk_lexer_point *lex_pt; | |
7689 | duk_compiler_func *func; | |
7690 | duk_idx_t entry_top; | |
7691 | duk_bool_t is_strict; | |
7692 | duk_bool_t is_eval; | |
7693 | duk_bool_t is_funcexpr; | |
7694 | duk_small_uint_t flags; | |
7695 | ||
7696 | DUK_ASSERT(thr != NULL); | |
7697 | ||
7698 | /* | |
7699 | * Arguments check | |
7700 | */ | |
7701 | ||
7702 | entry_top = duk_get_top(ctx); | |
7703 | DUK_ASSERT(entry_top >= 2); | |
7704 | ||
7705 | comp_stk = (duk__compiler_stkstate *) duk_require_pointer(ctx, -1); | |
7706 | comp_ctx = &comp_stk->comp_ctx_alloc; | |
7707 | lex_pt = &comp_stk->lex_pt_alloc; | |
7708 | DUK_ASSERT(comp_ctx != NULL); | |
7709 | DUK_ASSERT(lex_pt != NULL); | |
7710 | ||
7711 | flags = comp_stk->flags; | |
7712 | is_eval = (flags & DUK_JS_COMPILE_FLAG_EVAL ? 1 : 0); | |
7713 | is_strict = (flags & DUK_JS_COMPILE_FLAG_STRICT ? 1 : 0); | |
7714 | is_funcexpr = (flags & DUK_JS_COMPILE_FLAG_FUNCEXPR ? 1 : 0); | |
7715 | ||
7716 | h_filename = duk_get_hstring(ctx, -2); /* may be undefined */ | |
7717 | ||
7718 | /* | |
7719 | * Init compiler and lexer contexts | |
7720 | */ | |
7721 | ||
7722 | func = &comp_ctx->curr_func; | |
7723 | #ifdef DUK_USE_EXPLICIT_NULL_INIT | |
7724 | comp_ctx->thr = NULL; | |
7725 | comp_ctx->h_filename = NULL; | |
7726 | comp_ctx->prev_token.str1 = NULL; | |
7727 | comp_ctx->prev_token.str2 = NULL; | |
7728 | comp_ctx->curr_token.str1 = NULL; | |
7729 | comp_ctx->curr_token.str2 = NULL; | |
7730 | #endif | |
7731 | ||
7732 | duk_require_stack(ctx, DUK__COMPILE_ENTRY_SLOTS); | |
7733 | ||
7734 | duk_push_dynamic_buffer(ctx, 0); /* entry_top + 0 */ | |
7735 | duk_push_undefined(ctx); /* entry_top + 1 */ | |
7736 | duk_push_undefined(ctx); /* entry_top + 2 */ | |
7737 | duk_push_undefined(ctx); /* entry_top + 3 */ | |
7738 | duk_push_undefined(ctx); /* entry_top + 4 */ | |
7739 | ||
7740 | comp_ctx->thr = thr; | |
7741 | comp_ctx->h_filename = h_filename; | |
7742 | comp_ctx->tok11_idx = entry_top + 1; | |
7743 | comp_ctx->tok12_idx = entry_top + 2; | |
7744 | comp_ctx->tok21_idx = entry_top + 3; | |
7745 | comp_ctx->tok22_idx = entry_top + 4; | |
7746 | comp_ctx->recursion_limit = DUK_USE_COMPILER_RECLIMIT; | |
7747 | ||
7748 | /* comp_ctx->lex has been pre-initialized by caller: it has been | |
7749 | * zeroed and input/input_length has been set. | |
7750 | */ | |
7751 | comp_ctx->lex.thr = thr; | |
7752 | /* comp_ctx->lex.input and comp_ctx->lex.input_length filled by caller */ | |
7753 | comp_ctx->lex.slot1_idx = comp_ctx->tok11_idx; | |
7754 | comp_ctx->lex.slot2_idx = comp_ctx->tok12_idx; | |
7755 | comp_ctx->lex.buf_idx = entry_top + 0; | |
7756 | comp_ctx->lex.buf = (duk_hbuffer_dynamic *) duk_get_hbuffer(ctx, entry_top + 0); | |
7757 | DUK_ASSERT(comp_ctx->lex.buf != NULL); | |
7758 | DUK_ASSERT(DUK_HBUFFER_HAS_DYNAMIC(comp_ctx->lex.buf) && !DUK_HBUFFER_HAS_EXTERNAL(comp_ctx->lex.buf)); | |
7759 | comp_ctx->lex.token_limit = DUK_COMPILER_TOKEN_LIMIT; | |
7760 | ||
7761 | lex_pt->offset = 0; | |
7762 | lex_pt->line = 1; | |
7763 | DUK_LEXER_SETPOINT(&comp_ctx->lex, lex_pt); /* fills window */ | |
7764 | comp_ctx->curr_token.start_line = 0; /* needed for line number tracking (becomes prev_token.start_line) */ | |
7765 | ||
7766 | /* | |
7767 | * Initialize function state for a zero-argument function | |
7768 | */ | |
7769 | ||
7770 | duk__init_func_valstack_slots(comp_ctx); | |
7771 | DUK_ASSERT(func->num_formals == 0); | |
7772 | ||
7773 | if (is_funcexpr) { | |
7774 | /* Name will be filled from function expression, not by caller. | |
7775 | * This case is used by Function constructor and duk_compile() | |
7776 | * API with the DUK_COMPILE_FUNCTION option. | |
7777 | */ | |
7778 | DUK_ASSERT(func->h_name == NULL); | |
7779 | } else { | |
7780 | duk_push_hstring_stridx(ctx, (is_eval ? DUK_STRIDX_EVAL : | |
7781 | DUK_STRIDX_GLOBAL)); | |
7782 | func->h_name = duk_get_hstring(ctx, -1); | |
7783 | } | |
7784 | ||
7785 | /* | |
7786 | * Parse a function body or a function-like expression, depending | |
7787 | * on flags. | |
7788 | */ | |
7789 | ||
7790 | func->is_strict = is_strict; | |
7791 | func->is_setget = 0; | |
7792 | func->is_decl = 0; | |
7793 | ||
7794 | if (is_funcexpr) { | |
7795 | func->is_function = 1; | |
7796 | func->is_eval = 0; | |
7797 | func->is_global = 0; | |
7798 | ||
7799 | duk__advance(comp_ctx); /* init 'curr_token' */ | |
7800 | duk__advance_expect(comp_ctx, DUK_TOK_FUNCTION); | |
7801 | (void) duk__parse_func_like_raw(comp_ctx, | |
7802 | 0, /* is_decl */ | |
7803 | 0); /* is_setget */ | |
7804 | } else { | |
7805 | func->is_function = 0; | |
7806 | func->is_eval = is_eval; | |
7807 | func->is_global = !is_eval; | |
7808 | ||
7809 | duk__parse_func_body(comp_ctx, | |
7810 | 1, /* expect_eof */ | |
7811 | 1, /* implicit_return_value */ | |
7812 | -1); /* expect_token */ | |
7813 | } | |
7814 | ||
7815 | /* | |
7816 | * Convert duk_compiler_func to a function template | |
7817 | */ | |
7818 | ||
7819 | duk__convert_to_func_template(comp_ctx, 0 /*force_no_namebind*/); | |
7820 | ||
7821 | /* | |
7822 | * Wrapping duk_safe_call() will mangle the stack, just return stack top | |
7823 | */ | |
7824 | ||
7825 | /* [ ... filename (temps) func ] */ | |
7826 | ||
7827 | return 1; | |
7828 | } | |
7829 | ||
7830 | DUK_INTERNAL void duk_js_compile(duk_hthread *thr, const duk_uint8_t *src_buffer, duk_size_t src_length, duk_small_uint_t flags) { | |
7831 | duk_context *ctx = (duk_context *) thr; | |
7832 | duk__compiler_stkstate comp_stk; | |
7833 | duk_compiler_ctx *prev_ctx; | |
7834 | duk_ret_t safe_rc; | |
7835 | ||
7836 | /* XXX: this illustrates that a C catchpoint implemented using duk_safe_call() | |
7837 | * is a bit heavy at the moment. The wrapper compiles to ~180 bytes on x64. | |
7838 | * Alternatives would be nice. | |
7839 | */ | |
7840 | ||
7841 | DUK_ASSERT(thr != NULL); | |
7842 | DUK_ASSERT(src_buffer != NULL); | |
7843 | ||
7844 | /* preinitialize lexer state partially */ | |
7845 | DUK_MEMZERO(&comp_stk, sizeof(comp_stk)); | |
7846 | comp_stk.flags = flags; | |
7847 | DUK_LEXER_INITCTX(&comp_stk.comp_ctx_alloc.lex); | |
7848 | comp_stk.comp_ctx_alloc.lex.input = src_buffer; | |
7849 | comp_stk.comp_ctx_alloc.lex.input_length = src_length; | |
7850 | ||
7851 | duk_push_pointer(ctx, (void *) &comp_stk); | |
7852 | ||
7853 | /* [ ... filename &comp_stk ] */ | |
7854 | ||
7855 | prev_ctx = thr->compile_ctx; | |
7856 | thr->compile_ctx = &comp_stk.comp_ctx_alloc; /* for duk_error_augment.c */ | |
7857 | safe_rc = duk_safe_call(ctx, duk__js_compile_raw, 2 /*nargs*/, 1 /*nret*/); | |
11fdf7f2 | 7858 | thr->compile_ctx = prev_ctx; /* must restore reliably before returning */ |
7c673cae FG |
7859 | |
7860 | if (safe_rc != DUK_EXEC_SUCCESS) { | |
7c673cae FG |
7861 | duk_throw(ctx); |
7862 | } | |
7863 | ||
7864 | /* [ ... template ] */ | |
7865 | } |