Skip to content

Commit 21086be

Browse files
committed
[GR-23226] Disallow break and continue outside loops
PullRequest: graalpython/1006
2 parents e62937a + 057d314 commit 21086be

File tree

4 files changed

+1451
-1397
lines changed

4 files changed

+1451
-1397
lines changed

graalpython/com.oracle.graal.python.test/src/com/oracle/graal/python/test/parser/BasicTests.java

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* The Universal Permissive License (UPL), Version 1.0
@@ -41,13 +41,16 @@
4141

4242
package com.oracle.graal.python.test.parser;
4343

44+
import java.io.File;
45+
46+
import org.junit.Assert;
47+
import org.junit.Test;
48+
4449
import com.oracle.graal.python.runtime.PythonCore;
4550
import com.oracle.graal.python.runtime.PythonParser;
4651
import com.oracle.truffle.api.Truffle;
4752
import com.oracle.truffle.api.frame.Frame;
4853
import com.oracle.truffle.api.frame.FrameDescriptor;
49-
import java.io.File;
50-
import org.junit.*;
5154

5255
public class BasicTests extends ParserTestBase {
5356

@@ -493,6 +496,22 @@ public void for15() throws Exception {
493496
" return 10");
494497
}
495498

499+
@Test
500+
public void for16() throws Exception {
501+
checkSyntaxError(
502+
"for i in range(10):\n" +
503+
" def foo():\n" +
504+
" continue\n");
505+
}
506+
507+
@Test
508+
public void for17() throws Exception {
509+
checkSyntaxError(
510+
"for i in range(10):\n" +
511+
" class foo:\n" +
512+
" break\n");
513+
}
514+
496515
@Test
497516
public void global01() throws Exception {
498517
checkScopeAndTree();
@@ -829,6 +848,14 @@ public void while11() throws Exception {
829848
" break");
830849
}
831850

