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  * @enablePreview
 29  * @library /tools/lib /tools/javac/lib
 30  * @modules
 31  *      jdk.compiler/com.sun.tools.javac.api
 32  *      jdk.compiler/com.sun.tools.javac.file
 33  *      jdk.compiler/com.sun.tools.javac.main
 34  *      jdk.compiler/com.sun.tools.javac.util
 35  * @build toolbox.ToolBox toolbox.JavacTask
 36  * @build combo.ComboTestHelper
 37  * @compile BreakAndLoops.java
 38  * @run main BreakAndLoops
 39  */
 40 
 41 import combo.ComboInstance;
 42 import combo.ComboParameter;
 43 import combo.ComboTask;
 44 import combo.ComboTestHelper;
 45 import java.nio.file.Path;
 46 import java.nio.file.Paths;
 47 import toolbox.ToolBox;
 48 
 49 public class BreakAndLoops extends ComboInstance<BreakAndLoops> {
 50     protected ToolBox tb;
 51 
 52     BreakAndLoops() {
 53         super();
 54         tb = new ToolBox();
 55     }
 56 
 57     public static void main(String... args) throws Exception {
 58         new ComboTestHelper<BreakAndLoops>()
 59                 .withDimension("OUTTER_LABEL", (x, outterLabel) -> x.outterLabel = outterLabel, OutterLabel.values())
 60                 .withDimension("OUTTER_LOOP", (x, outterLoop) -> x.outterLoop = outterLoop, OutterLoop.values())
 61                 .withDimension("MAIN_LOOP", (x, mainLoop) -> x.mainLoop = mainLoop, MainLoop.values())
 62                 .withDimension("INNER_LABEL", (x, innerLabel) -> x.innerLabel = innerLabel, Label.values())
 63                 .withDimension("INNER_LOOP", (x, innerLoop) -> x.innerLoop = innerLoop, Loop.values())
 64                 .withDimension("BREAK", (x, brk) -> x.brk = brk, Break.values())
 65                 .withFilter(bal -> bal.outterLabel != OutterLabel.LABEL || bal.innerLabel != Label.LABEL)
 66                 .run(BreakAndLoops::new);
 67     }
 68 
 69     private OutterLabel outterLabel;
 70     private OutterLoop outterLoop;
 71     private MainLoop mainLoop;
 72     private Label innerLabel;
 73     private Loop innerLoop;
 74     private Break brk;
 75 
 76     private static final String MAIN_TEMPLATE =
 77             """
 78             public class Test {
 79                 public static void doTest(Object o, int i, Object[] arr) {
 80                     #{OUTTER_LABEL}
 81                 }
 82             }
 83             """;
 84 
 85     @Override
 86     protected void doWork() throws Throwable {
 87         Path base = Paths.get(".");
 88 
 89         ComboTask task = newCompilationTask()
 90                 .withSourceFromTemplate(MAIN_TEMPLATE, pname -> switch (pname) {
 91                         case "OUTTER_LABEL" -> outterLabel;
 92                         case "OUTTER_LOOP" -> outterLoop;
 93                         case "MAIN_LOOP" -> mainLoop;
 94                         case "NESTED_LOOP" -> innerLoop;
 95                         case "NESTED" -> brk;
 96                         case "BODY" -> innerLabel;
 97                         default -> throw new UnsupportedOperationException(pname);
 98                     });
 99 
100         task.generate(result -> {
101             boolean shouldPass;
102             if (brk == Break.NONE) {
103                 shouldPass = true;
104             } else if (innerLabel == Label.LABEL && brk == Break.BREAK_LABEL) {
105                 shouldPass = true;
106             } else if (innerLoop.supportsAnonymousBreak && brk == Break.BREAK) {
107                 shouldPass = true;
108              } else if (outterLabel == OutterLabel.LABEL && brk == Break.BREAK_LABEL && outterLoop != OutterLoop.NONE) {
109                  shouldPass = switch(mainLoop) {
110                      case WHILE, FOR -> true;
111                      case DO_WHILE -> switch (innerLoop) {
112                          case WHILE, FOR, FOR_EACH -> true;
113                          //the statement following the do-while is unreachable:
114                          case BLOCK, DO_WHILE, NONE -> {
115                              yield false;
116                          }
117                      };
118                  };
119             } else {
120                 shouldPass = false;
121             }
122             if (!(shouldPass ^ result.hasErrors())) {
123                 throw new AssertionError("Unexpected result: " + result.compilationInfo());
124             }
125         });
126     }
127 
128     public enum MainLoop implements ComboParameter {
129         WHILE("""
130               while (!(o instanceof String s)) {
131                   #{BODY}
132               }
133               """),
134         FOR("""
135             for( ; !(o instanceof String s) ; ) {
136                 #{BODY}
137             }
138             """),
139         DO_WHILE("""
140                  do {
141                      #{BODY}
142                  } while (!(o instanceof String s));
143                  """);
144         private final String body;
145 
146         private MainLoop(String body) {
147             this.body = body;
148         }
149 
150         @Override
151         public String expand(String optParameter) {
152             return body;
153         }
154     }
155 
156     public enum OutterLabel implements ComboParameter {
157         NONE("#{OUTTER_LOOP}"),
158         LABEL("LABEL: #{OUTTER_LOOP}");
159         private final String code;
160 
161         private OutterLabel(String code) {
162             this.code = code;
163         }
164 
165         @Override
166         public String expand(String optParameter) {
167             return code;
168         }
169     }
170 
171     public enum OutterLoop implements ComboParameter {
172         NONE("#{MAIN_LOOP} System.err.println(s);"),
173         BLOCK("{ #{MAIN_LOOP} System.err.println(s); }"),
174         WHILE("while (i-- > 0) { #{MAIN_LOOP} System.err.println(s); }"),
175         FOR("for ( ; i-- > 0; ) { #{MAIN_LOOP} System.err.println(s); }"),
176         FOR_EACH("for (Object outterO : arr) { #{MAIN_LOOP} System.err.println(s); }"),
177         DO_WHILE("do { #{MAIN_LOOP} System.err.println(s); } while (i-- > 0);");
178         private final String code;
179 
180         private OutterLoop(String code) {
181             this.code = code;
182         }
183 
184         @Override
185         public String expand(String optParameter) {
186             return code;
187         }
188     }
189 
190     public enum Label implements ComboParameter {
191         NONE("#{NESTED_LOOP}"),
192         LABEL("LABEL: #{NESTED_LOOP}");
193         private final String code;
194 
195         private Label(String code) {
196             this.code = code;
197         }
198 
199         @Override
200         public String expand(String optParameter) {
201             return code;
202         }
203     }
204 
205     public enum Loop implements ComboParameter {
206         NONE("#{NESTED}", false),
207         BLOCK("{ #{NESTED} }", false),
208         WHILE("while (i-- > 0) { #{NESTED} }", true),
209         FOR("for ( ; i-- > 0; ) { #{NESTED} }", true),
210         FOR_EACH("for (Object innerO : arr) { #{NESTED} }", true),
211         DO_WHILE("do { #{NESTED} } while (i-- > 0);", true);
212         private final String code;
213         private final boolean supportsAnonymousBreak;
214 
215         private Loop(String code, boolean supportsAnonymousBreak) {
216             this.code = code;
217             this.supportsAnonymousBreak = supportsAnonymousBreak;
218         }
219 
220         @Override
221         public String expand(String optParameter) {
222             return code;
223         }
224     }
225 
226     public enum Break implements ComboParameter {
227         NONE(";"),
228         BREAK("break;"),
229         BREAK_LABEL("break LABEL;");
230         private final String code;
231 
232         private Break(String code) {
233             this.code = code;
234         }
235 
236         @Override
237         public String expand(String optParameter) {
238             return code;
239         }
240     }
241 }