• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2021 Google Inc. All Rights Reserved.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.google.turbine.options;
18 
19 import com.google.auto.value.AutoValue;
20 import com.google.common.collect.ImmutableList;
21 import com.google.common.primitives.Ints;
22 import java.util.Iterator;
23 import java.util.OptionalInt;
24 import javax.lang.model.SourceVersion;
25 
26 /**
27  * The language version being compiled, corresponding to javac's {@code -source}, {@code -target},
28  * and {@code --release} flags.
29  */
30 @AutoValue
31 public abstract class LanguageVersion {
32 
33   /** The source version. */
source()34   public abstract int source();
35 
36   /** The target version. */
target()37   public abstract int target();
38 
39   /**
40    * The release version.
41    *
42    * <p>If set, system APIs will be resolved from the host JDK's ct.sym instead of using the
43    * provided {@code --bootclasspath}.
44    */
release()45   public abstract OptionalInt release();
46 
47   /** The class file major version corresponding to the {@link #target}. */
majorVersion()48   public int majorVersion() {
49     return target() + 44;
50   }
51 
sourceVersion()52   public SourceVersion sourceVersion() {
53     try {
54       return SourceVersion.valueOf("RELEASE_" + source());
55     } catch (IllegalArgumentException unused) {
56       return SourceVersion.latestSupported();
57     }
58   }
59 
create(int source, int target, OptionalInt release)60   private static LanguageVersion create(int source, int target, OptionalInt release) {
61     return new AutoValue_LanguageVersion(source, target, release);
62   }
63 
64   /** The default language version. Currently Java 8. */
createDefault()65   public static LanguageVersion createDefault() {
66     return create(DEFAULT, DEFAULT, OptionalInt.empty());
67   }
68 
69   private static final int DEFAULT = 8;
70 
71   /** Returns the effective {@code LanguageVersion} for the given list of javac options. */
fromJavacopts(ImmutableList<String> javacopts)72   public static LanguageVersion fromJavacopts(ImmutableList<String> javacopts) {
73     int sourceVersion = DEFAULT;
74     int targetVersion = DEFAULT;
75     OptionalInt release = OptionalInt.empty();
76     Iterator<String> it = javacopts.iterator();
77     while (it.hasNext()) {
78       String option = it.next();
79       switch (option) {
80         case "-source":
81         case "--source":
82           if (!it.hasNext()) {
83             throw new IllegalArgumentException(option + " requires an argument");
84           }
85           sourceVersion = parseVersion(it.next());
86           release = OptionalInt.empty();
87           break;
88         case "-target":
89         case "--target":
90           if (!it.hasNext()) {
91             throw new IllegalArgumentException(option + " requires an argument");
92           }
93           targetVersion = parseVersion(it.next());
94           release = OptionalInt.empty();
95           break;
96         case "--release":
97           if (!it.hasNext()) {
98             throw new IllegalArgumentException(option + " requires an argument");
99           }
100           String value = it.next();
101           Integer n = Ints.tryParse(value);
102           if (n == null) {
103             throw new IllegalArgumentException("invalid --release version: " + value);
104           }
105           release = OptionalInt.of(n);
106           sourceVersion = n;
107           targetVersion = n;
108           break;
109         default:
110           break;
111       }
112     }
113     return create(sourceVersion, targetVersion, release);
114   }
115 
parseVersion(String value)116   private static int parseVersion(String value) {
117     boolean hasPrefix = value.startsWith("1.");
118     Integer version = Ints.tryParse(hasPrefix ? value.substring("1.".length()) : value);
119     if (version == null || !isValidVersion(version, hasPrefix)) {
120       throw new IllegalArgumentException("invalid -source version: " + value);
121     }
122     return version;
123   }
124 
isValidVersion(int version, boolean hasPrefix)125   private static boolean isValidVersion(int version, boolean hasPrefix) {
126     if (version < 5) {
127       // the earliest source version supported by JDK 8 is Java 5
128       return false;
129     }
130     if (hasPrefix && version > 10) {
131       // javac supports legacy `1.*` version numbers for source versions up to Java 10
132       return false;
133     }
134     return true;
135   }
136 }
137