851+
@Test
852+
public void while12() throws Exception {
853+
checkSyntaxError(
854+
"while False:\n" +
855+
" def foo():\n" +
856+
" break");
857+
}
858+
832859
@Test
833860
public void with01() throws Exception {
834861
checkTreeResult(

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/parser/antlr/Python3.g4

Lines changed: 42 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -184,25 +184,28 @@ import java.util.Arrays;
184184

185185

186186
@parser::members {
187+
private static class LoopState {
188+
public boolean containsBreak;
189+
public boolean containsContinue;
190+
}
187191
private PythonSSTNodeFactory factory;
188192
private ScopeEnvironment scopeEnvironment;
189-
boolean containsBreak;
190-
boolean containsContinue;
191-
192-
public final boolean startLoopBreak() {
193-
try {
194-
return containsBreak;
195-
} finally {
196-
containsBreak = false;
197-
}
193+
private LoopState loopState;
194+
195+
public final LoopState startLoop() {
196+
try {
197+
return loopState;
198+
} finally {
199+
loopState = new LoopState();
200+
}
198201
}
199202

200-
public final boolean startLoopContinue() {
203+
public final LoopState saveLoopState() {
201204
try {
202-
return containsContinue;
203-
} finally {
204-
containsContinue = false;
205-
}
205+
return loopState;
206+
} finally {
207+
loopState = null;
208+
}
206209
}
207210

208211
private Object[] stack = new Object[8];
@@ -326,6 +329,7 @@ locals
326329
scopeEnvironment.pushScope(_localctx.toString(), ScopeInfo.ScopeKind.Module);
327330
}
328331
}
332+
{ loopState = null; }
329333
{ int start = start(); }
330334
(
331335
NEWLINE
@@ -345,6 +349,7 @@ locals
345349
[ com.oracle.graal.python.parser.ScopeInfo scope, ArrayList<StatementNode> list ]
346350
:
347351
{ _localctx.scope = scopeEnvironment.pushScope(_localctx.toString(), ScopeInfo.ScopeKind.Module); }
352+
{ loopState = null; }
348353
{ int start = start(); }
349354
(
350355
NEWLINE
@@ -411,13 +416,15 @@ funcdef
411416
ScopeInfo enclosingScope = scopeEnvironment.getCurrentScope();
412417
String enclosingClassName = enclosingScope.isInClassScope() ? enclosingScope.getScopeId() : null;
413418
ScopeInfo functionScope = scopeEnvironment.pushScope(name, ScopeInfo.ScopeKind.Function);
419+
LoopState savedLoopState = saveLoopState();
414420
functionScope.setHasAnnotations(true);
415421
$parameters.result.defineParamsInScope(functionScope);
416422
}
417423
s = suite
418424
{
419425
SSTNode funcDef = new FunctionDefSSTNode(scopeEnvironment.getCurrentScope(), name, enclosingClassName, $parameters.result, $s.result, getStartIndex(_localctx), getStopIndex(((FuncdefContext)_localctx).s));
420-
scopeEnvironment.popScope();
426+
scopeEnvironment.popScope();
427+
loopState = savedLoopState;
421428
push(funcDef);
422429
}
423430
;
@@ -675,14 +682,20 @@ del_stmt
675682
flow_stmt
676683
:
677684
b='break'
678-
{
685+
{
686+
if (loopState == null) {
687+
throw new PythonRecognitionException("'break' outside loop", this, _input, _localctx, getCurrentToken());
688+
}
679689
push(new SimpleSSTNode(SimpleSSTNode.Type.BREAK, getStartIndex($b), getStopIndex($b)));
680-
containsBreak = true;
690+
loopState.containsBreak = true;
681691
}
682692
| c='continue'
683-
{
693+
{
694+
if (loopState == null) {
695+
throw new PythonRecognitionException("'continue' not properly in loop", this, _input, _localctx, getCurrentToken());
696+
}
684697
push(new SimpleSSTNode(SimpleSSTNode.Type.CONTINUE, getStartIndex($c), getStopIndex($c)));
685-
containsContinue = true;
698+
loopState.containsContinue = true;
686699
}
687700
| return_stmt
688701
| raise_stmt
@@ -853,12 +866,11 @@ elif_stmt returns [SSTNode result]
853866
while_stmt
854867
:
855868
'while' test ':'
856-
{ boolean bFlag = startLoopBreak(); boolean cFlag = startLoopContinue(); }
869+
{ LoopState savedState = startLoop(); }
857870
suite
858871
{
859-
WhileSSTNode result = new WhileSSTNode($test.result, $suite.result, containsContinue, containsBreak, getStartIndex($ctx),getStopIndex($suite.stop));
860-
containsContinue = cFlag;
861-
containsBreak = bFlag;
872+
WhileSSTNode result = new WhileSSTNode($test.result, $suite.result, loopState.containsContinue, loopState.containsBreak, getStartIndex($ctx),getStopIndex($suite.stop));
873+
loopState = savedState;
862874
}
863875
(
864876
'else' ':' suite
@@ -875,13 +887,12 @@ while_stmt
875887
for_stmt
876888
:
877889
'for' exprlist 'in' testlist ':'
878-
{ boolean bFlag = startLoopBreak(); boolean cFlag = startLoopContinue(); }
890+
{ LoopState savedState = startLoop(); }
879891
suite
880892
{
881-
ForSSTNode result = factory.createForSSTNode($exprlist.result, $testlist.result, $suite.result, containsContinue, getStartIndex($ctx),getStopIndex($suite.stop));
882-
result.setContainsBreak(containsBreak);
883-
containsContinue = cFlag;
884-
containsBreak = bFlag;
893+
ForSSTNode result = factory.createForSSTNode($exprlist.result, $testlist.result, $suite.result, loopState.containsContinue, getStartIndex($ctx),getStopIndex($suite.stop));
894+
result.setContainsBreak(loopState.containsBreak);
895+
loopState = savedState;
885896
}
886897
(
887898
'else' ':' suite
@@ -1478,9 +1489,11 @@ locals [ com.oracle.graal.python.parser.ScopeInfo scope ]
14781489
factory.getScopeEnvironment().createLocal($NAME.text);
14791490
ScopeInfo classScope = scopeEnvironment.pushScope($NAME.text, ScopeInfo.ScopeKind.Class);
14801491
}
1492+
{ LoopState savedLoopState = saveLoopState(); }
14811493
':' suite
14821494
{ push(factory.createClassDefinition($NAME.text, baseClasses, $suite.result, getStartIndex($ctx), getStopIndex($suite.stop))); }
1483-
{scopeEnvironment.popScope(); }
1495+
{ scopeEnvironment.popScope(); }
1496+
{ loopState = savedLoopState; }
14841497
;
14851498

14861499
arglist returns [ArgListBuilder result]

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/parser/antlr/Python3.interp

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

0 commit comments

Comments
 (0)