summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--compiler.c28
-rw-r--r--tests/03_bugs/03_switch_fallthrough_miscompilation16
2 files changed, 24 insertions, 20 deletions
diff --git a/compiler.c b/compiler.c
index 436f91d..5c31409 100644
--- a/compiler.c
+++ b/compiler.c
@@ -2292,7 +2292,7 @@ static void
uc_compiler_compile_switch(uc_compiler *compiler)
{
uc_chunk *chunk = uc_compiler_current_chunk(compiler);
- size_t i, first_jmp, skip_jmp, next_jmp, default_jmp = 0;
+ size_t i, first_jmp, skip_jmp, next_jmp, default_off = 0;
bool in_case = false;
uc_jmplist jmps = {};
uc_patchlist p = {};
@@ -2317,7 +2317,7 @@ uc_compiler_compile_switch(uc_compiler *compiler)
!uc_compiler_parse_check(compiler, TK_EOF)) {
/* handle `default:` */
if (uc_compiler_parse_match(compiler, TK_DEFAULT)) {
- if (default_jmp) {
+ if (default_off) {
uc_compiler_syntax_error(compiler, compiler->parser->prev.pos,
"more than one switch default case");
@@ -2326,9 +2326,8 @@ uc_compiler_compile_switch(uc_compiler *compiler)
uc_compiler_parse_consume(compiler, TK_COLON);
- /* jump over default case, can only be reached by fallthrough or
- * conditional jump after last failed case condition */
- default_jmp = uc_compiler_emit_jmp(compiler, 0, 0);
+ /* remember address of default branch */
+ default_off = chunk->count;
in_case = true;
}
@@ -2385,29 +2384,19 @@ uc_compiler_compile_switch(uc_compiler *compiler)
if (i + 2 < jmps.count)
uc_compiler_set_jmpaddr(compiler, next_jmp, jmps.entries[i + 2] + 5);
/* case was last in switch, jump to default */
- else if (default_jmp)
- uc_compiler_set_jmpaddr(compiler, next_jmp, default_jmp + 5);
+ else if (default_off)
+ uc_compiler_set_jmpaddr(compiler, next_jmp, default_off);
/* if no default, jump to end */
else
uc_compiler_set_jmpaddr(compiler, next_jmp, chunk->count);
}
- /* if we have a default case, set target for the skip jump */
- if (default_jmp) {
- /* if we have cases, jump to the first one */
- if (jmps.count)
- uc_compiler_set_jmpaddr(compiler, default_jmp, jmps.entries[0] + 5);
- /* ... otherwise turn jump into no-op */
- else
- uc_compiler_set_jmpaddr(compiler, default_jmp, default_jmp + 5);
- }
-
/* if we have cases, patch initial jump after the first case condition */
if (jmps.count)
uc_compiler_set_jmpaddr(compiler, first_jmp, jmps.entries[0] + 5);
/* ... otherwise jump into default */
- else if (default_jmp)
- uc_compiler_set_jmpaddr(compiler, first_jmp, default_jmp + 5);
+ else if (default_off)
+ uc_compiler_set_jmpaddr(compiler, first_jmp, default_off);
/* ... otherwise if no defualt, turn into no-op */
else
uc_compiler_set_jmpaddr(compiler, first_jmp, first_jmp + 5);
@@ -2417,7 +2406,6 @@ uc_compiler_compile_switch(uc_compiler *compiler)
uc_compiler_leave_scope(compiler);
uc_compiler_backpatch(compiler, chunk->count, 0);
-
}
static void
diff --git a/tests/03_bugs/03_switch_fallthrough_miscompilation b/tests/03_bugs/03_switch_fallthrough_miscompilation
new file mode 100644
index 0000000..3e6410e
--- /dev/null
+++ b/tests/03_bugs/03_switch_fallthrough_miscompilation
@@ -0,0 +1,16 @@
+When falling through from a matched switch case into the default case,
+the compiler incorrectly emitted bytecode that led to an endless loop.
+
+-- Expect stdout --
+1
+-- End --
+
+-- Testcase --
+{%
+ switch (1) {
+ case 1:
+ default:
+ print("1\n");
+ }
+%}
+-- End --