1 /*
  2  * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
  3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  4  *
  5  * This code is free software; you can redistribute it and/or modify it
  6  * under the terms of the GNU General Public License version 2 only, as
  7  * published by the Free Software Foundation.
  8  *
  9  * This code is distributed in the hope that it will be useful, but WITHOUT
 10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 12  * version 2 for more details (a copy is included in the LICENSE file that
 13  * accompanied this code).
 14  *
 15  * You should have received a copy of the GNU General Public License version
 16  * 2 along with this work; if not, write to the Free Software Foundation,
 17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 18  *
 19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 20  * or visit www.oracle.com if you need additional information or have any
 21  * questions.
 22  */
 23 
 24 /*
 25  * @test
 26  * @bug 8234922
 27  * @summary Verify proper scope of binding related to loops and breaks.
 28  * @library /tools/lib /tools/javac/lib
 29  * @modules
 30  *      jdk.compiler/com.sun.tools.javac.api
 31  *      jdk.compiler/com.sun.tools.javac.file
 32  *      jdk.compiler/com.sun.tools.javac.main
 33  *      jdk.compiler/com.sun.tools.javac.util
 34  * @build toolbox.ToolBox toolbox.JavacTask
 35  * @build combo.ComboTestHelper
 36  * @compile BreakAndLoops.java
 37  * @run main BreakAndLoops
 38  */
 39 
 40 import combo.ComboInstance;
 41 import combo.ComboParameter;
 42 import combo.ComboTask;
 43 import combo.ComboTestHelper;
 44 import java.nio.file.Path;
 45 import java.nio.file.Paths;
 46 import toolbox.ToolBox;
 47 
 48 public class BreakAndLoops extends ComboInstance<BreakAndLoops> {
 49     protected ToolBox tb;
 50 
 51     BreakAndLoops() {
 52         super();
 53         tb = new ToolBox();
 54     }
 55 
 56     public static void main(String... args) throws Exception {
 57         new ComboTestHelper<BreakAndLoops>()
 58                 .withDimension("OUTTER_LABEL", (x, outterLabel) -> x.outterLabel = outterLabel, OutterLabel.values())
 59                 .withDimension("OUTTER_LOOP", (x, outterLoop) -> x.outterLoop = outterLoop, OutterLoop.values())
 60                 .withDimension("MAIN_LOOP", (x, mainLoop) -> x.mainLoop = mainLoop, MainLoop.values())
 61                 .withDimension("INNER_LABEL", (x, innerLabel) -> x.innerLabel = innerLabel, Label.values())
 62                 .withDimension("INNER_LOOP", (x, innerLoop) -> x.innerLoop = innerLoop, Loop.values())
 63                 .withDimension("BREAK", (x, brk) -> x.brk = brk, Break.values())
 64                 .withFilter(bal -> bal.outterLabel != OutterLabel.LABEL || bal.innerLabel != Label.LABEL)
 65                 .run(BreakAndLoops::new);
 66     }
 67 
 68     private OutterLabel outterLabel;
 69     private OutterLoop outterLoop;
 70     private MainLoop mainLoop;
 71     private Label innerLabel;
 72     private Loop innerLoop;
 73     private Break brk;
 74 
 75     private static final String MAIN_TEMPLATE =
 76             """
 77             public class Test {
 78                 public static void doTest(Object o, int i, Object[] arr) {
 79                     #{OUTTER_LABEL}
 80                 }
 81             }
 82             """;
 83 
 84     @Override
 85     protected void doWork() throws Throwable {
 86         Path base = Paths.get(".");
 87 
 88         ComboTask task = newCompilationTask()
 89                 .withSourceFromTemplate(MAIN_TEMPLATE, pname -> switch (pname) {
 90                         case "OUTTER_LABEL" -> outterLabel;
 91                         case "OUTTER_LOOP" -> outterLoop;
 92                         case "MAIN_LOOP" -> mainLoop;
 93                         case "NESTED_LOOP" -> innerLoop;
 94                         case "NESTED" -> brk;
 95                         case "BODY" -> innerLabel;
 96                         default -> throw new UnsupportedOperationException(pname);
 97                     });
 98 
 99         task.generate(result -> {
100             boolean shouldPass;
101             if (brk == Break.NONE) {
102                 shouldPass = true;
103             } else if (innerLabel == Label.LABEL && brk == Break.BREAK_LABEL) {
104                 shouldPass = true;
105             } else if (innerLoop.supportsAnonymousBreak && brk == Break.BREAK) {
106                 shouldPass = true;
107              } else if (outterLabel == OutterLabel.LABEL && brk == Break.BREAK_LABEL && outterLoop != OutterLoop.NONE) {
108                  shouldPass = switch(mainLoop) {
109                      case WHILE, FOR -> true;
110                      case DO_WHILE -> switch (innerLoop) {
111                          case WHILE, FOR, FOR_EACH -> true;
112                          //the statement following the do-while is unreachable:
113                          case BLOCK, DO_WHILE, NONE -> {
114                              yield false;
115                          }
116                      };
117                  };
118             } else {
119                 shouldPass = false;
120             }
121             if (!(shouldPass ^ result.hasErrors())) {
122                 throw new AssertionError("Unexpected result: " + result.compilationInfo());
123             }
124         });
125     }
126 
127     public enum MainLoop implements ComboParameter {
128         WHILE("""
129               while (!(o instanceof String s)) {
130                   #{BODY}
131               }
132               """),
133         FOR("""
134             for( ; !(o instanceof String s) ; ) {
135                 #{BODY}
136             }
137             """),
138         DO_WHILE("""
139                  do {
140                      #{BODY}
141                  } while (!(o instanceof String s));
142                  """);
143         private final String body;
144 
145         private MainLoop(String body) {
146             this.body = body;
147         }
148 
149         @Override
150         public String expand(String optParameter) {
151             return body;
152         }
153     }
154 
155     public enum OutterLabel implements ComboParameter {
156         NONE("#{OUTTER_LOOP}"),
157         LABEL("LABEL: #{OUTTER_LOOP}");
158         private final String code;
159 
160         private OutterLabel(String code) {
161             this.code = code;
162         }
163 
164         @Override
165         public String expand(String optParameter) {
166             return code;
167         }
168     }
169 
170     public enum OutterLoop implements ComboParameter {
171         NONE("#{MAIN_LOOP} System.err.println(s);"),
172         BLOCK("{ #{MAIN_LOOP} System.err.println(s); }"),
173         WHILE("while (i-- > 0) { #{MAIN_LOOP} System.err.println(s); }"),
174         FOR("for ( ; i-- > 0; ) { #{MAIN_LOOP} System.err.println(s); }"),
175         FOR_EACH("for (Object outterO : arr) { #{MAIN_LOOP} System.err.println(s); }"),
176         DO_WHILE("do { #{MAIN_LOOP} System.err.println(s); } while (i-- > 0);");
177         private final String code;
178 
179         private OutterLoop(String code) {
180             this.code = code;
181         }
182 
183         @Override
184         public String expand(String optParameter) {
185             return code;
186         }
187     }
188 
189     public enum Label implements ComboParameter {
190         NONE("#{NESTED_LOOP}"),
191         LABEL("LABEL: #{NESTED_LOOP}");
192         private final String code;
193 
194         private Label(String code) {
195             this.code = code;
196         }
197 
198         @Override
199         public String expand(String optParameter) {
200             return code;
201         }
202     }
203 
204     public enum Loop implements ComboParameter {
205         NONE("#{NESTED}", false),
206         BLOCK("{ #{NESTED} }", false),
207         WHILE("while (i-- > 0) { #{NESTED} }", true),
208         FOR("for ( ; i-- > 0; ) { #{NESTED} }", true),
209         FOR_EACH("for (Object innerO : arr) { #{NESTED} }", true),
210         DO_WHILE("do { #{NESTED} } while (i-- > 0);", true);
211         private final String code;
212         private final boolean supportsAnonymousBreak;
213 
214         private Loop(String code, boolean supportsAnonymousBreak) {
215             this.code = code;
216             this.supportsAnonymousBreak = supportsAnonymousBreak;
217         }
218 
219         @Override
220         public String expand(String optParameter) {
221             return code;
222         }
223     }
224 
225     public enum Break implements ComboParameter {
226         NONE(";"),
227         BREAK("break;"),
228         BREAK_LABEL("break LABEL;");
229         private final String code;
230 
231         private Break(String code) {
232             this.code = code;
233         }
234 
235         @Override
236         public String expand(String optParameter) {
237             return code;
238         }
239     }
240 }