1 /*
  2  * Copyright (c) 2011, 2021, 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 7025809 8028543 6415644 8028544 8029942 8187951 8193291 8196551 8233096 8275308
 27  * @summary Test latest, latestSupported, underscore as keyword, etc.
 28  * @author  Joseph D. Darcy
 29  * @modules java.compiler
 30  *          jdk.compiler
 31  */
 32 
 33 import java.util.*;
 34 import java.util.function.Predicate;
 35 import javax.lang.model.SourceVersion;
 36 import static javax.lang.model.SourceVersion.*;
 37 
 38 /**
 39  * Verify behavior of latest[Supported] and other methods.
 40  */
 41 public class TestSourceVersion {
 42     public static void main(String... args) {
 43         testLatestSupported();
 44         testVersionVaryingKeywords();
 45         testRestrictedKeywords();
 46         testVar();
 47         testYield();
 48         testValueOfRV();
 49         testRuntimeVersion();
 50     }
 51 
 52     private static void testLatestSupported() {
 53         SourceVersion[] values = SourceVersion.values();
 54         SourceVersion last = values[values.length - 1];
 55         SourceVersion latest = SourceVersion.latest();
 56         SourceVersion latestSupported = SourceVersion.latestSupported();
 57 
 58         if (latest == last &&
 59             latestSupported == SourceVersion.valueOf("RELEASE_" +
 60                                                      Runtime.version().feature()) &&
 61             (latest == latestSupported ||
 62              (latest.ordinal() - latestSupported.ordinal() == 1)) )
 63             return;
 64         else {
 65             throw new RuntimeException("Unexpected release value(s) found:\n" +
 66                                        "latest:\t" + latest + "\n" +
 67                                        "latestSupported:\t" + latestSupported);
 68         }
 69     }
 70 
 71     private static void testVersionVaryingKeywords() {
 72         Map<String, SourceVersion> keyWordStart =
 73             Map.of("strictfp", RELEASE_2,
 74                    "assert",   RELEASE_4,
 75                    "enum",     RELEASE_5,
 76                    "_",        RELEASE_9);
 77 
 78         for (Map.Entry<String, SourceVersion> entry : keyWordStart.entrySet()) {
 79             String key = entry.getKey();
 80             SourceVersion value = entry.getValue();
 81 
 82             check(true,  key, (String s) -> isKeyword(s), "keyword", latest());
 83             check(false, key, (String s) -> isName(s),    "name",    latest());
 84 
 85             for(SourceVersion version : SourceVersion.values()) {
 86                 boolean isKeyword = version.compareTo(value) >= 0;
 87 
 88                 check(isKeyword,  key, (String s) -> isKeyword(s, version), "keyword", version);
 89                 check(!isKeyword, key, (String s) -> isName(s, version),    "name",    version);
 90             }
 91         }
 92     }
 93 
 94     private static void testRestrictedKeywords() {
 95         // Restricted keywords are not full keywords
 96 
 97         /*
 98          * JLS 3.9
 99          * " A further ten character sequences are restricted
100          * keywords: open, module, requires, transitive, exports,
101          * opens, to, uses, provides, and with"
102          */
103         Set<String> restrictedKeywords =
104             Set.of("open", "module", "requires", "transitive", "exports",
105                    "opens", "to", "uses", "provides", "with",
106                    // Assume "record" and "sealed" will be restricted keywords.
107                    "record", "sealed", "value");
108 
109         for (String key : restrictedKeywords) {
110             for (SourceVersion version : SourceVersion.values()) {
111                 check(false, key, (String s) -> isKeyword(s, version), "keyword", version);
112                 check(true,  key, (String s) -> isName(s, version),    "name",    version);
113             }
114         }
115     }
116 
117     private static void testVar() {
118         for (SourceVersion version : SourceVersion.values()) {
119             Predicate<String> isKeywordVersion = (String s) -> isKeyword(s, version);
120             Predicate<String> isNameVersion = (String s) -> isName(s, version);
121 
122             for (String name : List.of("var", "foo.var", "var.foo")) {
123                 check(false, name, isKeywordVersion, "keyword", version);
124                 check(true, name,  isNameVersion, "name", version);
125             }
126         }
127     }
128 
129     private static void testYield() {
130         for (SourceVersion version : SourceVersion.values()) {
131             Predicate<String> isKeywordVersion = (String s) -> isKeyword(s, version);
132             Predicate<String> isNameVersion = (String s) -> isName(s, version);
133 
134             for  (String name : List.of("yield", "foo.yield", "yield.foo")) {
135                 check(false, name, isKeywordVersion, "keyword", version);
136                 check(true, name,  isNameVersion, "name", version);
137             }
138         }
139     }
140 
141     private static void testValue() {
142         for (SourceVersion version : SourceVersion.values()) {
143             Predicate<String> isKeywordVersion = (String s) -> isKeyword(s, version);
144             Predicate<String> isNameVersion = (String s) -> isName(s, version);
145 
146             for  (String name : List.of("value", "foo.value", "value.foo")) {
147                 check(false, name, isKeywordVersion, "keyword", version);
148                 check(true, name,  isNameVersion, "name", version);
149             }
150         }
151     }
152 
153     private static void check(boolean expected,
154                               String input,
155                               Predicate<String> predicate,
156                               String message,
157                               SourceVersion version) {
158         boolean result  = predicate.test(input);
159         if (result != expected) {
160             throw new RuntimeException("Unexpected " + message +  "-ness of " + input +
161                                        " on " + version);
162         }
163     }
164 
165     /**
166      * Test that SourceVersion.valueOf() maps a Runtime.Version to a
167      * SourceVersion properly. The SourceVersion result is only a
168      * function of the feature() component of a Runtime.Version.
169      */
170     private static void testValueOfRV() {
171         for (SourceVersion sv : SourceVersion.values()) {
172             if (sv == RELEASE_0) {
173                 continue;
174             } else {
175                 // Plain mapping; e.g. "17" -> RELEASE_17
176                 String featureBase = Integer.toString(sv.ordinal());
177                 checkValueOfResult(sv, featureBase);
178 
179                 // More populated runtime version, N.N
180                 checkValueOfResult(sv, featureBase + "." + featureBase);
181             }
182         }
183 
184         // Out of range test
185         try {
186             int latestFeature = SourceVersion.latest().runtimeVersion().feature();
187             SourceVersion.valueOf(Runtime.Version.parse(Integer.toString(latestFeature +1)));
188             throw new RuntimeException("Should not reach");
189         } catch (IllegalArgumentException iae) {
190             ; // Expected
191         }
192     }
193 
194     private static void checkValueOfResult(SourceVersion expected, String versionString) {
195         Runtime.Version rv = Runtime.Version.parse(versionString);
196         SourceVersion  result = SourceVersion.valueOf(rv);
197         if (result != expected) {
198             throw new RuntimeException("Unexpected result " + result +
199                                        " of mapping Runtime.Version " + versionString +
200                                        " intead of " + expected);
201         }
202     }
203 
204     private static void testRuntimeVersion() {
205         for (SourceVersion sv : SourceVersion.values()) {
206             Runtime.Version result = sv.runtimeVersion();
207             if (sv.compareTo(RELEASE_6) < 0) {
208                 if (result != null) {
209                     throw new RuntimeException("Unexpected result non-null " + result +
210                                                " as runtime version of  " + sv);
211                 }
212             } else {
213                 Runtime.Version expected = Runtime.Version.parse(Integer.toString(sv.ordinal()));
214                 if (!result.equals(expected)) {
215                     throw new RuntimeException("Unexpected result " + result +
216                                                " as runtime version of " + sv);
217                 }
218             }
219         }
220     }
221 }