infinite loop trap: nested loops

367 views
Skip to first unread message

John Dalbey

unread,
Oct 15, 2022, 2:26:20 PM10/15/22
to Blockly
I could use some help setting up infinite loop traps for nested loops.
I followed this example and it works for simple loops, but not nested loops.
If the outer loop is infinite, the trap doesn't work. I suspect a scoping problem ... the inner loop has the same trap variable name as the outer loop.


Blockly.JavaScript['loopUntilWall'] = function(block) {
  // repeat until loop.
  let branch = Blockly.JavaScript.statementToCode(block, 'DO');
  let result = `var loopTrap = 999; `;
  result += `do {` + branch;
  result += 'if(--loopTrap == 0) throw new Error("Infinite loop.");';
  result += `}while (!atWall());\n`;
  return result;
};    



(Note, I'm using JS-Interpreter).

This seems like it must be a common problem and I hope someone has a nice solution.  So far the only idea I have is to create a looptrap stack and have each loop push its trap variable on the stack.

Any ideas?
--john

Oz Ramos

unread,
Oct 15, 2022, 2:50:17 PM10/15/22
to Blockly
Depending on how your blocks are setup it could be a scoping issue, have you tried putting it inside a self executing function?
Something like:

let result = `;(function () {`
  // result += '...current code here...'
result += '})();'

Neil Fraser

unread,
Oct 15, 2022, 2:57:41 PM10/15/22
to blo...@googlegroups.com
Your code is declaring and initializing a 'loopTrap' variable at the start of every loop.  Thus a nested loop would reset the 'loopTrap' variable, meaning the outer loop would no longer have protection.

The example you linked to is very different.  The 'LoopTrap' in that example is a global property that's initialized outside the generator.  That will handle nested loops just fine.

However, you state that you are using the JS-Interpreter.  With the JS-Interpreter there is no need for loop traps.  It's a step-by-step interpreter, so you can step until you get bored, and end execution:

--
You received this message because you are subscribed to the Google Groups "Blockly" group.
To unsubscribe from this group and stop receiving emails from it, send an email to blockly+u...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/blockly/bbdd65d3-aef4-4f28-8845-54bac3043964n%40googlegroups.com.


--

John Dalbey

unread,
Oct 15, 2022, 4:24:09 PM10/15/22
to Blockly
Yes, I agree it's a scoping issue, and I see how you're proposed solution might work.  Thanks for the suggestion.

John Dalbey

unread,
Oct 15, 2022, 4:32:38 PM10/15/22
to Blockly

Thanks for your reply, Neil.  I understand that JS-Interpreter can be used in step-by-step mode, but if one wants to use it in "run" mode, won't infinite loops still be an issue?

Would you be willing to elaborate on what you mean by "until you get bored"?  I'm guessing you are suggesting that a user who is executing a program in step by step mode has control of the execution and can terminate whenever they choose. But if the user "steps" into a loop (i.e., repeat block) with no body, won't it still get stuck in an infinite loop?

Neil Fraser

unread,
Oct 15, 2022, 4:46:24 PM10/15/22
to blo...@googlegroups.com
Yes, 'run' is generally just used to test and prove that the interpreter is working, it's not safe.

Repeated calls to 'step' can be done in several ways.  One approach is to call 'step' indefinitely, and let the user kill it whenever they wish though some sort of a reset button (this is what Blockly Games does):
var stepPid;
function nextStep() {
  if (myInterpreter.step()) {
    stepPid = setTimeout(nextStep, 10);
  }
}
nextStep();
function stop() {
  clearTimeout(stepPid);
}
This won't lock up the browser since there's a 10ms pause between each step.  So it's safe to execute without limit.

Another approach is to put a step limit on the program:
var steps = 10000;
function nextStep() {
  if (myInterpreter.step() && steps-- !== 0) {
    setTimeout(nextStep, 10);
  }
}
nextStep();
It will execute until either the program ends or 'steps' counts down from 10,000 to 0.

Another approach is to put a time limit on the program:
var endTime = Date.now() + 1000 * 15;
function nextStep() {
  if (myInterpreter.step() && Date.now() < endTime) {
    setTimeout(nextStep, 10);
  }
}
nextStep();
It will execute until either the program ends or 15 seconds have elapsed.

These are a few ideas to get you started.  Hope this helps!

John Dalbey

unread,
Oct 15, 2022, 6:26:11 PM10/15/22
to Blockly
Thanks, Neil.  That's really helpful. 
I still have one doubt ... you say Blockly Games uses the first approach, of letting the user kill an infinite loop with a Reset button. You claim this won't lock up the browser. But when I use the Turtle game and create a "repeat while true" loop with an empty body, I'm unable to kill it with the Reset button.
So I think the second approach is needed (using a step limit) even if a Reset button is provided to the user.  Am I understanding correctly?

Neil Fraser

unread,
Oct 15, 2022, 6:38:10 PM10/15/22
to blo...@googlegroups.com
Ooh, that's interesting.  Confirming infinite loop crash in Blockly Games.  Will fix that tomorrow and push out a new release.

The source of that crash is that we're trying to be overly clever about making the breaks line up with 'turtle' blocks.  And since there are none, problems arise.  The details are very specific to Blockly Games.  But the code in my previous email will work fine and be completely safe from locking up the browser.

Thanks for pointing out that bug!

Reply all
Reply to author
Forward
0 new messages