diff --git a/Makefile b/Makefile index d255638a..9a74914a 100644 --- a/Makefile +++ b/Makefile @@ -188,6 +188,7 @@ CCOPTS_FEATURES += -DDUK_OPT_SELF_TESTS #CCOPTS_FEATURES += -DDUK_OPT_NO_PC2LINE #CCOPTS_FEATURES += -DDUK_OPT_NO_VERBOSE_ERRORS #CCOPTS_FEATURES += -DDUK_OPT_GC_TORTURE +#CCOPTS_FEATURES += -DDUK_OPT_SHUFFLE_TORTURE #CCOPTS_FEATURES += -DDUK_OPT_NO_MS_RESIZE_STRINGTABLE CCOPTS_FEATURES += -DDUK_OPT_DEBUG_BUFSIZE=512 #CCOPTS_FEATURES += -DDUK_OPT_NO_STRICT_DECL diff --git a/RELEASES.rst b/RELEASES.rst index fe81e6dd..864c239e 100644 --- a/RELEASES.rst +++ b/RELEASES.rst @@ -843,6 +843,9 @@ Planned * Fix for-in statement shuffle bug which caused enumeration to fail in large functions which enable register shuffling (GH-132) +* Fix shuffle handling issue for PUTPROP opcode, discovered by shuffle + torture tests (GH-135) + * Add support for TI-Nspire (using Ndless, see GH-113) 2.0.0 (XXXX-XX-XX) diff --git a/doc/feature-options.rst b/doc/feature-options.rst index 86771311..97adc917 100644 --- a/doc/feature-options.rst +++ b/doc/feature-options.rst @@ -825,6 +825,13 @@ DUK_OPT_NO_ZERO_BUFFER_DATA By default Duktape zeroes data allocated for buffer values. Define this to disable the zeroing (perhaps for performance reasons). +DUK_OPT_SHUFFLE_TORTURE +----------------------- + +Development time option: force compiler to shuffle every possible opcode +to stress shuffle behavior which is otherwise difficult to test for +comprehensively. + Using DUK_OPT_HAVE_CUSTOM_H and duk_custom.h ============================================ diff --git a/doc/release-checklist.rst b/doc/release-checklist.rst index 9f78e8dd..2b3b429b 100644 --- a/doc/release-checklist.rst +++ b/doc/release-checklist.rst @@ -121,6 +121,12 @@ Checklist for ordinary releases - Run with assertions enabled at least on x86-64 +* Run testcases with torture options + + - DUK_OPT_GC_TORTURE + + - DUK_OPT_SHUFFLE_TORTURE + * Memory usage testing - Leaks are mostly detected by Valgrind, but bugs in valstack or object diff --git a/src/duk_features.h.in b/src/duk_features.h.in index b7f0be8b..aa51dfc6 100644 --- a/src/duk_features.h.in +++ b/src/duk_features.h.in @@ -2648,6 +2648,11 @@ typedef FILE duk_file; #define DUK_USE_ESBC_MAX_LINENUMBER 0x7fff0000L #define DUK_USE_ESBC_MAX_BYTES 0x7fff0000L +#undef DUK_USE_SHUFFLE_TORTURE +#if defined(DUK_OPT_SHUFFLE_TORTURE) +#define DUK_USE_SHUFFLE_TORTURE +#endif + /* * User panic handler, panic exit behavior for default panic handler */ diff --git a/src/duk_js_compiler.c b/src/duk_js_compiler.c index e4486597..591afd49 100644 --- a/src/duk_js_compiler.c +++ b/src/duk_js_compiler.c @@ -108,6 +108,7 @@ DUK_LOCAL_DECL void duk__emit_extraop_b(duk_compiler_ctx *comp_ctx, duk_small_ui DUK_LOCAL_DECL void duk__emit_extraop_bc(duk_compiler_ctx *comp_ctx, duk_small_uint_t extraop, duk_regconst_t bc); DUK_LOCAL_DECL void duk__emit_extraop_only(duk_compiler_ctx *comp_ctx, duk_small_uint_t extraop_flags); DUK_LOCAL_DECL void duk__emit_load_int32(duk_compiler_ctx *comp_ctx, duk_reg_t reg, duk_int32_t val); +DUK_LOCAL_DECL void duk__emit_load_int32_noshuffle(duk_compiler_ctx *comp_ctx, duk_reg_t reg, duk_int32_t val); DUK_LOCAL_DECL void duk__emit_jump(duk_compiler_ctx *comp_ctx, duk_int_t target_pc); DUK_LOCAL_DECL duk_int_t duk__emit_jump_empty(duk_compiler_ctx *comp_ctx); DUK_LOCAL_DECL void duk__insert_jump_entry(duk_compiler_ctx *comp_ctx, duk_int_t jump_pc); @@ -1128,9 +1129,9 @@ DUK_LOCAL void duk__emit_op_only(duk_compiler_ctx *comp_ctx, duk_small_uint_t op /* Important main primitive. */ DUK_LOCAL void duk__emit_a_b_c(duk_compiler_ctx *comp_ctx, duk_small_uint_t op_flags, duk_regconst_t a, duk_regconst_t b, duk_regconst_t c) { duk_instr_t ins = 0; - duk_int_t a_out = 0; - duk_int_t b_out = 0; - duk_int_t c_out = 0; + duk_int_t a_out = -1; + duk_int_t b_out = -1; + duk_int_t c_out = -1; duk_int_t tmp; DUK_DDD(DUK_DDDPRINT("emit: op_flags=%04lx, a=%ld, b=%ld, c=%ld", @@ -1154,7 +1155,11 @@ DUK_LOCAL void duk__emit_a_b_c(duk_compiler_ctx *comp_ctx, duk_small_uint_t op_f /* Slot A */ +#if defined(DUK_USE_SHUFFLE_TORTURE) + if (a <= DUK_BC_A_MAX && (op_flags & DUK__EMIT_FLAG_NO_SHUFFLE_A)) { +#else if (a <= DUK_BC_A_MAX) { +#endif ; } else if (op_flags & DUK__EMIT_FLAG_NO_SHUFFLE_A) { DUK_D(DUK_DPRINT("out of regs: 'a' (reg) needs shuffling but shuffle prohibited, a: %ld", (long) a)); @@ -1171,7 +1176,7 @@ DUK_LOCAL void duk__emit_a_b_c(duk_compiler_ctx *comp_ctx, duk_small_uint_t op_f * is expressed indirectly, but there is no output shuffling. */ DUK_ASSERT((op_flags & DUK__EMIT_FLAG_A_IS_SOURCE) == 0); - duk__emit_load_int32(comp_ctx, tmp, a); + duk__emit_load_int32_noshuffle(comp_ctx, tmp, a); DUK_ASSERT(DUK_OP_CSVARI == DUK_OP_CSVAR + 1); DUK_ASSERT(DUK_OP_CSREGI == DUK_OP_CSREG + 1); DUK_ASSERT(DUK_OP_CSPROPI == DUK_OP_CSPROP + 1); @@ -1195,7 +1200,11 @@ DUK_LOCAL void duk__emit_a_b_c(duk_compiler_ctx *comp_ctx, duk_small_uint_t op_f DUK_ASSERT((op_flags & 0xff) != DUK_OP_CALL); DUK_ASSERT((op_flags & 0xff) != DUK_OP_NEW); b = b & ~DUK__CONST_MARKER; +#if defined(DUK_USE_SHUFFLE_TORTURE) + if (0) { +#else if (b <= 0xff) { +#endif ins |= DUK_ENC_OP_A_B_C(0, 0, 0x100, 0); /* const flag for B */ } else if (b <= DUK_BC_BC_MAX) { comp_ctx->curr_func.needs_shuffle = 1; @@ -1207,7 +1216,11 @@ DUK_LOCAL void duk__emit_a_b_c(duk_compiler_ctx *comp_ctx, duk_small_uint_t op_f goto error_outofregs; } } else { +#if defined(DUK_USE_SHUFFLE_TORTURE) + if (b <= 0xff && (op_flags & DUK__EMIT_FLAG_NO_SHUFFLE_B)) { +#else if (b <= 0xff) { +#endif ; } else if (op_flags & DUK__EMIT_FLAG_NO_SHUFFLE_B) { if (b > DUK_BC_B_MAX) { @@ -1231,7 +1244,7 @@ DUK_LOCAL void duk__emit_a_b_c(duk_compiler_ctx *comp_ctx, duk_small_uint_t op_f * an indirect version of the opcode is used. */ DUK_ASSERT((op_flags & DUK__EMIT_FLAG_B_IS_TARGET) == 0); - duk__emit_load_int32(comp_ctx, tmp, b); + duk__emit_load_int32_noshuffle(comp_ctx, tmp, b); DUK_ASSERT(DUK_OP_CALLI == DUK_OP_CALL + 1); DUK_ASSERT(DUK_OP_NEWI == DUK_OP_NEW + 1); DUK_ASSERT(DUK_OP_MPUTOBJI == DUK_OP_MPUTOBJ + 1); @@ -1254,7 +1267,11 @@ DUK_LOCAL void duk__emit_a_b_c(duk_compiler_ctx *comp_ctx, duk_small_uint_t op_f DUK_ASSERT((op_flags & DUK__EMIT_FLAG_NO_SHUFFLE_C) == 0); DUK_ASSERT((op_flags & DUK__EMIT_FLAG_C_IS_TARGET) == 0); c = c & ~DUK__CONST_MARKER; +#if defined(DUK_USE_SHUFFLE_TORTURE) + if (0) { +#else if (c <= 0xff) { +#endif ins |= DUK_ENC_OP_A_B_C(0, 0, 0, 0x100); /* const flag for C */ } else if (c <= DUK_BC_BC_MAX) { comp_ctx->curr_func.needs_shuffle = 1; @@ -1266,7 +1283,11 @@ DUK_LOCAL void duk__emit_a_b_c(duk_compiler_ctx *comp_ctx, duk_small_uint_t op_f goto error_outofregs; } } else { +#if defined(DUK_USE_SHUFFLE_TORTURE) + if (c <= 0xff && (op_flags & DUK__EMIT_FLAG_NO_SHUFFLE_C)) { +#else if (c <= 0xff) { +#endif ; } else if (op_flags & DUK__EMIT_FLAG_NO_SHUFFLE_C) { if (c > DUK_BC_C_MAX) { @@ -1289,7 +1310,7 @@ DUK_LOCAL void duk__emit_a_b_c(duk_compiler_ctx *comp_ctx, duk_small_uint_t op_f * normally. Use an indirect variant instead. */ DUK_ASSERT((op_flags & DUK__EMIT_FLAG_C_IS_TARGET) == 0); - duk__emit_load_int32(comp_ctx, tmp, c); + duk__emit_load_int32_noshuffle(comp_ctx, tmp, c); DUK_ASSERT(DUK_EXTRAOP_INITGETI == DUK_EXTRAOP_INITGET + 1); DUK_ASSERT(DUK_EXTRAOP_INITSETI == DUK_EXTRAOP_INITSET + 1); a++; /* indirect opcode follows direct */ @@ -1327,21 +1348,23 @@ DUK_LOCAL void duk__emit_a_b_c(duk_compiler_ctx *comp_ctx, duk_small_uint_t op_f } /* Output shuffling: only one output register is realistically possible. - * Zero is OK to check against: if the target register was zero, it is - * never shuffled. + * + * (Zero would normally be an OK marker value: if the target register + * was zero, it would never be shuffled. But with DUK_USE_SHUFFLE_TORTURE + * this is no longer true, so use -1 as a marker instead.) */ - if (a_out != 0) { - DUK_ASSERT(b_out == 0); - DUK_ASSERT(c_out == 0); + if (a_out >= 0) { + DUK_ASSERT(b_out < 0); + DUK_ASSERT(c_out < 0); duk__emit(comp_ctx, DUK_ENC_OP_A_BC(DUK_OP_STREG, a, a_out)); - } else if (b_out != 0) { - DUK_ASSERT(a_out == 0); - DUK_ASSERT(c_out == 0); + } else if (b_out >= 0) { + DUK_ASSERT(a_out < 0); + DUK_ASSERT(c_out < 0); duk__emit(comp_ctx, DUK_ENC_OP_A_BC(DUK_OP_STREG, b, b_out)); - } else if (c_out != 0) { - DUK_ASSERT(b_out == 0); - DUK_ASSERT(c_out == 0); + } else if (c_out >= 0) { + DUK_ASSERT(b_out < 0); + DUK_ASSERT(c_out < 0); duk__emit(comp_ctx, DUK_ENC_OP_A_BC(DUK_OP_STREG, c, c_out)); } @@ -1352,12 +1375,12 @@ DUK_LOCAL void duk__emit_a_b_c(duk_compiler_ctx *comp_ctx, duk_small_uint_t op_f } DUK_LOCAL void duk__emit_a_b(duk_compiler_ctx *comp_ctx, duk_small_uint_t op_flags, duk_regconst_t a, duk_regconst_t b) { - duk__emit_a_b_c(comp_ctx, op_flags, a, b, 0); + duk__emit_a_b_c(comp_ctx, op_flags | DUK__EMIT_FLAG_NO_SHUFFLE_C, a, b, 0); } #if 0 /* unused */ DUK_LOCAL void duk__emit_a(duk_compiler_ctx *comp_ctx, int op_flags, int a) { - duk__emit_a_b_c(comp_ctx, op_flags, a, 0, 0); + duk__emit_a_b_c(comp_ctx, op_flags | DUK__EMIT_FLAG_NO_SHUFFLE_B | DUK__EMIT_FLAG_NO_SHUFFLE_C, a, 0, 0); } #endif @@ -1381,7 +1404,11 @@ DUK_LOCAL void duk__emit_a_bc(duk_compiler_ctx *comp_ctx, duk_small_uint_t op_fl goto error_outofregs; } +#if defined(DUK_USE_SHUFFLE_TORTURE) + if (a <= DUK_BC_A_MAX && (op_flags & DUK__EMIT_FLAG_NO_SHUFFLE_A)) { +#else if (a <= DUK_BC_A_MAX) { +#endif ins = DUK_ENC_OP_A_BC(op_flags & 0xff, a, bc); duk__emit(comp_ctx, ins); } else if (op_flags & DUK__EMIT_FLAG_NO_SHUFFLE_A) { @@ -1435,9 +1462,11 @@ DUK_LOCAL void duk__emit_abc(duk_compiler_ctx *comp_ctx, duk_small_uint_t op, du DUK_LOCAL void duk__emit_extraop_b_c(duk_compiler_ctx *comp_ctx, duk_small_uint_t extraop_flags, duk_regconst_t b, duk_regconst_t c) { DUK_ASSERT_DISABLE((extraop_flags & 0xff) >= DUK_BC_EXTRAOP_MIN); /* unsigned */ DUK_ASSERT((extraop_flags & 0xff) <= DUK_BC_EXTRAOP_MAX); - /* Setting "no shuffle A" would be prudent but not necessary, assert covers it. */ + /* Setting "no shuffle A" is covered by the assert, but it's needed + * with DUK_USE_SHUFFLE_TORTURE. + */ duk__emit_a_b_c(comp_ctx, - DUK_OP_EXTRA | (extraop_flags & ~0xff), /* transfer flags */ + DUK_OP_EXTRA | DUK__EMIT_FLAG_NO_SHUFFLE_A | (extraop_flags & ~0xff), /* transfer flags */ extraop_flags & 0xff, b, c); @@ -1446,9 +1475,11 @@ DUK_LOCAL void duk__emit_extraop_b_c(duk_compiler_ctx *comp_ctx, duk_small_uint_ DUK_LOCAL void duk__emit_extraop_b(duk_compiler_ctx *comp_ctx, duk_small_uint_t extraop_flags, duk_regconst_t b) { DUK_ASSERT_DISABLE((extraop_flags & 0xff) >= DUK_BC_EXTRAOP_MIN); /* unsigned */ DUK_ASSERT((extraop_flags & 0xff) <= DUK_BC_EXTRAOP_MAX); - /* Setting "no shuffle A" would be prudent but not necessary, assert covers it. */ + /* Setting "no shuffle A" is covered by the assert, but it's needed + * with DUK_USE_SHUFFLE_TORTURE. + */ duk__emit_a_b_c(comp_ctx, - DUK_OP_EXTRA | (extraop_flags & ~0xff), /* transfer flags */ + DUK_OP_EXTRA | DUK__EMIT_FLAG_NO_SHUFFLE_A | (extraop_flags & ~0xff), /* transfer flags */ extraop_flags & 0xff, b, 0); @@ -1457,9 +1488,11 @@ DUK_LOCAL void duk__emit_extraop_b(duk_compiler_ctx *comp_ctx, duk_small_uint_t DUK_LOCAL void duk__emit_extraop_bc(duk_compiler_ctx *comp_ctx, duk_small_uint_t extraop, duk_regconst_t bc) { DUK_ASSERT_DISABLE(extraop >= DUK_BC_EXTRAOP_MIN); /* unsigned */ DUK_ASSERT(extraop <= DUK_BC_EXTRAOP_MAX); - /* Setting "no shuffle A" would be prudent but not necessary, assert covers it. */ + /* Setting "no shuffle A" is covered by the assert, but it's needed + * with DUK_USE_SHUFFLE_TORTURE. + */ duk__emit_a_bc(comp_ctx, - DUK_OP_EXTRA, + DUK_OP_EXTRA | DUK__EMIT_FLAG_NO_SHUFFLE_A, extraop, bc); } @@ -1467,15 +1500,18 @@ DUK_LOCAL void duk__emit_extraop_bc(duk_compiler_ctx *comp_ctx, duk_small_uint_t DUK_LOCAL void duk__emit_extraop_only(duk_compiler_ctx *comp_ctx, duk_small_uint_t extraop_flags) { DUK_ASSERT_DISABLE((extraop_flags & 0xff) >= DUK_BC_EXTRAOP_MIN); /* unsigned */ DUK_ASSERT((extraop_flags & 0xff) <= DUK_BC_EXTRAOP_MAX); - /* Setting "no shuffle A" would be prudent but not necessary, assert covers it. */ + /* Setting "no shuffle A" is covered by the assert, but it's needed + * with DUK_USE_SHUFFLE_TORTURE. + */ duk__emit_a_b_c(comp_ctx, - DUK_OP_EXTRA | (extraop_flags & ~0xff), /* transfer flags */ + DUK_OP_EXTRA | DUK__EMIT_FLAG_NO_SHUFFLE_A | DUK__EMIT_FLAG_NO_SHUFFLE_B | + DUK__EMIT_FLAG_NO_SHUFFLE_C | (extraop_flags & ~0xff), /* transfer flags */ extraop_flags & 0xff, 0, 0); } -DUK_LOCAL void duk__emit_load_int32(duk_compiler_ctx *comp_ctx, duk_reg_t reg, duk_int32_t val) { +DUK_LOCAL void duk__emit_load_int32_raw(duk_compiler_ctx *comp_ctx, duk_reg_t reg, duk_int32_t val, duk_small_uint_t op_flags) { /* XXX: Shuffling support could be implemented here so that LDINT+LDINTX * would only shuffle once (instead of twice). The current code works * though, and has a smaller compiler footprint. @@ -1484,18 +1520,39 @@ DUK_LOCAL void duk__emit_load_int32(duk_compiler_ctx *comp_ctx, duk_reg_t reg, d if ((val >= (duk_int32_t) DUK_BC_BC_MIN - (duk_int32_t) DUK_BC_LDINT_BIAS) && (val <= (duk_int32_t) DUK_BC_BC_MAX - (duk_int32_t) DUK_BC_LDINT_BIAS)) { DUK_DDD(DUK_DDDPRINT("emit LDINT to reg %ld for %ld", (long) reg, (long) val)); - duk__emit_a_bc(comp_ctx, DUK_OP_LDINT, reg, (duk_regconst_t) (val + (duk_int32_t) DUK_BC_LDINT_BIAS)); + duk__emit_a_bc(comp_ctx, DUK_OP_LDINT | op_flags, reg, (duk_regconst_t) (val + (duk_int32_t) DUK_BC_LDINT_BIAS)); } else { duk_int32_t hi = val >> DUK_BC_LDINTX_SHIFT; duk_int32_t lo = val & ((((duk_int32_t) 1) << DUK_BC_LDINTX_SHIFT) - 1); DUK_ASSERT(lo >= 0); DUK_DDD(DUK_DDDPRINT("emit LDINT+LDINTX to reg %ld for %ld -> hi %ld, lo %ld", (long) reg, (long) val, (long) hi, (long) lo)); - duk__emit_a_bc(comp_ctx, DUK_OP_LDINT, reg, (duk_regconst_t) (hi + (duk_int32_t) DUK_BC_LDINT_BIAS)); - duk__emit_a_bc(comp_ctx, DUK_OP_LDINTX, reg, (duk_regconst_t) lo); + duk__emit_a_bc(comp_ctx, DUK_OP_LDINT | op_flags, reg, (duk_regconst_t) (hi + (duk_int32_t) DUK_BC_LDINT_BIAS)); + duk__emit_a_bc(comp_ctx, DUK_OP_LDINTX | op_flags, reg, (duk_regconst_t) lo); } } +DUK_LOCAL void duk__emit_load_int32(duk_compiler_ctx *comp_ctx, duk_reg_t reg, duk_int32_t val) { + duk__emit_load_int32_raw(comp_ctx, reg, val, 0 /*op_flags*/); +} + +#if defined(DUK_USE_SHUFFLE_TORTURE) +/* Used by duk__emit*() calls so that we don't shuffle the loadints that + * are needed to handle indirect opcodes. + */ +DUK_LOCAL void duk__emit_load_int32_noshuffle(duk_compiler_ctx *comp_ctx, duk_reg_t reg, duk_int32_t val) { + duk__emit_load_int32_raw(comp_ctx, reg, val, DUK__EMIT_FLAG_NO_SHUFFLE_A /*op_flags*/); +} +#else +DUK_LOCAL void duk__emit_load_int32_noshuffle(duk_compiler_ctx *comp_ctx, duk_reg_t reg, duk_int32_t val) { + /* When torture not enabled, can just use the same helper because + * 'reg' won't get spilled. + */ + DUK_ASSERT(reg <= DUK_BC_A_MAX); + duk__emit_load_int32(comp_ctx, reg, val); +} +#endif + DUK_LOCAL void duk__emit_jump(duk_compiler_ctx *comp_ctx, duk_int_t target_pc) { duk_hbuffer_dynamic *h; duk_int_t curr_pc; @@ -1602,11 +1659,19 @@ DUK_LOCAL void duk__patch_trycatch(duk_compiler_ctx *comp_ctx, duk_int_t trycatc } DUK_LOCAL void duk__emit_if_false_skip(duk_compiler_ctx *comp_ctx, duk_regconst_t regconst) { - duk__emit_a_b_c(comp_ctx, DUK_OP_IF, 0 /*false*/, regconst, 0); + duk__emit_a_b_c(comp_ctx, + DUK_OP_IF | DUK__EMIT_FLAG_NO_SHUFFLE_A | DUK__EMIT_FLAG_NO_SHUFFLE_C, + 0 /*false*/, + regconst, + 0 /*unused*/); } DUK_LOCAL void duk__emit_if_true_skip(duk_compiler_ctx *comp_ctx, duk_regconst_t regconst) { - duk__emit_a_b_c(comp_ctx, DUK_OP_IF, 1 /*true*/, regconst, 0); + duk__emit_a_b_c(comp_ctx, + DUK_OP_IF | DUK__EMIT_FLAG_NO_SHUFFLE_A | DUK__EMIT_FLAG_NO_SHUFFLE_C, + 1 /*true*/, + regconst, + 0 /*unused*/); } DUK_LOCAL void duk__emit_invalid(duk_compiler_ctx *comp_ctx) { @@ -3581,7 +3646,7 @@ DUK_LOCAL void duk__expr_nud(duk_compiler_ctx *comp_ctx, duk_ivalue *res) { (duk_regconst_t) reg_res, (duk_regconst_t) reg_res); duk__emit_a_b_c(comp_ctx, - DUK_OP_PUTPROP, + DUK_OP_PUTPROP | DUK__EMIT_FLAG_A_IS_SOURCE, (duk_regconst_t) reg_obj, rc_key, (duk_regconst_t) reg_res); @@ -4119,7 +4184,7 @@ DUK_LOCAL void duk__expr_led(duk_compiler_ctx *comp_ctx, duk_ivalue *left, duk_i duk__ivalue_toforcedreg(comp_ctx, left, reg_temp); duk__emit_a_b(comp_ctx, - DUK_OP_IF, + DUK_OP_IF | DUK__EMIT_FLAG_NO_SHUFFLE_A, (duk_regconst_t) args_truthval, (duk_regconst_t) reg_temp); /* skip jump conditionally */ pc_jump = duk__emit_jump_empty(comp_ctx); @@ -4285,7 +4350,7 @@ DUK_LOCAL void duk__expr_led(duk_compiler_ctx *comp_ctx, duk_ivalue *left, duk_i } duk__emit_a_b_c(comp_ctx, - DUK_OP_PUTPROP, + DUK_OP_PUTPROP | DUK__EMIT_FLAG_A_IS_SOURCE, (duk_regconst_t) reg_obj, rc_key, rc_res); @@ -4414,7 +4479,7 @@ DUK_LOCAL void duk__expr_led(duk_compiler_ctx *comp_ctx, duk_ivalue *left, duk_i (duk_regconst_t) reg_temp, (duk_regconst_t) reg_res); duk__emit_a_b_c(comp_ctx, - DUK_OP_PUTPROP, + DUK_OP_PUTPROP | DUK__EMIT_FLAG_A_IS_SOURCE, (duk_regconst_t) reg_obj, rc_key, (duk_regconst_t) reg_temp); @@ -4926,7 +4991,7 @@ DUK_LOCAL void duk__parse_for_stmt(duk_compiler_ctx *comp_ctx, duk_ivalue *res, reg_obj = duk__ispec_toregconst_raw(comp_ctx, &res->x1, -1 /*forced_reg*/, 0 /*flags*/); /* don't allow const */ rc_key = duk__ispec_toregconst_raw(comp_ctx, &res->x2, -1 /*forced_reg*/, DUK__IVAL_FLAG_ALLOW_CONST /*flags*/); duk__emit_a_b_c(comp_ctx, - DUK_OP_PUTPROP, + DUK_OP_PUTPROP | DUK__EMIT_FLAG_A_IS_SOURCE, (duk_regconst_t) reg_obj, rc_key, (duk_regconst_t) (reg_temps + 0)); @@ -5608,7 +5673,7 @@ DUK_LOCAL void duk__parse_return_stmt(duk_compiler_ctx *comp_ctx, duk_ivalue *re #endif duk__emit_a_b(comp_ctx, - DUK_OP_RETURN, + DUK_OP_RETURN | DUK__EMIT_FLAG_NO_SHUFFLE_A, (duk_regconst_t) ret_flags /*flags*/, rc_val /*reg*/); } @@ -6695,7 +6760,7 @@ DUK_LOCAL void duk__init_varmap_and_prologue_for_pass2(duk_compiler_ctx *comp_ct } duk__emit_a_b_c(comp_ctx, - DUK_OP_DECLVAR, + DUK_OP_DECLVAR | DUK__EMIT_FLAG_NO_SHUFFLE_A, (duk_regconst_t) declvar_flags /*flags*/, rc_name /*name*/, (duk_regconst_t) reg_temp /*value*/); @@ -6780,7 +6845,7 @@ DUK_LOCAL void duk__init_varmap_and_prologue_for_pass2(duk_compiler_ctx *comp_ct } duk__emit_a_b_c(comp_ctx, - DUK_OP_DECLVAR, + DUK_OP_DECLVAR | DUK__EMIT_FLAG_NO_SHUFFLE_A, (duk_regconst_t) declvar_flags /*flags*/, rc_name /*name*/, (duk_regconst_t) 0 /*value*/); @@ -7073,12 +7138,12 @@ DUK_LOCAL void duk__parse_func_body(duk_compiler_ctx *comp_ctx, duk_bool_t expec DUK_ASSERT(comp_ctx->curr_func.catch_depth == 0); /* fast returns are always OK here */ if (reg_stmt_value >= 0) { duk__emit_a_b(comp_ctx, - DUK_OP_RETURN, + DUK_OP_RETURN | DUK__EMIT_FLAG_NO_SHUFFLE_A, (duk_regconst_t) (DUK_BC_RETURN_FLAG_HAVE_RETVAL | DUK_BC_RETURN_FLAG_FAST) /*flags*/, (duk_regconst_t) reg_stmt_value /*reg*/); } else { duk__emit_a_b(comp_ctx, - DUK_OP_RETURN, + DUK_OP_RETURN | DUK__EMIT_FLAG_NO_SHUFFLE_A, (duk_regconst_t) DUK_BC_RETURN_FLAG_FAST /*flags*/, (duk_regconst_t) 0 /*reg(ignored)*/); } diff --git a/src/duk_js_executor.c b/src/duk_js_executor.c index 4910f425..5ac5ef9d 100644 --- a/src/duk_js_executor.c +++ b/src/duk_js_executor.c @@ -2063,14 +2063,31 @@ DUK_INTERNAL void duk_js_execute_bytecode(duk_hthread *exec_thr) { DUK_ASSERT(bcode + act->pc >= DUK_HCOMPILEDFUNCTION_GET_CODE_BASE(thr->heap, fun)); DUK_ASSERT(bcode + act->pc < DUK_HCOMPILEDFUNCTION_GET_CODE_END(thr->heap, fun)); - DUK_DDD(DUK_DDDPRINT("executing bytecode: pc=%ld ins=0x%08lx, op=%ld, valstack_top=%ld/%ld --> %!I", + DUK_DDD(DUK_DDDPRINT("executing bytecode: pc=%ld ins=0x%08lx, op=%ld, valstack_top=%ld/%ld, nregs=%ld --> %!I", (long) act->pc, (unsigned long) bcode[act->pc], (long) DUK_DEC_OP(bcode[act->pc]), (long) (thr->valstack_top - thr->valstack), (long) (thr->valstack_end - thr->valstack), + (long) (fun ? fun->nregs : -1), (duk_instr_t) bcode[act->pc])); +#if defined(DUK_USE_ASSERTIONS) + /* Quite heavy assert: check that valstack is in correctly + * initialized state. Improper shuffle instructions can + * write beyond valstack_end so this check catches them in + * the act. + */ + { + duk_tval *tv; + tv = thr->valstack_top; + while (tv != thr->valstack_end) { + DUK_ASSERT(DUK_TVAL_IS_UNDEFINED_UNUSED(tv)); + tv++; + } + } +#endif + ins = bcode[act->pc++]; /* Typing: use duk_small_(u)int_fast_t when decoding small diff --git a/util/matrix_compile.py b/util/matrix_compile.py index 6dbc38b1..868729eb 100644 --- a/util/matrix_compile.py +++ b/util/matrix_compile.py @@ -278,12 +278,13 @@ def create_matrix(fn_duk): '-Os' ]) - # Feature options in suitable chunks that can be subsetted arbitrarilt. + # Feature options in suitable chunks that can be subsetted arbitrarily. duktape_options = Subset([ Select([ '-DDUK_OPT_NO_REFERENCE_COUNTING', '-DDUK_OPT_NO_MARK_AND_SWEEP', '-DDUK_OPT_GC_TORTURE' ]), + '-DDUK_OPT_SHUFFLE_TORTURE', '-DDUK_OPT_NO_VOLUNTARY_GC', '-DDUK_OPT_SEGFAULT_ON_PANIC', '-DDUK_OPT_DPRINT_COLORS',