summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--compiler.c10
-rw-r--r--compiler.h2
-rw-r--r--tests/custom/03_bugs/22_compile_break_continue_scoping59
3 files changed, 65 insertions, 6 deletions
diff --git a/compiler.c b/compiler.c
index ee6b073..f8d63b6 100644
--- a/compiler.c
+++ b/compiler.c
@@ -2031,8 +2031,8 @@ static void
uc_compiler_compile_while(uc_compiler *compiler)
{
uc_chunk *chunk = uc_compiler_current_chunk(compiler);
+ uc_patchlist p = { .depth = compiler->scope_depth };
size_t cond_off, jmpz_off, end_off;
- uc_patchlist p = { 0 };
p.parent = compiler->patchlist;
compiler->patchlist = &p;
@@ -2079,8 +2079,8 @@ static void
uc_compiler_compile_for_in(uc_compiler *compiler, bool local, uc_token *kvar, uc_token *vvar)
{
uc_chunk *chunk = uc_compiler_current_chunk(compiler);
+ uc_patchlist p = { .depth = compiler->scope_depth + 1 };
size_t skip_jmp, test_jmp, key_slot, val_slot;
- uc_patchlist p = { 0 };
p.parent = compiler->patchlist;
compiler->patchlist = &p;
@@ -2190,7 +2190,7 @@ uc_compiler_compile_for_count(uc_compiler *compiler, bool local, uc_token *var)
{
uc_chunk *chunk = uc_compiler_current_chunk(compiler);
size_t test_off = 0, incr_off, skip_off, cond_off = 0;
- uc_patchlist p = { 0 };
+ uc_patchlist p = { .depth = compiler->scope_depth + 1 };
p.parent = compiler->patchlist;
compiler->patchlist = &p;
@@ -2344,9 +2344,9 @@ uc_compiler_compile_switch(uc_compiler *compiler)
{
size_t i, test_jmp, skip_jmp, next_jmp, value_slot, default_off = 0;
uc_chunk *chunk = uc_compiler_current_chunk(compiler);
+ uc_patchlist p = { .depth = compiler->scope_depth };
uc_locals *locals = &compiler->locals;
uc_jmplist cases = { 0 };
- uc_patchlist p = { 0 };
p.parent = compiler->patchlist;
compiler->patchlist = &p;
@@ -2599,7 +2599,7 @@ uc_compiler_compile_control(uc_compiler *compiler)
}
/* pop locals in scope up to this point */
- for (i = locals->count; i > 0 && (size_t)locals->entries[i - 1].depth == compiler->scope_depth; i--)
+ for (i = locals->count; i > 0 && (size_t)locals->entries[i - 1].depth > p->depth; i--)
uc_compiler_emit_insn(compiler, 0, I_POP);
uc_vector_grow(p);
diff --git a/compiler.h b/compiler.h
index 3dcb79a..deec3d5 100644
--- a/compiler.h
+++ b/compiler.h
@@ -66,7 +66,7 @@ typedef enum {
struct uc_patchlist {
struct uc_patchlist *parent;
- size_t count, *entries;
+ size_t depth, count, *entries;
};
typedef struct uc_patchlist uc_patchlist;
diff --git a/tests/custom/03_bugs/22_compile_break_continue_scoping b/tests/custom/03_bugs/22_compile_break_continue_scoping
new file mode 100644
index 0000000..461b144
--- /dev/null
+++ b/tests/custom/03_bugs/22_compile_break_continue_scoping
@@ -0,0 +1,59 @@
+When compiling a break or continue statement, the compiler emitted pop
+instructions for local variables within the scope the break or continue
+keyword appeared in, but it must also pop local variables in enclosing
+scopes up until the scope of the containing loop or switch body.
+
+-- Expect stdout --
+1
+2
+3
+-- End --
+
+-- Testcase --
+{%
+ for (let i = 1; i <= 3; i++) {
+ while (true) {
+ let n = i;
+
+ print(n, "\n");
+
+ {
+ // The `let n` stack slot is not popped since it is
+ // outside of break's scope...
+ break;
+ }
+ }
+ }
+%}
+-- End --
+
+-- Expect stdout --
+1
+2
+3
+2
+4
+6
+3
+6
+9
+-- End --
+
+-- Testcase --
+{%
+ for (let i = 1; i <= 3; i++) {
+ for (let j = 1; j <= 3; j++) {
+ let n = i * j;
+
+ print(n, "\n");
+
+ if (j == 1)
+ {
+ // The `let n` stack slot is not popped since it is
+ // outside of continue's scope...
+ continue;
+ }
+ }
+ }
+%}
+-- End --