Skip to content

Commit fec84ae

Browse files
authored
Merge pull request #381 from scratchcpp/fix_if_and_loop_skipping
Fix skipping of if statements and loops
2 parents 2083851 + afef8a0 commit fec84ae

File tree

2 files changed

+68
-9
lines changed

2 files changed

+68
-9
lines changed

src/engine/virtualmachine_p.cpp

Lines changed: 35 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -236,25 +236,40 @@ unsigned int *VirtualMachinePrivate::run(unsigned int *pos, bool reset)
236236
checkpoint = pos - 1;
237237
DISPATCH();
238238

239-
do_if:
239+
do_if : {
240240
if (!READ_LAST_REG()->toBool()) {
241241
unsigned int ifCounter = 1;
242-
while ((*pos != OP_ELSE && *pos != OP_ENDIF) || (ifCounter > 0)) {
242+
while (!((*pos == OP_ELSE && ifCounter == 1) || (*pos == OP_ENDIF && ifCounter == 0))) {
243243
pos += instruction_arg_count[*pos++];
244244

245-
if (*pos == OP_IF)
245+
if ((*pos == OP_IF) || (*pos == OP_FOREVER_LOOP) || (*pos == OP_REPEAT_LOOP) || (*pos == OP_UNTIL_LOOP))
246246
ifCounter++;
247-
else if ((*pos == OP_ELSE) || (*pos == OP_ENDIF))
247+
else if ((*pos == OP_ENDIF) || (*pos == OP_LOOP_END)) {
248+
assert(ifCounter > 0);
248249
ifCounter--;
250+
}
249251
}
250252
}
251253
FREE_REGS(1);
252254
DISPATCH();
255+
}
253256

254-
do_else:
255-
while (*pos != OP_ENDIF)
257+
do_else : {
258+
unsigned int ifCounter = 1;
259+
while (!(*pos == OP_ENDIF && ifCounter == 0)) {
256260
pos += instruction_arg_count[*pos++];
257261

262+
if ((*pos == OP_IF) || (*pos == OP_FOREVER_LOOP) || (*pos == OP_REPEAT_LOOP) || (*pos == OP_UNTIL_LOOP))
263+
ifCounter++;
264+
else if ((*pos == OP_ENDIF) || (*pos == OP_LOOP_END)) {
265+
assert(ifCounter > 0);
266+
ifCounter--;
267+
}
268+
269+
assert(!(*pos == OP_ELSE && ifCounter == 1));
270+
}
271+
}
272+
258273
do_endif:
259274
DISPATCH();
260275

@@ -275,10 +290,12 @@ unsigned int *VirtualMachinePrivate::run(unsigned int *pos, bool reset)
275290
while ((*loopEnd != OP_LOOP_END) || (loopCounter > 0)) {
276291
loopEnd += instruction_arg_count[*loopEnd++];
277292

278-
if ((*loopEnd == OP_FOREVER_LOOP) || (*loopEnd == OP_REPEAT_LOOP) || (*loopEnd == OP_UNTIL_LOOP))
293+
if ((*loopEnd == OP_IF) || (*loopEnd == OP_FOREVER_LOOP) || (*loopEnd == OP_REPEAT_LOOP) || (*loopEnd == OP_UNTIL_LOOP))
279294
loopCounter++;
280-
else if (*loopEnd == OP_LOOP_END)
295+
else if ((*loopEnd == OP_ENDIF) || (*loopEnd == OP_LOOP_END)) {
296+
assert(loopCounter > 0);
281297
loopCounter--;
298+
}
282299
}
283300
pos = loopEnd;
284301
} else {
@@ -317,8 +334,17 @@ do_repeat_loop_index1 : {
317334
pos = loopStart;
318335
} else {
319336
pos = loopStart;
320-
while (*pos != OP_LOOP_END)
337+
unsigned int loopCounter = 1;
338+
while ((*pos != OP_LOOP_END) || (loopCounter > 0)) {
321339
pos += instruction_arg_count[*pos++];
340+
341+
if ((*pos == OP_IF) || (*pos == OP_FOREVER_LOOP) || (*pos == OP_REPEAT_LOOP) || (*pos == OP_UNTIL_LOOP))
342+
loopCounter++;
343+
else if ((*pos == OP_ENDIF) || (*pos == OP_LOOP_END)) {
344+
assert(loopCounter > 0);
345+
loopCounter--;
346+
}
347+
}
322348
}
323349
FREE_REGS(1);
324350
DISPATCH();

test/virtual_machine/virtual_machine_test.cpp

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1626,3 +1626,36 @@ TEST(VirtualMachineTest, NoCrashInNestedIfStatementsWithLoop)
16261626
vm.run();
16271627
ASSERT_EQ(vm.registerCount(), 0);
16281628
}
1629+
1630+
TEST(VirtualMachineTest, NoCrashInNestedIfStatementsWithLoopAndIfElse)
1631+
{
1632+
// Regtest for #378
1633+
static unsigned int bytecode[] = { OP_START, OP_NULL, OP_IF, OP_NULL, OP_REPEAT_LOOP, OP_NULL, OP_IF, OP_ELSE, OP_ENDIF, OP_LOOP_END, OP_ENDIF, OP_HALT };
1634+
1635+
VirtualMachine vm(nullptr, nullptr, nullptr);
1636+
vm.setBytecode(bytecode);
1637+
vm.run();
1638+
ASSERT_EQ(vm.registerCount(), 0);
1639+
}
1640+
1641+
TEST(VirtualMachineTest, NoCrashInNestedLoopsInRepeatUntilLoop)
1642+
{
1643+
// Regtest for #379
1644+
static unsigned int bytecode[] = { OP_START, OP_NULL, OP_NOT, OP_IF, OP_ELSE, OP_NULL, OP_REPEAT_LOOP, OP_NULL, OP_IF, OP_ENDIF, OP_LOOP_END, OP_ENDIF, OP_HALT };
1645+
1646+
VirtualMachine vm(nullptr, nullptr, nullptr);
1647+
vm.setBytecode(bytecode);
1648+
vm.run();
1649+
ASSERT_EQ(vm.registerCount(), 0);
1650+
}
1651+
1652+
TEST(VirtualMachineTest, NoCrashInNestedLoopsInIfElseStatements)
1653+
{
1654+
// Regtest for #380
1655+
static unsigned int bytecode[] = { OP_START, OP_UNTIL_LOOP, OP_NULL, OP_NOT, OP_BEGIN_UNTIL_LOOP, OP_NULL, OP_REPEAT_LOOP, OP_LOOP_END, OP_LOOP_END, OP_HALT };
1656+
1657+
VirtualMachine vm(nullptr, nullptr, nullptr);
1658+
vm.setBytecode(bytecode);
1659+
vm.run();
1660+
ASSERT_EQ(vm.registerCount(), 0);
1661+
}

0 commit comments

Comments
 (0)