aboutsummaryrefslogtreecommitdiff
path: root/java/com
diff options
context:
space:
mode:
Diffstat (limited to 'java/com')
-rw-r--r--java/com/google/common/escape/SourceCodeEscapers.java8
-rw-r--r--java/com/google/turbine/binder/Binder.java38
-rw-r--r--java/com/google/turbine/binder/CanonicalTypeBinder.java18
-rw-r--r--java/com/google/turbine/binder/ClassPathBinder.java4
-rw-r--r--java/com/google/turbine/binder/CompUnitPreprocessor.java4
-rw-r--r--java/com/google/turbine/binder/ConstBinder.java23
-rw-r--r--java/com/google/turbine/binder/ConstEvaluator.java63
-rw-r--r--java/com/google/turbine/binder/CtSymClassBinder.java47
-rw-r--r--java/com/google/turbine/binder/DisambiguateTypeAnnotations.java4
-rw-r--r--java/com/google/turbine/binder/FileManagerClassBinder.java235
-rw-r--r--java/com/google/turbine/binder/ModuleBinder.java5
-rw-r--r--java/com/google/turbine/binder/Processing.java239
-rw-r--r--java/com/google/turbine/binder/Resolve.java4
-rw-r--r--java/com/google/turbine/binder/TypeBinder.java18
-rw-r--r--java/com/google/turbine/binder/bound/AnnotationMetadata.java2
-rw-r--r--java/com/google/turbine/binder/bound/HeaderBoundClass.java2
-rw-r--r--java/com/google/turbine/binder/bound/TypeBoundClass.java10
-rw-r--r--java/com/google/turbine/binder/bytecode/BytecodeBinder.java4
-rw-r--r--java/com/google/turbine/binder/bytecode/BytecodeBoundClass.java87
-rw-r--r--java/com/google/turbine/binder/bytecode/package-info.java17
-rw-r--r--java/com/google/turbine/binder/env/Env.java6
-rw-r--r--java/com/google/turbine/binder/env/LazyEnv.java12
-rw-r--r--java/com/google/turbine/bytecode/AnnotationWriter.java4
-rw-r--r--java/com/google/turbine/bytecode/Attribute.java18
-rw-r--r--java/com/google/turbine/bytecode/AttributeWriter.java10
-rw-r--r--java/com/google/turbine/bytecode/ClassFile.java11
-rw-r--r--java/com/google/turbine/bytecode/ClassReader.java12
-rw-r--r--java/com/google/turbine/bytecode/ClassWriter.java4
-rw-r--r--java/com/google/turbine/bytecode/LowerAttributes.java7
-rw-r--r--java/com/google/turbine/bytecode/sig/Sig.java4
-rw-r--r--java/com/google/turbine/deps/Dependencies.java4
-rw-r--r--java/com/google/turbine/deps/Transitive.java19
-rw-r--r--java/com/google/turbine/diag/LineMap.java10
-rw-r--r--java/com/google/turbine/diag/TurbineDiagnostic.java6
-rw-r--r--java/com/google/turbine/diag/TurbineError.java7
-rw-r--r--java/com/google/turbine/diag/TurbineLog.java26
-rw-r--r--java/com/google/turbine/lower/Lower.java6
-rw-r--r--java/com/google/turbine/lower/LowerSignature.java14
-rw-r--r--java/com/google/turbine/main/Main.java25
-rw-r--r--java/com/google/turbine/model/TurbineFlag.java4
-rw-r--r--java/com/google/turbine/options/TurbineOptions.java66
-rw-r--r--java/com/google/turbine/options/TurbineOptionsParser.java67
-rw-r--r--java/com/google/turbine/options/package-info.java17
-rw-r--r--java/com/google/turbine/parse/ConstExpressionParser.java30
-rw-r--r--java/com/google/turbine/parse/Parser.java14
-rw-r--r--java/com/google/turbine/parse/StreamLexer.java38
-rw-r--r--java/com/google/turbine/parse/UnicodeEscapePreprocessor.java20
-rw-r--r--java/com/google/turbine/parse/VariableInitializerParser.java10
-rw-r--r--java/com/google/turbine/processing/TurbineAnnotationMirror.java8
-rw-r--r--java/com/google/turbine/processing/TurbineAnnotationProxy.java6
-rw-r--r--java/com/google/turbine/processing/TurbineElement.java26
-rw-r--r--java/com/google/turbine/processing/TurbineElements.java6
-rw-r--r--java/com/google/turbine/processing/TurbineFiler.java3
-rw-r--r--java/com/google/turbine/processing/TurbineProcessingEnvironment.java2
-rw-r--r--java/com/google/turbine/processing/TurbineTypes.java53
-rw-r--r--java/com/google/turbine/type/Type.java9
-rw-r--r--java/com/google/turbine/types/Deannotate.java88
-rw-r--r--java/com/google/turbine/types/Erasure.java15
-rw-r--r--java/com/google/turbine/zip/Zip.java4
59 files changed, 1115 insertions, 408 deletions
diff --git a/java/com/google/common/escape/SourceCodeEscapers.java b/java/com/google/common/escape/SourceCodeEscapers.java
index 4a1aa99..c0f9d6b 100644
--- a/java/com/google/common/escape/SourceCodeEscapers.java
+++ b/java/com/google/common/escape/SourceCodeEscapers.java
@@ -22,7 +22,7 @@ import java.util.Map;
/**
* A factory for Escaper instances used to escape strings for safe use in Java.
*
- * <p>This is a subset of source code escapers that are in the process of being open-sources as part
+ * <p>This is a subset of source code escapers that are in the process of being open-sourced as part
* of guava, see: https://github.com/google/guava/issues/1620
*/
// TODO(cushon): migrate to the guava version once it is open-sourced, and delete this
@@ -43,8 +43,8 @@ public final class SourceCodeEscapers {
* safely be included in either a Java character literal or string literal. This is the preferred
* way to escape Java characters for use in String or character literals.
*
- * <p>See: <a href= "http://java.sun.com/docs/books/jls/third_edition/html/lexical.html#101089"
- * >The Java Language Specification</a> for more details.
+ * <p>See: <a href="https://docs.oracle.com/javase/specs/jls/se14/html/jls-3.html#jls-3.10.6" >The
+ * Java Language Specification</a> for more details.
*/
public static CharEscaper javaCharEscaper() {
return JAVA_CHAR_ESCAPER;
@@ -66,7 +66,7 @@ public final class SourceCodeEscapers {
}
// This escaper does not produce octal escape sequences. See:
- // http://java.sun.com/docs/books/jls/third_edition/html/lexical.html#101089
+ // https://docs.oracle.com/javase/specs/jls/se14/html/jls-3.html#jls-3.10.6
// "Octal escapes are provided for compatibility with C, but can express
// only Unicode values \u0000 through \u00FF, so Unicode escapes are
// usually preferred."
diff --git a/java/com/google/turbine/binder/Binder.java b/java/com/google/turbine/binder/Binder.java
index 0e3f41f..6c828b3 100644
--- a/java/com/google/turbine/binder/Binder.java
+++ b/java/com/google/turbine/binder/Binder.java
@@ -54,6 +54,7 @@ import com.google.turbine.binder.sym.ClassSymbol;
import com.google.turbine.binder.sym.FieldSymbol;
import com.google.turbine.binder.sym.ModuleSymbol;
import com.google.turbine.diag.SourceFile;
+import com.google.turbine.diag.TurbineDiagnostic;
import com.google.turbine.diag.TurbineError;
import com.google.turbine.diag.TurbineError.ErrorKind;
import com.google.turbine.diag.TurbineLog;
@@ -68,7 +69,7 @@ import java.util.Optional;
import javax.annotation.processing.Processor;
/** The entry point for analysis. */
-public class Binder {
+public final class Binder {
/** Binds symbols and types to the given compilation units. */
public static BindingResult bind(
@@ -87,19 +88,28 @@ public class Binder {
ClassPath bootclasspath,
Optional<String> moduleVersion) {
TurbineLog log = new TurbineLog();
- BindingResult br =
- bind(
- log,
- units,
- /* generatedSources= */ ImmutableMap.of(),
- /* generatedClasses= */ ImmutableMap.of(),
- classpath,
- bootclasspath,
- moduleVersion);
- if (!processorInfo.processors().isEmpty() && !units.isEmpty()) {
+ BindingResult br;
+ try {
br =
- Processing.process(
- log, units, classpath, processorInfo, bootclasspath, br, moduleVersion);
+ bind(
+ log,
+ units,
+ /* generatedSources= */ ImmutableMap.of(),
+ /* generatedClasses= */ ImmutableMap.of(),
+ classpath,
+ bootclasspath,
+ moduleVersion);
+ if (!processorInfo.processors().isEmpty() && !units.isEmpty()) {
+ br =
+ Processing.process(
+ log, units, classpath, processorInfo, bootclasspath, br, moduleVersion);
+ }
+ } catch (TurbineError turbineError) {
+ throw new TurbineError(
+ ImmutableList.<TurbineDiagnostic>builder()
+ .addAll(log.diagnostics())
+ .addAll(turbineError.diagnostics())
+ .build());
}
log.maybeThrow();
return br;
@@ -540,4 +550,6 @@ public class Binder {
units, modules, classPathEnv, tli, generatedSources, generatedClasses, statistics);
}
}
+
+ private Binder() {}
}
diff --git a/java/com/google/turbine/binder/CanonicalTypeBinder.java b/java/com/google/turbine/binder/CanonicalTypeBinder.java
index a2f045a..ae82b4f 100644
--- a/java/com/google/turbine/binder/CanonicalTypeBinder.java
+++ b/java/com/google/turbine/binder/CanonicalTypeBinder.java
@@ -38,19 +38,15 @@ import java.util.Map;
/**
* Canonicalizes all qualified types in a {@link SourceTypeBoundClass} using {@link Canonicalize}.
*/
-public class CanonicalTypeBinder {
+public final class CanonicalTypeBinder {
static SourceTypeBoundClass bind(
ClassSymbol sym, SourceTypeBoundClass base, Env<ClassSymbol, TypeBoundClass> env) {
- ClassTy superClassType = null;
- if (base.superClassType() != null && base.superClassType().tyKind() == TyKind.CLASS_TY) {
+ Type superClassType = base.superClassType();
+ if (superClassType != null && superClassType.tyKind() == TyKind.CLASS_TY) {
superClassType =
Canonicalize.canonicalizeClassTy(
- base.source(),
- base.decl().position(),
- env,
- base.owner(),
- (ClassTy) base.superClassType());
+ base.source(), base.decl().position(), env, base.owner(), (ClassTy) superClassType);
}
ImmutableList.Builder<Type> interfaceTypes = ImmutableList.builder();
for (Type i : base.interfaceTypes()) {
@@ -133,9 +129,7 @@ public class CanonicalTypeBinder {
base.defaultValue(),
base.decl(),
base.annotations(),
- base.receiver() != null
- ? param(source, base.decl().position(), env, sym, base.receiver())
- : null));
+ base.receiver() != null ? param(source, pos, env, sym, base.receiver()) : null));
}
return result.build();
}
@@ -181,4 +175,6 @@ public class CanonicalTypeBinder {
}
return result.build();
}
+
+ private CanonicalTypeBinder() {}
}
diff --git a/java/com/google/turbine/binder/ClassPathBinder.java b/java/com/google/turbine/binder/ClassPathBinder.java
index 8aead80..1825c23 100644
--- a/java/com/google/turbine/binder/ClassPathBinder.java
+++ b/java/com/google/turbine/binder/ClassPathBinder.java
@@ -38,7 +38,7 @@ import java.util.LinkedHashMap;
import java.util.Map;
/** Sets up an environment for symbols on the classpath. */
-public class ClassPathBinder {
+public final class ClassPathBinder {
/**
* The prefix for repackaged transitive dependencies; see {@link
@@ -148,4 +148,6 @@ public class ClassPathBinder {
}
});
}
+
+ private ClassPathBinder() {}
}
diff --git a/java/com/google/turbine/binder/CompUnitPreprocessor.java b/java/com/google/turbine/binder/CompUnitPreprocessor.java
index ed70e88..9e9a0bb 100644
--- a/java/com/google/turbine/binder/CompUnitPreprocessor.java
+++ b/java/com/google/turbine/binder/CompUnitPreprocessor.java
@@ -45,7 +45,7 @@ import java.util.Set;
* Processes compilation units before binding, creating symbols for type declarations and desugaring
* access modifiers.
*/
-public class CompUnitPreprocessor {
+public final class CompUnitPreprocessor {
/** A pre-processed compilation unit. */
public static class PreprocessedCompUnit {
@@ -222,4 +222,6 @@ public class CompUnitPreprocessor {
TurbineTyKind.INTERFACE,
/* javadoc= */ null);
}
+
+ private CompUnitPreprocessor() {}
}
diff --git a/java/com/google/turbine/binder/ConstBinder.java b/java/com/google/turbine/binder/ConstBinder.java
index 3a41e94..8511183 100644
--- a/java/com/google/turbine/binder/ConstBinder.java
+++ b/java/com/google/turbine/binder/ConstBinder.java
@@ -16,6 +16,8 @@
package com.google.turbine.binder;
+import static java.util.Objects.requireNonNull;
+
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
@@ -196,6 +198,9 @@ public class ConstBinder {
private static RetentionPolicy bindRetention(AnnoInfo annotation) {
Const value = annotation.values().get("value");
+ if (value == null) {
+ return null;
+ }
if (value.kind() != Kind.ENUM_CONSTANT) {
return null;
}
@@ -208,7 +213,8 @@ public class ConstBinder {
private static ImmutableSet<TurbineElementType> bindTarget(AnnoInfo annotation) {
ImmutableSet.Builder<TurbineElementType> result = ImmutableSet.builder();
- Const val = annotation.values().get("value");
+ // requireNonNull is safe because java.lang.annotation.Target declares `value`.
+ Const val = requireNonNull(annotation.values().get("value"));
switch (val.kind()) {
case ARRAY:
for (Const element : ((ArrayInitValue) val).elements()) {
@@ -227,7 +233,8 @@ public class ConstBinder {
}
private static ClassSymbol bindRepeatable(AnnoInfo annotation) {
- Const value = annotation.values().get("value");
+ // requireNonNull is safe because java.lang.annotation.Repeatable declares `value`.
+ Const value = requireNonNull(annotation.values().get("value"));
if (value.kind() != Kind.CLASS_LITERAL) {
return null;
}
@@ -268,11 +275,12 @@ public class ConstBinder {
if ((base.access() & TurbineFlag.ACC_FINAL) == 0) {
return null;
}
- switch (base.type().tyKind()) {
+ Type type = base.type();
+ switch (type.tyKind()) {
case PRIM_TY:
break;
case CLASS_TY:
- if (((Type.ClassTy) base.type()).sym().equals(ClassSymbol.STRING)) {
+ if (((Type.ClassTy) type).sym().equals(ClassSymbol.STRING)) {
break;
}
// falls through
@@ -280,8 +288,11 @@ public class ConstBinder {
return null;
}
Value value = constantEnv.get(base.sym());
- if (value != null) {
- value = (Value) ConstEvaluator.cast(base.type(), value);
+ if (value == null) {
+ return null;
+ }
+ if (type.tyKind().equals(TyKind.PRIM_TY)) {
+ value = ConstEvaluator.coerce(value, ((Type.PrimTy) type).primkind());
}
return value;
}
diff --git a/java/com/google/turbine/binder/ConstEvaluator.java b/java/com/google/turbine/binder/ConstEvaluator.java
index 9d5f042..bef98a7 100644
--- a/java/com/google/turbine/binder/ConstEvaluator.java
+++ b/java/com/google/turbine/binder/ConstEvaluator.java
@@ -47,6 +47,7 @@ import com.google.turbine.model.Const.ConstCastError;
import com.google.turbine.model.Const.Value;
import com.google.turbine.model.TurbineConstantTypeKind;
import com.google.turbine.model.TurbineFlag;
+import com.google.turbine.model.TurbineTyKind;
import com.google.turbine.tree.Tree;
import com.google.turbine.tree.Tree.ArrayInit;
import com.google.turbine.tree.Tree.Binary;
@@ -126,22 +127,13 @@ public strictfp class ConstEvaluator {
}
switch (a.constantTypeKind()) {
case CHAR:
- return new Const.CharValue(((com.google.turbine.model.Const.CharValue) a).value());
case INT:
- return new Const.IntValue(((com.google.turbine.model.Const.IntValue) a).value());
case LONG:
- return new Const.LongValue(((com.google.turbine.model.Const.LongValue) a).value());
case FLOAT:
- return new Const.FloatValue(((com.google.turbine.model.Const.FloatValue) a).value());
case DOUBLE:
- return new Const.DoubleValue(
- ((com.google.turbine.model.Const.DoubleValue) a).value());
case BOOLEAN:
- return new Const.BooleanValue(
- ((com.google.turbine.model.Const.BooleanValue) a).value());
case STRING:
- return new Const.StringValue(
- ((com.google.turbine.model.Const.StringValue) a).value());
+ return a;
case SHORT:
case BYTE:
case NULL:
@@ -318,20 +310,24 @@ public strictfp class ConstEvaluator {
}
/** Casts the value to the given type. */
- static Const cast(Type ty, Const value) {
+ private Const cast(int position, Type ty, Const value) {
checkNotNull(value);
switch (ty.tyKind()) {
case CLASS_TY:
case TY_VAR:
return value;
case PRIM_TY:
+ if (!value.kind().equals(Const.Kind.PRIMITIVE)) {
+ throw error(position, ErrorKind.EXPRESSION_ERROR);
+ }
return coerce((Const.Value) value, ((Type.PrimTy) ty).primkind());
default:
throw new AssertionError(ty.tyKind());
}
}
- private static Const.Value coerce(Const.Value value, TurbineConstantTypeKind kind) {
+ /** Casts the constant value to the given type. */
+ static Const.Value coerce(Const.Value value, TurbineConstantTypeKind kind) {
switch (kind) {
case BOOLEAN:
return value.asBoolean();
@@ -925,12 +921,16 @@ public strictfp class ConstEvaluator {
if (info.sym() == null) {
return info;
}
-
- Map<String, Type> template = new LinkedHashMap<>();
TypeBoundClass annoClass = env.get(info.sym());
+ if (annoClass.kind() != TurbineTyKind.ANNOTATION) {
+ // we've already reported an error for non-annotation symbols used as annotations,
+ // skip error handling for annotation arguments
+ return info;
+ }
+ Map<String, MethodInfo> template = new LinkedHashMap<>();
if (annoClass != null) {
for (MethodInfo method : annoClass.methods()) {
- template.put(method.name(), method.returnType());
+ template.put(method.name(), method);
}
}
@@ -947,20 +947,28 @@ public strictfp class ConstEvaluator {
key = "value";
expr = arg;
}
- Type ty = template.get(key);
- if (ty == null) {
- throw error(
+ MethodInfo methodInfo = template.remove(key);
+ if (methodInfo == null) {
+ log.error(
arg.position(),
ErrorKind.CANNOT_RESOLVE,
String.format("element %s() in %s", key, info.sym()));
+ continue;
}
- Const value = evalAnnotationValue(expr, ty);
+ Const value = evalAnnotationValue(expr, methodInfo.returnType());
if (value == null) {
- throw error(expr.position(), ErrorKind.EXPRESSION_ERROR);
+ log.error(expr.position(), ErrorKind.EXPRESSION_ERROR);
+ continue;
}
Const existing = values.put(key, value);
if (existing != null) {
- throw error(arg.position(), ErrorKind.INVALID_ANNOTATION_ARGUMENT);
+ log.error(arg.position(), ErrorKind.INVALID_ANNOTATION_ARGUMENT);
+ continue;
+ }
+ }
+ for (MethodInfo methodInfo : template.values()) {
+ if (!methodInfo.hasDefaultValue()) {
+ log.error(info.tree().position(), ErrorKind.MISSING_ANNOTATION_ARGUMENT, methodInfo.name());
}
}
return info.withValues(ImmutableMap.copyOf(values));
@@ -969,8 +977,9 @@ public strictfp class ConstEvaluator {
private TurbineAnnotationValue evalAnno(Tree.Anno t) {
LookupResult result = scope.lookup(new LookupKey(t.name()));
if (result == null) {
- throw error(
+ log.error(
t.name().get(0).position(), ErrorKind.CANNOT_RESOLVE, Joiner.on(".").join(t.name()));
+ return null;
}
ClassSymbol sym = (ClassSymbol) result.sym();
for (Ident name : result.remaining()) {
@@ -982,6 +991,9 @@ public strictfp class ConstEvaluator {
if (sym == null) {
return null;
}
+ if (env.get(sym).kind() != TurbineTyKind.ANNOTATION) {
+ log.error(t.position(), ErrorKind.NOT_AN_ANNOTATION, sym);
+ }
AnnoInfo annoInfo = evaluateAnnotation(new AnnoInfo(source, sym, t, ImmutableMap.of()));
return new TurbineAnnotationValue(annoInfo);
}
@@ -1004,7 +1016,8 @@ public strictfp class ConstEvaluator {
}
Const value = eval(tree);
if (value == null) {
- throw error(tree.position(), ErrorKind.EXPRESSION_ERROR);
+ log.error(tree.position(), ErrorKind.EXPRESSION_ERROR);
+ return null;
}
switch (ty.tyKind()) {
case PRIM_TY:
@@ -1024,7 +1037,7 @@ public strictfp class ConstEvaluator {
: ImmutableList.of(value);
ImmutableList.Builder<Const> coerced = ImmutableList.builder();
for (Const element : elements) {
- coerced.add(cast(elementType, element));
+ coerced.add(cast(tree.position(), elementType, element));
}
return new Const.ArrayInitValue(coerced.build());
}
@@ -1043,7 +1056,7 @@ public strictfp class ConstEvaluator {
if (value == null || value.kind() != Const.Kind.PRIMITIVE) {
return null;
}
- return (Const.Value) cast(type, value);
+ return (Const.Value) cast(expression.position(), type, value);
} catch (TurbineError error) {
for (TurbineDiagnostic diagnostic : error.diagnostics()) {
switch (diagnostic.kind()) {
diff --git a/java/com/google/turbine/binder/CtSymClassBinder.java b/java/com/google/turbine/binder/CtSymClassBinder.java
index a6f1b3d..1d7ece7 100644
--- a/java/com/google/turbine/binder/CtSymClassBinder.java
+++ b/java/com/google/turbine/binder/CtSymClassBinder.java
@@ -16,11 +16,15 @@
package com.google.turbine.binder;
+import static com.google.common.base.Ascii.toUpperCase;
import static com.google.common.base.StandardSystemProperty.JAVA_HOME;
+import static java.util.Objects.requireNonNull;
+import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Supplier;
import com.google.common.base.Suppliers;
import com.google.common.collect.ImmutableMap;
+import com.google.common.primitives.Ints;
import com.google.turbine.binder.bound.ModuleInfo;
import com.google.turbine.binder.bytecode.BytecodeBinder;
import com.google.turbine.binder.bytecode.BytecodeBoundClass;
@@ -32,6 +36,7 @@ import com.google.turbine.binder.sym.ClassSymbol;
import com.google.turbine.binder.sym.ModuleSymbol;
import com.google.turbine.zip.Zip;
import java.io.IOException;
+import java.lang.reflect.Method;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
@@ -40,12 +45,13 @@ import java.util.Map;
import org.checkerframework.checker.nullness.qual.Nullable;
/** Constructs a platform {@link ClassPath} from the current JDK's ct.sym file. */
-public class CtSymClassBinder {
+public final class CtSymClassBinder {
@Nullable
public static ClassPath bind(String version) throws IOException {
- Path javaHome = Paths.get(JAVA_HOME.value());
- Path ctSym = javaHome.resolve("lib/ct.sym");
+ String javaHome = JAVA_HOME.value();
+ requireNonNull(javaHome, "attempted to use --release, but JAVA_HOME is not set");
+ Path ctSym = Paths.get(javaHome).resolve("lib/ct.sym");
if (!Files.exists(ctSym)) {
throw new IllegalStateException("lib/ct.sym does not exist in " + javaHome);
}
@@ -59,7 +65,9 @@ public class CtSymClassBinder {
}
};
// ct.sym contains directories whose names are the concatentation of a list of target versions
- // (e.g. 789) and which contain interface class files with a .sig extension.
+ // formatted as a single character 0-9 or A-Z (e.g. 789A) and which contain interface class
+ // files with a .sig extension.
+ String releaseString = formatReleaseVersion(version);
for (Zip.Entry ze : new Zip.ZipIterable(ctSym)) {
String name = ze.name();
if (!name.endsWith(".sig")) {
@@ -70,10 +78,13 @@ public class CtSymClassBinder {
continue;
}
// check if the directory matches the desired release
- // TODO(cushon): what happens when version numbers contain more than one digit?
- if (!ze.name().substring(0, idx).contains(version)) {
+ if (!ze.name().substring(0, idx).contains(releaseString)) {
continue;
}
+ if (isAtLeastJDK12()) {
+ // JDK >= 12 includes the module name as a prefix
+ idx = name.indexOf('/', idx + 1);
+ }
if (name.substring(name.lastIndexOf('/') + 1).equals("module-info.sig")) {
ModuleInfo moduleInfo = BytecodeBinder.bindModuleInfo(name, toByteArrayOrDie(ze));
modules.put(new ModuleSymbol(moduleInfo.name()), moduleInfo);
@@ -122,4 +133,28 @@ public class CtSymClassBinder {
}
});
}
+
+ @VisibleForTesting
+ static String formatReleaseVersion(String version) {
+ Integer n = Ints.tryParse(version);
+ if (n == null || n <= 4 || n >= 36) {
+ throw new IllegalArgumentException("invalid release version: " + version);
+ }
+ return toUpperCase(Integer.toString(n, 36));
+ }
+
+ private static boolean isAtLeastJDK12() {
+ int major;
+ try {
+ Method versionMethod = Runtime.class.getMethod("version");
+ Object version = versionMethod.invoke(null);
+ major = (int) version.getClass().getMethod("major").invoke(version);
+ } catch (ReflectiveOperationException e) {
+ // `Runtime.version()` was added in JDK 9
+ return false;
+ }
+ return major >= 12;
+ }
+
+ private CtSymClassBinder() {}
}
diff --git a/java/com/google/turbine/binder/DisambiguateTypeAnnotations.java b/java/com/google/turbine/binder/DisambiguateTypeAnnotations.java
index 7e3fbda..c5de8c1 100644
--- a/java/com/google/turbine/binder/DisambiguateTypeAnnotations.java
+++ b/java/com/google/turbine/binder/DisambiguateTypeAnnotations.java
@@ -65,7 +65,7 @@ import java.util.Map;
* constant binding is done, read the {@code @Target} meta-annotation for each ambiguous annotation,
* and move it to the appropriate location.
*/
-public class DisambiguateTypeAnnotations {
+public final class DisambiguateTypeAnnotations {
public static SourceTypeBoundClass bind(
SourceTypeBoundClass base, Env<ClassSymbol, TypeBoundClass> env) {
return new SourceTypeBoundClass(
@@ -317,4 +317,6 @@ public class DisambiguateTypeAnnotations {
}
return false;
}
+
+ private DisambiguateTypeAnnotations() {}
}
diff --git a/java/com/google/turbine/binder/FileManagerClassBinder.java b/java/com/google/turbine/binder/FileManagerClassBinder.java
new file mode 100644
index 0000000..42a8162
--- /dev/null
+++ b/java/com/google/turbine/binder/FileManagerClassBinder.java
@@ -0,0 +1,235 @@
+/*
+ * Copyright 2020 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.turbine.binder;
+
+import com.google.common.base.Joiner;
+import com.google.common.base.Supplier;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.io.ByteStreams;
+import com.google.turbine.binder.bound.ModuleInfo;
+import com.google.turbine.binder.bytecode.BytecodeBoundClass;
+import com.google.turbine.binder.env.Env;
+import com.google.turbine.binder.env.SimpleEnv;
+import com.google.turbine.binder.lookup.LookupKey;
+import com.google.turbine.binder.lookup.LookupResult;
+import com.google.turbine.binder.lookup.PackageScope;
+import com.google.turbine.binder.lookup.Scope;
+import com.google.turbine.binder.lookup.TopLevelIndex;
+import com.google.turbine.binder.sym.ClassSymbol;
+import com.google.turbine.binder.sym.ModuleSymbol;
+import java.io.IOException;
+import java.io.UncheckedIOException;
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.Map;
+import javax.tools.FileObject;
+import javax.tools.JavaFileObject;
+import javax.tools.StandardJavaFileManager;
+import javax.tools.StandardLocation;
+import org.checkerframework.checker.nullness.qual.Nullable;
+
+/**
+ * Binds a {@link StandardJavaFileManager} to an {@link ClassPath}. This can be used to share a
+ * filemanager (and associated IO costs) between turbine and javac when running both in the same
+ * process.
+ */
+public final class FileManagerClassBinder {
+
+ public static ClassPath adapt(StandardJavaFileManager fileManager, StandardLocation location) {
+ PackageLookup packageLookup = new PackageLookup(fileManager, location);
+ Env<ClassSymbol, BytecodeBoundClass> env =
+ new Env<ClassSymbol, BytecodeBoundClass>() {
+ @Override
+ public BytecodeBoundClass get(ClassSymbol sym) {
+ return packageLookup.getPackage(this, sym.packageName()).get(sym);
+ }
+ };
+ SimpleEnv<ModuleSymbol, ModuleInfo> moduleEnv = new SimpleEnv<>(ImmutableMap.of());
+ TopLevelIndex tli = new FileManagerTopLevelIndex(env, packageLookup);
+ return new ClassPath() {
+ @Override
+ public Env<ClassSymbol, BytecodeBoundClass> env() {
+ return env;
+ }
+
+ @Override
+ public Env<ModuleSymbol, ModuleInfo> moduleEnv() {
+ return moduleEnv;
+ }
+
+ @Override
+ public TopLevelIndex index() {
+ return tli;
+ }
+
+ @Override
+ public Supplier<byte[]> resource(String path) {
+ return packageLookup.resource(path);
+ }
+ };
+ }
+
+ private static class PackageLookup {
+
+ private final Map<String, Map<ClassSymbol, BytecodeBoundClass>> packages = new HashMap<>();
+ private final StandardJavaFileManager fileManager;
+ private final StandardLocation location;
+
+ private PackageLookup(StandardJavaFileManager fileManager, StandardLocation location) {
+ this.fileManager = fileManager;
+ this.location = location;
+ }
+
+ private ImmutableMap<ClassSymbol, BytecodeBoundClass> listPackage(
+ Env<ClassSymbol, BytecodeBoundClass> env, String packageName) throws IOException {
+ Map<ClassSymbol, BytecodeBoundClass> result = new HashMap<>();
+ for (JavaFileObject jfo :
+ fileManager.list(
+ location,
+ packageName.replace('/', '.'),
+ EnumSet.of(JavaFileObject.Kind.CLASS),
+ false)) {
+ String binaryName = fileManager.inferBinaryName(location, jfo);
+ ClassSymbol sym = new ClassSymbol(binaryName.replace('.', '/'));
+ result.putIfAbsent(
+ sym,
+ new BytecodeBoundClass(
+ sym,
+ new Supplier<byte[]>() {
+ @Override
+ public byte[] get() {
+ try {
+ return ByteStreams.toByteArray(jfo.openInputStream());
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ }
+ },
+ env,
+ /* jarFile= */ null));
+ }
+ return ImmutableMap.copyOf(result);
+ }
+
+ private Map<ClassSymbol, BytecodeBoundClass> getPackage(
+ Env<ClassSymbol, BytecodeBoundClass> env, String key) {
+ return packages.computeIfAbsent(
+ key,
+ k -> {
+ try {
+ return listPackage(env, key);
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ });
+ }
+
+ public Supplier<byte[]> resource(String resource) {
+ String dir;
+ String name;
+ int idx = resource.lastIndexOf('/');
+ if (idx != -1) {
+ dir = resource.substring(0, idx + 1);
+ name = resource.substring(idx + 1, resource.length());
+ } else {
+ dir = "";
+ name = resource;
+ }
+ FileObject fileObject;
+ try {
+ fileObject = fileManager.getFileForInput(location, dir, name);
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ if (fileObject == null) {
+ return null;
+ }
+ return new Supplier<byte[]>() {
+ @Override
+ public byte[] get() {
+ try {
+ return ByteStreams.toByteArray(fileObject.openInputStream());
+ } catch (IOException e) {
+ throw new UncheckedIOException(e);
+ }
+ }
+ };
+ }
+ }
+
+ private static class FileManagerTopLevelIndex implements TopLevelIndex {
+ private final Env<ClassSymbol, BytecodeBoundClass> env;
+ private final PackageLookup packageLookup;
+
+ public FileManagerTopLevelIndex(
+ Env<ClassSymbol, BytecodeBoundClass> env, PackageLookup packageLookup) {
+ this.env = env;
+ this.packageLookup = packageLookup;
+ }
+
+ @Override
+ public Scope scope() {
+ return new Scope() {
+ @Override
+ public @Nullable LookupResult lookup(LookupKey lookupKey) {
+ for (int i = lookupKey.simpleNames().size(); i > 0; i--) {
+ String p = Joiner.on('/').join(lookupKey.simpleNames().subList(0, i));
+ ClassSymbol sym = new ClassSymbol(p);
+ BytecodeBoundClass r = env.get(sym);
+ if (r != null) {
+ return new LookupResult(
+ sym,
+ new LookupKey(
+ lookupKey.simpleNames().subList(i - 1, lookupKey.simpleNames().size())));
+ }
+ }
+ return null;
+ }
+ };
+ }
+
+ @Override
+ public PackageScope lookupPackage(Iterable<String> names) {
+ String packageName = Joiner.on('/').join(names);
+ Map<ClassSymbol, BytecodeBoundClass> pkg = packageLookup.getPackage(env, packageName);
+ if (pkg.isEmpty()) {
+ return null;
+ }
+ return new PackageScope() {
+ @Override
+ public Iterable<ClassSymbol> classes() {
+ return pkg.keySet();
+ }
+
+ @Override
+ public @Nullable LookupResult lookup(LookupKey lookupKey) {
+ String className = lookupKey.first().value();
+ if (!packageName.isEmpty()) {
+ className = packageName + "/" + className;
+ }
+ ClassSymbol sym = new ClassSymbol(className);
+ if (!pkg.containsKey(sym)) {
+ return null;
+ }
+ return new LookupResult(sym, lookupKey);
+ }
+ };
+ }
+ }
+
+ private FileManagerClassBinder() {}
+}
diff --git a/java/com/google/turbine/binder/ModuleBinder.java b/java/com/google/turbine/binder/ModuleBinder.java
index 748ff39..04ce81d 100644
--- a/java/com/google/turbine/binder/ModuleBinder.java
+++ b/java/com/google/turbine/binder/ModuleBinder.java
@@ -216,11 +216,12 @@ public class ModuleBinder {
}
ClassSymbol sym = (ClassSymbol) result.sym();
for (Tree.Ident name : result.remaining()) {
- sym = Resolve.resolve(env, /* origin= */ null, sym, name);
- if (sym == null) {
+ ClassSymbol next = Resolve.resolve(env, /* origin= */ null, sym, name);
+ if (next == null) {
throw error(
ErrorKind.SYMBOL_NOT_FOUND, pos, new ClassSymbol(sym.binaryName() + '$' + name));
}
+ sym = next;
}
return sym;
}
diff --git a/java/com/google/turbine/binder/Processing.java b/java/com/google/turbine/binder/Processing.java
index ecdf195..16407aa 100644
--- a/java/com/google/turbine/binder/Processing.java
+++ b/java/com/google/turbine/binder/Processing.java
@@ -16,7 +16,10 @@
package com.google.turbine.binder;
+import static java.util.Objects.requireNonNull;
+
import com.google.auto.value.AutoValue;
+import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Function;
import com.google.common.base.Joiner;
import com.google.common.base.Stopwatch;
@@ -27,6 +30,7 @@ import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSetMultimap;
import com.google.common.collect.Sets;
+import com.google.common.primitives.Ints;
import com.google.turbine.binder.Binder.BindingResult;
import com.google.turbine.binder.Binder.Statistics;
import com.google.turbine.binder.bound.SourceTypeBoundClass;
@@ -56,7 +60,6 @@ import java.nio.file.Paths;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Collection;
-import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
@@ -76,6 +79,7 @@ import org.checkerframework.checker.nullness.qual.Nullable;
/** Top level annotation processing logic, see also {@link Binder}. */
public class Processing {
+ @Nullable
static BindingResult process(
TurbineLog log,
final ImmutableList<CompUnit> initialSources,
@@ -131,19 +135,13 @@ public class Processing {
try (Timers.Timer unused = timers.start(processor)) {
processor.init(processingEnv);
} catch (Throwable t) {
- reportProcessorCrash(log, processor, t);
+ logProcessorCrash(log, processor, t);
+ return null;
}
}
- Map<Processor, Pattern> wanted = new HashMap<>();
- for (Processor processor : processorInfo.processors()) {
- List<String> patterns = new ArrayList<>();
- for (String supportedAnnotationType : processor.getSupportedAnnotationTypes()) {
- // TODO(b/139026291): this handling of getSupportedAnnotationTypes isn't correct
- patterns.add(supportedAnnotationType.replace("*", ".*"));
- }
- wanted.put(processor, Pattern.compile(Joiner.on('|').join(patterns)));
- }
+ ImmutableMap<Processor, SupportedAnnotationTypes> wanted =
+ initializeSupportedAnnotationTypes(processorInfo);
Set<ClassSymbol> allSymbols = new HashSet<>();
@@ -163,12 +161,14 @@ public class Processing {
}
ImmutableSetMultimap<ClassSymbol, Symbol> allAnnotations = getAllAnnotations(env, syms);
TurbineRoundEnvironment roundEnv = null;
- for (Processor processor : processorInfo.processors()) {
+ for (Map.Entry<Processor, SupportedAnnotationTypes> e : wanted.entrySet()) {
+ Processor processor = e.getKey();
+ SupportedAnnotationTypes supportedAnnotationTypes = e.getValue();
Set<TypeElement> annotations = new HashSet<>();
- Pattern pattern = wanted.get(processor);
- boolean run = toRun.contains(processor);
+ boolean run = supportedAnnotationTypes.everything() || toRun.contains(processor);
for (ClassSymbol a : allAnnotations.keys()) {
- if (pattern.matcher(a.toString()).matches()) {
+ if (supportedAnnotationTypes.everything()
+ || supportedAnnotationTypes.pattern().matcher(a.toString()).matches()) {
annotations.add(factory.typeElement(a));
run = true;
}
@@ -184,7 +184,8 @@ public class Processing {
// TODO(cushon): consider disallowing this, or reporting a diagnostic
processor.process(annotations, roundEnv);
} catch (Throwable t) {
- reportProcessorCrash(log, processor, t);
+ logProcessorCrash(log, processor, t);
+ return null;
}
}
}
@@ -197,7 +198,7 @@ public class Processing {
}
errorRaised = log.errorRaised();
if (errorRaised) {
- log.maybeThrow();
+ break;
}
log.clear();
result =
@@ -228,7 +229,8 @@ public class Processing {
try (Timers.Timer unused = timers.start(processor)) {
processor.process(ImmutableSet.of(), roundEnv);
} catch (Throwable t) {
- reportProcessorCrash(log, processor, t);
+ logProcessorCrash(log, processor, t);
+ return null;
}
}
@@ -249,7 +251,9 @@ public class Processing {
classpath,
bootclasspath,
moduleVersion);
- log.maybeThrow();
+ if (log.anyErrors()) {
+ return null;
+ }
}
if (!filer.generatedClasses().isEmpty()) {
@@ -267,13 +271,44 @@ public class Processing {
return result;
}
- private static void reportProcessorCrash(TurbineLog log, Processor processor, Throwable t) {
+ private static ImmutableMap<Processor, SupportedAnnotationTypes>
+ initializeSupportedAnnotationTypes(ProcessorInfo processorInfo) {
+ ImmutableMap.Builder<Processor, SupportedAnnotationTypes> result = ImmutableMap.builder();
+ for (Processor processor : processorInfo.processors()) {
+ result.put(processor, SupportedAnnotationTypes.create(processor));
+ }
+ return result.build();
+ }
+
+ @AutoValue
+ abstract static class SupportedAnnotationTypes {
+
+ abstract boolean everything();
+
+ abstract Pattern pattern();
+
+ static SupportedAnnotationTypes create(Processor processor) {
+ List<String> patterns = new ArrayList<>();
+ boolean everything = false;
+ for (String supportedAnnotationType : processor.getSupportedAnnotationTypes()) {
+ if (supportedAnnotationType.equals("*")) {
+ everything = true;
+ } else {
+ // TODO(b/139026291): this handling of getSupportedAnnotationTypes isn't correct
+ patterns.add(supportedAnnotationType);
+ }
+ }
+ return new AutoValue_Processing_SupportedAnnotationTypes(
+ everything, Pattern.compile(Joiner.on('|').join(patterns)));
+ }
+ }
+
+ private static void logProcessorCrash(TurbineLog log, Processor processor, Throwable t) {
log.diagnostic(
Diagnostic.Kind.ERROR,
String.format(
"An exception occurred in %s:\n%s",
processor.getClass().getCanonicalName(), Throwables.getStackTraceAsString(t)));
- log.maybeThrow();
}
/** Returns a map from annotations present in the compilation to the annotated elements. */
@@ -313,7 +348,7 @@ public class Processing {
}
// TODO(cushon): consider memoizing this (or isAnnotationInherited) if they show up in profiles
- private static Set<ClassSymbol> inheritedAnnotations(
+ private static ImmutableSet<ClassSymbol> inheritedAnnotations(
Set<ClassSymbol> seen, ClassSymbol sym, Env<ClassSymbol, TypeBoundClass> env) {
ImmutableSet.Builder<ClassSymbol> result = ImmutableSet.builder();
ClassSymbol curr = sym;
@@ -360,87 +395,110 @@ public class Processing {
public static ProcessorInfo initializeProcessors(
ImmutableList<String> javacopts,
- ImmutableList<String> processorPath,
ImmutableSet<String> processorNames,
- ImmutableSet<String> builtinProcessors)
- throws MalformedURLException {
- ClassLoader processorLoader = null;
+ ClassLoader processorLoader) {
+ if (processorNames.isEmpty() || javacopts.contains("-proc:none")) {
+ return ProcessorInfo.empty();
+ }
+ ImmutableList<Processor> processors = instantiateProcessors(processorNames, processorLoader);
+ ImmutableMap<String, String> processorOptions = processorOptions(javacopts);
+ SourceVersion sourceVersion = parseSourceVersion(javacopts);
+ return ProcessorInfo.create(processors, processorLoader, processorOptions, sourceVersion);
+ }
+
+ private static ImmutableList<Processor> instantiateProcessors(
+ ImmutableSet<String> processorNames, ClassLoader processorLoader) {
ImmutableList.Builder<Processor> processors = ImmutableList.builder();
- ImmutableMap<String, String> processorOptions;
- if (!processorNames.isEmpty() && !javacopts.contains("-proc:none")) {
- if (!processorPath.isEmpty()) {
- processorLoader =
- new URLClassLoader(
- toUrls(processorPath),
- new ClassLoader(getPlatformClassLoader()) {
- @Override
- protected Class<?> findClass(String name) throws ClassNotFoundException {
- if (name.startsWith("com.sun.source.")
- || name.startsWith("com.sun.tools.")
- || name.startsWith("com.google.common.collect.")
- || name.startsWith("com.google.common.base.")
- || name.startsWith("com.google.common.graph.")
- || name.startsWith("com.google.devtools.build.buildjar.javac.statistics.")
- || name.startsWith("dagger.model.")
- || name.startsWith("dagger.spi.")
- || name.equals("com.google.turbine.processing.TurbineProcessingEnvironment")
- || builtinProcessors.contains(name)) {
- return Class.forName(name);
- }
- throw new ClassNotFoundException(name);
- }
- });
- } else {
- processorLoader = Processing.class.getClassLoader();
- }
- for (String processor : processorNames) {
- try {
- Class<? extends Processor> clazz =
- Class.forName(processor, false, processorLoader).asSubclass(Processor.class);
- processors.add(clazz.getConstructor().newInstance());
- } catch (ReflectiveOperationException e) {
- throw new LinkageError(e.getMessage(), e);
- }
+ for (String processor : processorNames) {
+ try {
+ Class<? extends Processor> clazz =
+ Class.forName(processor, false, processorLoader).asSubclass(Processor.class);
+ processors.add(clazz.getConstructor().newInstance());
+ } catch (ReflectiveOperationException e) {
+ throw new LinkageError(e.getMessage(), e);
}
- processorOptions = processorOptions(javacopts);
- } else {
- processorOptions = ImmutableMap.of();
}
+ return processors.build();
+ }
+
+ public static ClassLoader processorLoader(
+ ImmutableList<String> processorPath, ImmutableSet<String> builtinProcessors)
+ throws MalformedURLException {
+ if (processorPath.isEmpty()) {
+ return Processing.class.getClassLoader();
+ }
+ return new URLClassLoader(
+ toUrls(processorPath),
+ new ClassLoader(getPlatformClassLoader()) {
+ @Override
+ protected Class<?> findClass(String name) throws ClassNotFoundException {
+ if (name.equals("com.google.turbine.processing.TurbineProcessingEnvironment")) {
+ return Class.forName(name);
+ }
+ if (!builtinProcessors.isEmpty()) {
+ if (name.startsWith("com.sun.source.")
+ || name.startsWith("com.sun.tools.")
+ || name.startsWith("com.google.common.collect.")
+ || name.startsWith("com.google.common.base.")
+ || name.startsWith("com.google.common.graph.")
+ || name.startsWith("com.google.devtools.build.buildjar.javac.statistics.")
+ || name.startsWith("dagger.model.")
+ || name.startsWith("dagger.spi.")
+ || builtinProcessors.contains(name)) {
+ return Class.forName(name);
+ }
+ }
+ throw new ClassNotFoundException(name);
+ }
+ });
+ }
+
+ @VisibleForTesting
+ static SourceVersion parseSourceVersion(ImmutableList<String> javacopts) {
SourceVersion sourceVersion = SourceVersion.latestSupported();
Iterator<String> it = javacopts.iterator();
while (it.hasNext()) {
String option = it.next();
switch (option) {
- case "-target":
- if (it.hasNext()) {
- String value = it.next();
- switch (value) {
- case "5":
- case "1.5":
- sourceVersion = SourceVersion.RELEASE_5;
- break;
- case "6":
- case "1.6":
- sourceVersion = SourceVersion.RELEASE_6;
- break;
- case "7":
- case "1.7":
- sourceVersion = SourceVersion.RELEASE_7;
- break;
- case "8":
- sourceVersion = SourceVersion.RELEASE_8;
- break;
- default:
- break;
- }
+ case "-source":
+ if (!it.hasNext()) {
+ throw new IllegalArgumentException("-source requires an argument");
}
+ sourceVersion = parseSourceVersion(it.next());
break;
default:
break;
}
}
- return ProcessorInfo.create(
- processors.build(), processorLoader, processorOptions, sourceVersion);
+ return sourceVersion;
+ }
+
+ private static SourceVersion parseSourceVersion(String value) {
+ boolean hasPrefix = value.startsWith("1.");
+ Integer version = Ints.tryParse(hasPrefix ? value.substring("1.".length()) : value);
+ if (!isValidSourceVersion(version, hasPrefix)) {
+ throw new IllegalArgumentException("invalid -source version: " + value);
+ }
+ try {
+ return SourceVersion.valueOf("RELEASE_" + version);
+ } catch (IllegalArgumentException unused) {
+ throw new IllegalArgumentException("invalid -source version: " + value);
+ }
+ }
+
+ private static boolean isValidSourceVersion(Integer version, boolean hasPrefix) {
+ if (version == null) {
+ return false;
+ }
+ if (version < 5) {
+ // the earliest source version supported by JDK 8 is Java 5
+ return false;
+ }
+ if (hasPrefix && version > 10) {
+ // javac supports legacy `1.*` version numbers for source versions up to Java 10
+ return false;
+ }
+ return true;
}
private static URL[] toUrls(ImmutableList<String> processorPath) throws MalformedURLException {
@@ -548,9 +606,12 @@ public class Processing {
ImmutableMap<String, Duration> build() {
ImmutableMap.Builder<String, Duration> result = ImmutableMap.builder();
for (Map.Entry<Class<?>, Stopwatch> e : processorTimers.entrySet()) {
- result.put(e.getKey().getCanonicalName(), e.getValue().elapsed());
+ // requireNonNull is safe, barring bizarre processor implementations (e.g., anonymous class)
+ result.put(requireNonNull(e.getKey().getCanonicalName()), e.getValue().elapsed());
}
return result.build();
}
}
+
+ private Processing() {}
}
diff --git a/java/com/google/turbine/binder/Resolve.java b/java/com/google/turbine/binder/Resolve.java
index 28a8be3..66e1036 100644
--- a/java/com/google/turbine/binder/Resolve.java
+++ b/java/com/google/turbine/binder/Resolve.java
@@ -33,7 +33,7 @@ import java.util.Objects;
import java.util.Set;
/** Qualified name resolution. */
-public class Resolve {
+public final class Resolve {
/**
* Performs JLS 6.5.5.2 qualified type name resolution of a type with the given simple name,
@@ -213,4 +213,6 @@ public class Resolve {
}
throw new AssertionError(visibility);
}
+
+ private Resolve() {}
}
diff --git a/java/com/google/turbine/binder/TypeBinder.java b/java/com/google/turbine/binder/TypeBinder.java
index 7b01856..a28acd9 100644
--- a/java/com/google/turbine/binder/TypeBinder.java
+++ b/java/com/google/turbine/binder/TypeBinder.java
@@ -16,6 +16,8 @@
package com.google.turbine.binder;
+import static java.util.Objects.requireNonNull;
+
import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
@@ -54,6 +56,7 @@ import com.google.turbine.tree.TurbineModifier;
import com.google.turbine.type.AnnoInfo;
import com.google.turbine.type.Type;
import com.google.turbine.type.Type.IntersectionTy;
+import com.google.turbine.types.Deannotate;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.HashSet;
@@ -394,7 +397,8 @@ public class TypeBinder {
ImmutableList<Tree.TyParam> trees, CompoundScope scope, Map<String, TyVarSymbol> symbols) {
ImmutableMap.Builder<TyVarSymbol, TyVarInfo> result = ImmutableMap.builder();
for (Tree.TyParam tree : trees) {
- TyVarSymbol sym = symbols.get(tree.name().value());
+ // `symbols` is constructed to guarantee the requireNonNull call is safe.
+ TyVarSymbol sym = requireNonNull(symbols.get(tree.name().value()));
ImmutableList.Builder<Type> bounds = ImmutableList.builder();
for (Tree bound : tree.bounds()) {
bounds.add(bindTy(scope, bound));
@@ -493,6 +497,9 @@ public class TypeBinder {
== 0) {
access |= TurbineFlag.ACC_ABSTRACT;
}
+ if ((access & TurbineFlag.ACC_FINAL) == TurbineFlag.ACC_FINAL) {
+ log.error(t.position(), ErrorKind.UNEXPECTED_MODIFIER, TurbineModifier.FINAL);
+ }
break;
case ENUM:
if (name.equals("<init>")) {
@@ -618,7 +625,14 @@ public class TypeBinder {
case WILD_TY:
return bindWildTy(scope, (Tree.WildTy) ty);
default:
- return bindTy(scope, ty);
+ Type result = bindTy(scope, ty);
+ if (result.tyKind().equals(Type.TyKind.PRIM_TY)) {
+ // Omit type annotations when printing the type in the diagnostic, since they're
+ // irrelevant and could be invalid if there were deferred errors.
+ // TODO(cushon): consider ensuring this is done for all diagnostics that mention types
+ log.error(ty.position(), ErrorKind.UNEXPECTED_TYPE, Deannotate.deannotate(result));
+ }
+ return result;
}
}
diff --git a/java/com/google/turbine/binder/bound/AnnotationMetadata.java b/java/com/google/turbine/binder/bound/AnnotationMetadata.java
index 31860b6..a4d3037 100644
--- a/java/com/google/turbine/binder/bound/AnnotationMetadata.java
+++ b/java/com/google/turbine/binder/bound/AnnotationMetadata.java
@@ -25,7 +25,7 @@ import java.lang.annotation.RetentionPolicy;
import java.util.EnumSet;
/**
- * Annotation metadata, e.g. from {@link @java.lang.annotation.Target}, {@link
+ * Annotation metadata, e.g. from {@link java.lang.annotation.Target}, {@link
* java.lang.annotation.Retention}, and {@link java.lang.annotation.Repeatable}.
*/
public class AnnotationMetadata {
diff --git a/java/com/google/turbine/binder/bound/HeaderBoundClass.java b/java/com/google/turbine/binder/bound/HeaderBoundClass.java
index 14807bb..7aeb3d8 100644
--- a/java/com/google/turbine/binder/bound/HeaderBoundClass.java
+++ b/java/com/google/turbine/binder/bound/HeaderBoundClass.java
@@ -30,5 +30,5 @@ public interface HeaderBoundClass extends BoundClass {
ImmutableList<ClassSymbol> interfaces();
/** Declared type parameters. */
- public ImmutableMap<String, TyVarSymbol> typeParameters();
+ ImmutableMap<String, TyVarSymbol> typeParameters();
}
diff --git a/java/com/google/turbine/binder/bound/TypeBoundClass.java b/java/com/google/turbine/binder/bound/TypeBoundClass.java
index e8933ac..99d15bb 100644
--- a/java/com/google/turbine/binder/bound/TypeBoundClass.java
+++ b/java/com/google/turbine/binder/bound/TypeBoundClass.java
@@ -51,7 +51,7 @@ public interface TypeBoundClass extends HeaderBoundClass {
ImmutableList<MethodInfo> methods();
/**
- * Annotation metadata, e.g. from {@link @java.lang.annotation.Target}, {@link
+ * Annotation metadata, e.g. from {@link java.lang.annotation.Target}, {@link
* java.lang.annotation.Retention}, and {@link java.lang.annotation.Repeatable}.
*/
AnnotationMetadata annotationMetadata();
@@ -229,6 +229,14 @@ public interface TypeBoundClass extends HeaderBoundClass {
return defaultValue;
}
+ /**
+ * Returns true for annotation members with a default value. The default value may not have been
+ * bound yet, in which case {@link #defaultValue} may still return {@code null}.
+ */
+ public boolean hasDefaultValue() {
+ return decl() != null ? decl().defaultValue().isPresent() : defaultValue() != null;
+ }
+
/** The declaration. */
public MethDecl decl() {
return decl;
diff --git a/java/com/google/turbine/binder/bytecode/BytecodeBinder.java b/java/com/google/turbine/binder/bytecode/BytecodeBinder.java
index 66d4cf0..0f4bac1 100644
--- a/java/com/google/turbine/binder/bytecode/BytecodeBinder.java
+++ b/java/com/google/turbine/binder/bytecode/BytecodeBinder.java
@@ -49,7 +49,7 @@ import java.util.function.Function;
import java.util.function.Supplier;
/** Bind {@link Type}s from bytecode. */
-public class BytecodeBinder {
+public final class BytecodeBinder {
static Type.ClassTy bindClassTy(Sig.ClassTySig sig, Function<String, TyVarSymbol> scope) {
StringBuilder sb = new StringBuilder(sig.pkg());
@@ -212,4 +212,6 @@ public class BytecodeBinder {
/* uses= */ ImmutableList.of(),
/* provides= */ ImmutableList.of());
}
+
+ private BytecodeBinder() {}
}
diff --git a/java/com/google/turbine/binder/bytecode/BytecodeBoundClass.java b/java/com/google/turbine/binder/bytecode/BytecodeBoundClass.java
index b992643..82cefc1 100644
--- a/java/com/google/turbine/binder/bytecode/BytecodeBoundClass.java
+++ b/java/com/google/turbine/binder/bytecode/BytecodeBoundClass.java
@@ -18,6 +18,7 @@ package com.google.turbine.binder.bytecode;
import static com.google.common.base.MoreObjects.firstNonNull;
import static com.google.common.base.Verify.verify;
+import static java.util.Objects.requireNonNull;
import com.google.common.base.Supplier;
import com.google.common.base.Suppliers;
@@ -25,8 +26,6 @@ import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.turbine.binder.bound.AnnotationMetadata;
-import com.google.turbine.binder.bound.BoundClass;
-import com.google.turbine.binder.bound.HeaderBoundClass;
import com.google.turbine.binder.bound.TypeBoundClass;
import com.google.turbine.binder.env.Env;
import com.google.turbine.binder.sym.ClassSymbol;
@@ -69,18 +68,18 @@ import org.checkerframework.checker.nullness.qual.Nullable;
* resolved and canonicalized so there are no cycles. The laziness also minimizes the amount of work
* done on the classpath.
*/
-public class BytecodeBoundClass implements BoundClass, HeaderBoundClass, TypeBoundClass {
+public class BytecodeBoundClass implements TypeBoundClass {
private final ClassSymbol sym;
private final Env<ClassSymbol, BytecodeBoundClass> env;
private final Supplier<ClassFile> classFile;
- private final String jarFile;
+ private final @Nullable String jarFile;
public BytecodeBoundClass(
ClassSymbol sym,
Supplier<byte[]> bytes,
Env<ClassSymbol, BytecodeBoundClass> env,
- String jarFile) {
+ @Nullable String jarFile) {
this.sym = sym;
this.env = env;
this.jarFile = jarFile;
@@ -124,11 +123,11 @@ public class BytecodeBoundClass implements BoundClass, HeaderBoundClass, TypeBou
return kind.get();
}
- private final Supplier<ClassSymbol> owner =
+ private final Supplier<@Nullable ClassSymbol> owner =
Suppliers.memoize(
- new Supplier<ClassSymbol>() {
+ new Supplier<@Nullable ClassSymbol>() {
@Override
- public ClassSymbol get() {
+ public @Nullable ClassSymbol get() {
for (ClassFile.InnerClass inner : classFile.get().innerClasses()) {
if (sym.binaryName().equals(inner.innerClass())) {
return new ClassSymbol(inner.outerClass());
@@ -188,11 +187,11 @@ public class BytecodeBoundClass implements BoundClass, HeaderBoundClass, TypeBou
return access.get();
}
- private final Supplier<ClassSig> sig =
+ private final Supplier<@Nullable ClassSig> sig =
Suppliers.memoize(
- new Supplier<ClassSig>() {
+ new Supplier<@Nullable ClassSig>() {
@Override
- public ClassSig get() {
+ public @Nullable ClassSig get() {
String signature = classFile.get().signature();
if (signature == null) {
return null;
@@ -223,11 +222,11 @@ public class BytecodeBoundClass implements BoundClass, HeaderBoundClass, TypeBou
return tyParams.get();
}
- private final Supplier<ClassSymbol> superclass =
+ private final Supplier<@Nullable ClassSymbol> superclass =
Suppliers.memoize(
- new Supplier<ClassSymbol>() {
+ new Supplier<@Nullable ClassSymbol>() {
@Override
- public ClassSymbol get() {
+ public @Nullable ClassSymbol get() {
String superclass = classFile.get().superName();
if (superclass == null) {
return null;
@@ -237,7 +236,7 @@ public class BytecodeBoundClass implements BoundClass, HeaderBoundClass, TypeBou
});
@Override
- public ClassSymbol superclass() {
+ public @Nullable ClassSymbol superclass() {
return superclass.get();
}
@@ -259,11 +258,11 @@ public class BytecodeBoundClass implements BoundClass, HeaderBoundClass, TypeBou
return interfaces.get();
}
- private final Supplier<ClassTy> superClassType =
+ private final Supplier<@Nullable ClassTy> superClassType =
Suppliers.memoize(
- new Supplier<ClassTy>() {
+ new Supplier<@Nullable ClassTy>() {
@Override
- public ClassTy get() {
+ public @Nullable ClassTy get() {
if (superclass() == null) {
return null;
}
@@ -276,7 +275,7 @@ public class BytecodeBoundClass implements BoundClass, HeaderBoundClass, TypeBou
});
@Override
- public ClassTy superClassType() {
+ public @Nullable ClassTy superClassType() {
return superClassType.get();
}
@@ -319,7 +318,8 @@ public class BytecodeBoundClass implements BoundClass, HeaderBoundClass, TypeBou
ImmutableMap.Builder<TyVarSymbol, TyVarInfo> tparams = ImmutableMap.builder();
Function<String, TyVarSymbol> scope = makeScope(env, sym, typeParameters());
for (Sig.TyParamSig p : sig.get().tyParams()) {
- tparams.put(typeParameters().get(p.name()), bindTyParam(p, scope));
+ // typeParameters() is constructed to guarantee the requireNonNull call is safe.
+ tparams.put(requireNonNull(typeParameters().get(p.name())), bindTyParam(p, scope));
}
return tparams.build();
}
@@ -380,14 +380,19 @@ public class BytecodeBoundClass implements BoundClass, HeaderBoundClass, TypeBou
public ImmutableList<MethodInfo> get() {
ImmutableList.Builder<MethodInfo> methods = ImmutableList.builder();
int idx = 0;
- for (ClassFile.MethodInfo m : classFile.get().methods()) {
- methods.add(bindMethod(idx++, m));
+ ClassFile cf = classFile.get();
+ for (ClassFile.MethodInfo m : cf.methods()) {
+ if (m.name().equals("<clinit>")) {
+ // Don't bother reading class initializers, which we don't need
+ continue;
+ }
+ methods.add(bindMethod(cf, idx++, m));
}
return methods.build();
}
});
- private MethodInfo bindMethod(int methodIdx, ClassFile.MethodInfo m) {
+ private MethodInfo bindMethod(ClassFile classFile, int methodIdx, ClassFile.MethodInfo m) {
MethodSymbol methodSymbol = new MethodSymbol(methodIdx, sym, m.name());
Sig.MethodSig sig = new SigParser(firstNonNull(m.signature(), m.descriptor())).parseMethodSig();
@@ -405,7 +410,8 @@ public class BytecodeBoundClass implements BoundClass, HeaderBoundClass, TypeBou
ImmutableMap.Builder<TyVarSymbol, TyVarInfo> tparams = ImmutableMap.builder();
Function<String, TyVarSymbol> scope = makeScope(env, sym, tyParams);
for (Sig.TyParamSig p : sig.tyParams()) {
- tparams.put(tyParams.get(p.name()), bindTyParam(p, scope));
+ // tyParams is constructed to guarantee the requireNonNull call is safe.
+ tparams.put(requireNonNull(tyParams.get(p.name())), bindTyParam(p, scope));
}
tyParamTypes = tparams.build();
}
@@ -460,13 +466,19 @@ public class BytecodeBoundClass implements BoundClass, HeaderBoundClass, TypeBou
ImmutableList<AnnoInfo> annotations = BytecodeBinder.bindAnnotations(m.annotations());
+ int access = m.access();
+ if (((classFile.access() & TurbineFlag.ACC_INTERFACE) == TurbineFlag.ACC_INTERFACE)
+ && (access & (TurbineFlag.ACC_ABSTRACT | TurbineFlag.ACC_STATIC)) == 0) {
+ access |= TurbineFlag.ACC_DEFAULT;
+ }
+
return new MethodInfo(
methodSymbol,
tyParamTypes,
ret,
formals.build(),
exceptions.build(),
- m.access(),
+ access,
defaultValue,
/* decl= */ null,
annotations,
@@ -478,11 +490,11 @@ public class BytecodeBoundClass implements BoundClass, HeaderBoundClass, TypeBou
return methods.get();
}
- private final Supplier<AnnotationMetadata> annotationMetadata =
+ private final Supplier<@Nullable AnnotationMetadata> annotationMetadata =
Suppliers.memoize(
- new Supplier<AnnotationMetadata>() {
+ new Supplier<@Nullable AnnotationMetadata>() {
@Override
- public AnnotationMetadata get() {
+ public @Nullable AnnotationMetadata get() {
if ((access() & TurbineFlag.ACC_ANNOTATION) != TurbineFlag.ACC_ANNOTATION) {
return null;
}
@@ -508,8 +520,11 @@ public class BytecodeBoundClass implements BoundClass, HeaderBoundClass, TypeBou
}
});
- private static RetentionPolicy bindRetention(AnnotationInfo annotation) {
+ private static @Nullable RetentionPolicy bindRetention(AnnotationInfo annotation) {
ElementValue val = annotation.elementValuePairs().get("value");
+ if (val == null) {
+ return null;
+ }
if (val.kind() != Kind.ENUM) {
return null;
}
@@ -523,6 +538,7 @@ public class BytecodeBoundClass implements BoundClass, HeaderBoundClass, TypeBou
private static ImmutableSet<TurbineElementType> bindTarget(AnnotationInfo annotation) {
ImmutableSet.Builder<TurbineElementType> result = ImmutableSet.builder();
ElementValue val = annotation.elementValuePairs().get("value");
+ requireNonNull(val);
switch (val.kind()) {
case ARRAY:
for (ElementValue element : ((ArrayValue) val).elements()) {
@@ -547,8 +563,11 @@ public class BytecodeBoundClass implements BoundClass, HeaderBoundClass, TypeBou
}
}
- private static ClassSymbol bindRepeatable(AnnotationInfo annotation) {
+ private static @Nullable ClassSymbol bindRepeatable(AnnotationInfo annotation) {
ElementValue val = annotation.elementValuePairs().get("value");
+ if (val == null) {
+ return null;
+ }
switch (val.kind()) {
case CLASS:
String className = ((ConstTurbineClassValue) val).className();
@@ -560,7 +579,7 @@ public class BytecodeBoundClass implements BoundClass, HeaderBoundClass, TypeBou
}
@Override
- public AnnotationMetadata annotationMetadata() {
+ public @Nullable AnnotationMetadata annotationMetadata() {
return annotationMetadata.get();
}
@@ -611,7 +630,11 @@ public class BytecodeBoundClass implements BoundClass, HeaderBoundClass, TypeBou
}
/** The jar file the symbol was loaded from. */
- public String jarFile() {
+ public @Nullable String jarFile() {
+ String transitiveJar = classFile.get().transitiveJar();
+ if (transitiveJar != null) {
+ return transitiveJar;
+ }
return jarFile;
}
diff --git a/java/com/google/turbine/binder/bytecode/package-info.java b/java/com/google/turbine/binder/bytecode/package-info.java
new file mode 100644
index 0000000..23c59f0
--- /dev/null
+++ b/java/com/google/turbine/binder/bytecode/package-info.java
@@ -0,0 +1,17 @@
+/*
+ * Copyright 2016 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.turbine.binder.bytecode;
diff --git a/java/com/google/turbine/binder/env/Env.java b/java/com/google/turbine/binder/env/Env.java
index 6ee38a4..a78d3e6 100644
--- a/java/com/google/turbine/binder/env/Env.java
+++ b/java/com/google/turbine/binder/env/Env.java
@@ -20,10 +20,10 @@ import com.google.turbine.binder.sym.ClassSymbol;
import com.google.turbine.binder.sym.Symbol;
/**
- * An environment that maps {@link Symbols} {@code S} to bound nodes {@code V}.
+ * An environment that maps {@link Symbol}s {@code S} to bound nodes {@code V}.
*
- * <p>For example, {@link BoundClass} represents superclasses as a {@link ClassSymbol}, which only
- * contains the binary name of the type. To get the {@link BoundClass} for that supertype, an {@code
+ * <p>For example, {@code BoundClass} represents superclasses as a {@link ClassSymbol}, which only
+ * contains the binary name of the type. To get the {@code BoundClass} for that supertype, an {@code
* Env<BoundClass>} is used.
*
* <p>The indirection through env makes it possible to represent a graph with cycles using immutable
diff --git a/java/com/google/turbine/binder/env/LazyEnv.java b/java/com/google/turbine/binder/env/LazyEnv.java
index 9e8afd5..a9c3bd1 100644
--- a/java/com/google/turbine/binder/env/LazyEnv.java
+++ b/java/com/google/turbine/binder/env/LazyEnv.java
@@ -27,17 +27,17 @@ import java.util.Map;
* An env that permits an analysis pass to access information about symbols from the current pass,
* recursively. Cycles are detected, and result in an {@link LazyBindingError} being thrown.
*
- * <p>This is used primarily for resolving the supertype hierarchy in {@link HierarchyBinder}. The
- * supertype hierarchy forms a directed acyclic graph, and {@link HierarchyBinder} needs to process
+ * <p>This is used primarily for resolving the supertype hierarchy in {@code HierarchyBinder}. The
+ * supertype hierarchy forms a directed acyclic graph, and {@code HierarchyBinder} needs to process
* classes in a topological sort order of that graph. Unfortuntately, we can't produce a suitable
* sort order until the graph exists.
*
* @param <T> the interface type of the bound node {@link V}, shared by any underlying environments.
- * @param <V> a specific implementation of {@code T}. For example, during hierarchy binding {@link
+ * @param <V> a specific implementation of {@code T}. For example, during hierarchy binding {@code
* SourceHeaderBoundClass} nodes are being completed from the sources being compiled, and the
- * analysis of a given symbol may require looking up {@link HeaderBoundClass} nodes that will
- * either be backed by other {@link SourceHeaderBoundClass} nodes or {@link BytecodeBoundClass}
- * nodes. So the phase uses an {@link LazyEnv<HeaderBoundClass, SourceHeaderBoundClass>}.
+ * analysis of a given symbol may require looking up {@code HeaderBoundClass} nodes that will
+ * either be backed by other {@code SourceHeaderBoundClass} nodes or {@code BytecodeBoundClass}
+ * nodes. So the phase uses an {@code LazyEnv<HeaderBoundClass, SourceHeaderBoundClass>}.
*/
public class LazyEnv<S extends Symbol, T, V extends T> implements Env<S, V> {
diff --git a/java/com/google/turbine/bytecode/AnnotationWriter.java b/java/com/google/turbine/bytecode/AnnotationWriter.java
index b547971..34d6262 100644
--- a/java/com/google/turbine/bytecode/AnnotationWriter.java
+++ b/java/com/google/turbine/bytecode/AnnotationWriter.java
@@ -34,7 +34,7 @@ import com.google.turbine.bytecode.ClassFile.TypeAnnotationInfo.TypeParameterBou
import com.google.turbine.bytecode.ClassFile.TypeAnnotationInfo.TypeParameterTarget;
import com.google.turbine.bytecode.ClassFile.TypeAnnotationInfo.TypePath;
import com.google.turbine.model.Const.Value;
-import java.util.Map.Entry;
+import java.util.Map;
/** Writes an {@link AnnotationInfo} to a class file. */
public class AnnotationWriter {
@@ -50,7 +50,7 @@ public class AnnotationWriter {
public void writeAnnotation(AnnotationInfo annotation) {
output.writeShort(pool.utf8(annotation.typeName()));
output.writeShort(annotation.elementValuePairs().size());
- for (Entry<String, ElementValue> entry : annotation.elementValuePairs().entrySet()) {
+ for (Map.Entry<String, ElementValue> entry : annotation.elementValuePairs().entrySet()) {
output.writeShort(pool.utf8(entry.getKey()));
writeElementValue(entry.getValue());
}
diff --git a/java/com/google/turbine/bytecode/Attribute.java b/java/com/google/turbine/bytecode/Attribute.java
index 29efb60..7b415a7 100644
--- a/java/com/google/turbine/bytecode/Attribute.java
+++ b/java/com/google/turbine/bytecode/Attribute.java
@@ -41,7 +41,8 @@ interface Attribute {
RUNTIME_VISIBLE_TYPE_ANNOTATIONS("RuntimeVisibleTypeAnnotations"),
RUNTIME_INVISIBLE_TYPE_ANNOTATIONS("RuntimeInvisibleTypeAnnotations"),
METHOD_PARAMETERS("MethodParameters"),
- MODULE("Module");
+ MODULE("Module"),
+ TURBINE_TRANSITIVE_JAR("TurbineTransitiveJar");
private final String signature;
@@ -309,4 +310,19 @@ interface Attribute {
return module;
}
}
+
+ /** A custom attribute for recording the original jar of repackaged transitive classes. */
+ class TurbineTransitiveJar implements Attribute {
+
+ final String transitiveJar;
+
+ public TurbineTransitiveJar(String transitiveJar) {
+ this.transitiveJar = transitiveJar;
+ }
+
+ @Override
+ public Kind kind() {
+ return Kind.TURBINE_TRANSITIVE_JAR;
+ }
+ }
}
diff --git a/java/com/google/turbine/bytecode/AttributeWriter.java b/java/com/google/turbine/bytecode/AttributeWriter.java
index c5ffd16..84ca55f 100644
--- a/java/com/google/turbine/bytecode/AttributeWriter.java
+++ b/java/com/google/turbine/bytecode/AttributeWriter.java
@@ -24,6 +24,7 @@ import com.google.turbine.bytecode.Attribute.ExceptionsAttribute;
import com.google.turbine.bytecode.Attribute.InnerClasses;
import com.google.turbine.bytecode.Attribute.MethodParameters;
import com.google.turbine.bytecode.Attribute.Signature;
+import com.google.turbine.bytecode.Attribute.TurbineTransitiveJar;
import com.google.turbine.bytecode.Attribute.TypeAnnotations;
import com.google.turbine.bytecode.ClassFile.AnnotationInfo;
import com.google.turbine.bytecode.ClassFile.MethodInfo.ParameterInfo;
@@ -87,6 +88,9 @@ public class AttributeWriter {
case MODULE:
writeModule((Attribute.Module) attribute);
break;
+ case TURBINE_TRANSITIVE_JAR:
+ writeTurbineTransitiveJar((Attribute.TurbineTransitiveJar) attribute);
+ break;
}
}
@@ -266,4 +270,10 @@ public class AttributeWriter {
output.writeInt(data.length);
output.write(data);
}
+
+ private void writeTurbineTransitiveJar(TurbineTransitiveJar attribute) {
+ output.writeShort(pool.utf8(attribute.kind().signature()));
+ output.writeInt(2);
+ output.writeShort(pool.utf8(attribute.transitiveJar));
+ }
}
diff --git a/java/com/google/turbine/bytecode/ClassFile.java b/java/com/google/turbine/bytecode/ClassFile.java
index 8ee2aac..e979edc 100644
--- a/java/com/google/turbine/bytecode/ClassFile.java
+++ b/java/com/google/turbine/bytecode/ClassFile.java
@@ -42,6 +42,7 @@ public class ClassFile {
private final List<InnerClass> innerClasses;
private final ImmutableList<TypeAnnotationInfo> typeAnnotations;
@Nullable private final ModuleInfo module;
+ @Nullable private final String transitiveJar;
public ClassFile(
int access,
@@ -54,7 +55,8 @@ public class ClassFile {
List<AnnotationInfo> annotations,
List<InnerClass> innerClasses,
ImmutableList<TypeAnnotationInfo> typeAnnotations,
- @Nullable ModuleInfo module) {
+ @Nullable ModuleInfo module,
+ @Nullable String transitiveJar) {
this.access = access;
this.name = name;
this.signature = signature;
@@ -66,6 +68,7 @@ public class ClassFile {
this.innerClasses = innerClasses;
this.typeAnnotations = typeAnnotations;
this.module = module;
+ this.transitiveJar = transitiveJar;
}
/** Class access and property flags. */
@@ -124,6 +127,12 @@ public class ClassFile {
return module;
}
+ /** The original jar of a repackaged transitive class. */
+ @Nullable
+ public String transitiveJar() {
+ return transitiveJar;
+ }
+
/** The contents of a JVMS §4.5 field_info structure. */
public static class FieldInfo {
diff --git a/java/com/google/turbine/bytecode/ClassReader.java b/java/com/google/turbine/bytecode/ClassReader.java
index 9c79b42..ac8b1e1 100644
--- a/java/com/google/turbine/bytecode/ClassReader.java
+++ b/java/com/google/turbine/bytecode/ClassReader.java
@@ -106,6 +106,7 @@ public class ClassReader {
List<ClassFile.InnerClass> innerclasses = ImmutableList.of();
ImmutableList.Builder<ClassFile.AnnotationInfo> annotations = ImmutableList.builder();
ClassFile.ModuleInfo module = null;
+ String transitiveJar = null;
int attributesCount = reader.u2();
for (int j = 0; j < attributesCount; j++) {
int attributeNameIndex = reader.u2();
@@ -124,6 +125,9 @@ public class ClassReader {
case "Module":
module = readModule(constantPool);
break;
+ case "TurbineTransitiveJar":
+ transitiveJar = readTurbineTransitiveJar(constantPool);
+ break;
default:
reader.skip(reader.u4());
break;
@@ -141,7 +145,8 @@ public class ClassReader {
annotations.build(),
innerclasses,
ImmutableList.of(),
- module);
+ module,
+ transitiveJar);
}
/** Reads a JVMS 4.7.9 Signature attribute. */
@@ -509,4 +514,9 @@ public class ClassReader {
}
return fields;
}
+
+ private String readTurbineTransitiveJar(ConstantPoolReader constantPool) {
+ reader.u4(); // length
+ return constantPool.utf8(reader.u2());
+ }
}
diff --git a/java/com/google/turbine/bytecode/ClassWriter.java b/java/com/google/turbine/bytecode/ClassWriter.java
index c3490ca..de975f2 100644
--- a/java/com/google/turbine/bytecode/ClassWriter.java
+++ b/java/com/google/turbine/bytecode/ClassWriter.java
@@ -27,7 +27,7 @@ import com.google.turbine.model.Const.Value;
import java.util.List;
/** Class file writing. */
-public class ClassWriter {
+public final class ClassWriter {
private static final int MAGIC = 0xcafebabe;
private static final int MINOR_VERSION = 0;
@@ -124,4 +124,6 @@ public class ClassWriter {
result.write(body.toByteArray());
return result.toByteArray();
}
+
+ private ClassWriter() {}
}
diff --git a/java/com/google/turbine/bytecode/LowerAttributes.java b/java/com/google/turbine/bytecode/LowerAttributes.java
index 67ef2b4..5ae42af 100644
--- a/java/com/google/turbine/bytecode/LowerAttributes.java
+++ b/java/com/google/turbine/bytecode/LowerAttributes.java
@@ -29,7 +29,7 @@ import java.util.ArrayList;
import java.util.List;
/** Lower information in {@link ClassFile} structures to attributes. */
-public class LowerAttributes {
+public final class LowerAttributes {
/** Collects the {@link Attribute}s for a {@link ClassFile}. */
static List<Attribute> classAttributes(ClassFile classfile) {
@@ -45,6 +45,9 @@ public class LowerAttributes {
if (classfile.module() != null) {
attributes.add(new Attribute.Module(classfile.module()));
}
+ if (classfile.transitiveJar() != null) {
+ attributes.add(new Attribute.TurbineTransitiveJar(classfile.transitiveJar()));
+ }
return attributes;
}
@@ -146,4 +149,6 @@ public class LowerAttributes {
attributes.add(new Attribute.RuntimeInvisibleParameterAnnotations(invisibles));
}
}
+
+ private LowerAttributes() {}
}
diff --git a/java/com/google/turbine/bytecode/sig/Sig.java b/java/com/google/turbine/bytecode/sig/Sig.java
index e85740f..f759269 100644
--- a/java/com/google/turbine/bytecode/sig/Sig.java
+++ b/java/com/google/turbine/bytecode/sig/Sig.java
@@ -21,7 +21,7 @@ import com.google.turbine.model.TurbineConstantTypeKind;
import org.checkerframework.checker.nullness.qual.Nullable;
/** JVMS 4.7.9.1 signatures. */
-public class Sig {
+public final class Sig {
/** A JVMS 4.7.9.1 ClassSignature. */
public static class ClassSig {
@@ -343,4 +343,6 @@ public class Sig {
return exceptions;
}
}
+
+ private Sig() {}
}
diff --git a/java/com/google/turbine/deps/Dependencies.java b/java/com/google/turbine/deps/Dependencies.java
index 92193e8..ef1eea9 100644
--- a/java/com/google/turbine/deps/Dependencies.java
+++ b/java/com/google/turbine/deps/Dependencies.java
@@ -51,7 +51,7 @@ import java.util.Optional;
import java.util.Set;
/** Support for Bazel jdeps dependency output. */
-public class Dependencies {
+public final class Dependencies {
/** Creates a jdeps proto for the current compilation. */
public static DepsProto.Dependencies collectDeps(
Optional<String> targetLabel, ClassPath bootclasspath, BindingResult bound, Lowered lowered) {
@@ -219,4 +219,6 @@ public class Dependencies {
// preserve the order of entries in the transitive classpath
return Collections2.filter(transitiveClasspath, Predicates.in(reduced));
}
+
+ private Dependencies() {}
}
diff --git a/java/com/google/turbine/deps/Transitive.java b/java/com/google/turbine/deps/Transitive.java
index 8b0d44d..75d23f6 100644
--- a/java/com/google/turbine/deps/Transitive.java
+++ b/java/com/google/turbine/deps/Transitive.java
@@ -33,13 +33,14 @@ import com.google.turbine.bytecode.ClassWriter;
import com.google.turbine.model.TurbineFlag;
import java.util.LinkedHashSet;
import java.util.Set;
+import org.checkerframework.checker.nullness.qual.Nullable;
/**
* Collects the minimal compile-time API for symbols in the supertype closure of compiled classes.
* This allows header compilations to be performed against a classpath containing only direct
* dependencies and no transitive dependencies.
*/
-public class Transitive {
+public final class Transitive {
public static ImmutableMap<String, byte[]> collectDeps(
ClassPath bootClassPath, BindingResult bound) {
@@ -54,7 +55,8 @@ public class Transitive {
// don't export symbols loaded from the bootclasspath
continue;
}
- transitive.put(sym.binaryName(), ClassWriter.writeClass(trimClass(info.classFile())));
+ transitive.put(
+ sym.binaryName(), ClassWriter.writeClass(trimClass(info.classFile(), info.jarFile())));
}
return transitive.build();
}
@@ -62,7 +64,7 @@ public class Transitive {
/**
* Removes information from repackaged classes that will not be needed by upstream compilations.
*/
- public static ClassFile trimClass(ClassFile cf) {
+ public static ClassFile trimClass(ClassFile cf, @Nullable String jarFile) {
// drop non-constant fields
ImmutableList.Builder<FieldInfo> fields = ImmutableList.builder();
for (FieldInfo f : cf.fields()) {
@@ -80,6 +82,12 @@ public class Transitive {
innerClasses.add(i);
}
}
+ // Include the original jar file name when repackaging transitive deps. If the same transitive
+ // dep is repackaged more than once, keep the original name.
+ String transitiveJar = cf.transitiveJar();
+ if (transitiveJar == null) {
+ transitiveJar = jarFile;
+ }
return new ClassFile(
cf.access(),
cf.name(),
@@ -96,7 +104,8 @@ public class Transitive {
cf.annotations(),
innerClasses.build(),
cf.typeAnnotations(),
- /* module= */ null);
+ /* module= */ null,
+ /* transitiveJar = */ transitiveJar);
}
private static Set<ClassSymbol> superClosure(BindingResult bound) {
@@ -134,4 +143,6 @@ public class Transitive {
addSuperTypes(closure, env, i);
}
}
+
+ private Transitive() {}
}
diff --git a/java/com/google/turbine/diag/LineMap.java b/java/com/google/turbine/diag/LineMap.java
index 7a39aba..37d055b 100644
--- a/java/com/google/turbine/diag/LineMap.java
+++ b/java/com/google/turbine/diag/LineMap.java
@@ -17,6 +17,7 @@
package com.google.turbine.diag;
import static com.google.common.base.Preconditions.checkArgument;
+import static java.util.Objects.requireNonNull;
import com.google.common.collect.ImmutableRangeMap;
import com.google.common.collect.Range;
@@ -64,19 +65,22 @@ public class LineMap {
/** The zero-indexed column number of the given source position. */
public int column(int position) {
checkArgument(0 <= position && position < source.length(), "%s", position);
- return position - lines.getEntry(position).getKey().lowerEndpoint();
+ // requireNonNull is safe because `lines` covers the whole file length.
+ return position - requireNonNull(lines.getEntry(position)).getKey().lowerEndpoint();
}
/** The one-indexed line number of the given source position. */
public int lineNumber(int position) {
checkArgument(0 <= position && position < source.length(), "%s", position);
- return lines.get(position);
+ // requireNonNull is safe because `lines` covers the whole file length.
+ return requireNonNull(lines.get(position));
}
/** The one-indexed line of the given source position. */
public String line(int position) {
checkArgument(0 <= position && position < source.length(), "%s", position);
- Range<Integer> range = lines.getEntry(position).getKey();
+ // requireNonNull is safe because `lines` covers the whole file length.
+ Range<Integer> range = requireNonNull(lines.getEntry(position)).getKey();
return source.substring(range.lowerEndpoint(), range.upperEndpoint());
}
}
diff --git a/java/com/google/turbine/diag/TurbineDiagnostic.java b/java/com/google/turbine/diag/TurbineDiagnostic.java
index ccbaa7f..ed04a5d 100644
--- a/java/com/google/turbine/diag/TurbineDiagnostic.java
+++ b/java/com/google/turbine/diag/TurbineDiagnostic.java
@@ -64,6 +64,10 @@ public class TurbineDiagnostic {
return severity;
}
+ boolean isError() {
+ return severity.equals(Diagnostic.Kind.ERROR);
+ }
+
/** The diagnostic message. */
public String diagnostic() {
StringBuilder sb = new StringBuilder(path());
@@ -71,7 +75,7 @@ public class TurbineDiagnostic {
sb.append(':').append(line());
}
sb.append(": error: ");
- sb.append(message().trim()).append(System.lineSeparator());
+ sb.append(message()).append(System.lineSeparator());
if (line() != -1 && column() != -1) {
sb.append(CharMatcher.breakingWhitespace().trimTrailingFrom(source.lineMap().line(position)))
.append(System.lineSeparator());
diff --git a/java/com/google/turbine/diag/TurbineError.java b/java/com/google/turbine/diag/TurbineError.java
index 39244b5..f3ebf08 100644
--- a/java/com/google/turbine/diag/TurbineError.java
+++ b/java/com/google/turbine/diag/TurbineError.java
@@ -26,15 +26,17 @@ public class TurbineError extends Error {
/** A diagnostic kind. */
public enum ErrorKind {
- UNEXPECTED_INPUT("unexpected input: %c"),
+ UNEXPECTED_INPUT("unexpected input: %s"),
UNEXPECTED_IDENTIFIER("unexpected identifier '%s'"),
UNEXPECTED_EOF("unexpected end of input"),
UNTERMINATED_STRING("unterminated string literal"),
UNTERMINATED_CHARACTER_LITERAL("unterminated char literal"),
+ UNPAIRED_SURROGATE("unpaired surrogate 0x%x"),
UNTERMINATED_EXPRESSION("unterminated expression, expected ';' not found"),
INVALID_UNICODE("illegal unicode escape"),
EMPTY_CHARACTER_LITERAL("empty char literal"),
EXPECTED_TOKEN("expected token %s"),
+ EXTENDS_AFTER_IMPLEMENTS("'extends' must come before 'implements'"),
INVALID_LITERAL("invalid literal: %s"),
UNEXPECTED_TYPE_PARAMETER("unexpected type parameter %s"),
SYMBOL_NOT_FOUND("symbol not found %s"),
@@ -42,6 +44,7 @@ public class TurbineError extends Error {
TYPE_PARAMETER_QUALIFIER("type parameter used as type qualifier"),
UNEXPECTED_TOKEN("unexpected token: %s"),
INVALID_ANNOTATION_ARGUMENT("invalid annotation argument"),
+ MISSING_ANNOTATION_ARGUMENT("missing required annotation argument: %s"),
CANNOT_RESOLVE("could not resolve %s"),
EXPRESSION_ERROR("could not evaluate constant expression"),
OPERAND_TYPE("bad operand type %s"),
@@ -51,6 +54,8 @@ public class TurbineError extends Error {
DUPLICATE_DECLARATION("duplicate declaration of %s"),
BAD_MODULE_INFO("unexpected declaration found in module-info"),
UNCLOSED_COMMENT("unclosed comment"),
+ UNEXPECTED_TYPE("unexpected type %s"),
+ UNEXPECTED_MODIFIER("unexpected modifier: %s"),
PROC("%s");
private final String message;
diff --git a/java/com/google/turbine/diag/TurbineLog.java b/java/com/google/turbine/diag/TurbineLog.java
index b336e25..565b9ea 100644
--- a/java/com/google/turbine/diag/TurbineLog.java
+++ b/java/com/google/turbine/diag/TurbineLog.java
@@ -18,7 +18,6 @@ package com.google.turbine.diag;
import com.google.common.collect.ImmutableList;
import com.google.turbine.diag.TurbineError.ErrorKind;
-import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Set;
import javax.tools.Diagnostic;
@@ -26,20 +25,24 @@ import javax.tools.Diagnostic;
/** A log that collects diagnostics. */
public class TurbineLog {
- private final Set<TurbineDiagnostic> errors = new LinkedHashSet<>();
+ private final Set<TurbineDiagnostic> diagnostics = new LinkedHashSet<>();
public TurbineLogWithSource withSource(SourceFile source) {
return new TurbineLogWithSource(source);
}
+ public ImmutableList<TurbineDiagnostic> diagnostics() {
+ return ImmutableList.copyOf(diagnostics);
+ }
+
public void maybeThrow() {
if (anyErrors()) {
- throw new TurbineError(ImmutableList.copyOf(errors));
+ throw new TurbineError(diagnostics());
}
}
- private boolean anyErrors() {
- for (TurbineDiagnostic error : errors) {
+ public boolean anyErrors() {
+ for (TurbineDiagnostic error : diagnostics) {
if (error.severity().equals(Diagnostic.Kind.ERROR)) {
return true;
}
@@ -55,7 +58,7 @@ public class TurbineLog {
* code generated in later processing rounds.
*/
public boolean errorRaised() {
- for (TurbineDiagnostic error : errors) {
+ for (TurbineDiagnostic error : diagnostics) {
if (error.kind().equals(ErrorKind.PROC) && error.severity().equals(Diagnostic.Kind.ERROR)) {
return true;
}
@@ -65,17 +68,12 @@ public class TurbineLog {
/** Reset the log between annotation processing rounds. */
public void clear() {
- Iterator<TurbineDiagnostic> it = errors.iterator();
- while (it.hasNext()) {
- if (it.next().severity().equals(Diagnostic.Kind.ERROR)) {
- it.remove();
- }
- }
+ diagnostics.removeIf(TurbineDiagnostic::isError);
}
/** Reports an annotation processing diagnostic with no position information. */
public void diagnostic(Diagnostic.Kind severity, String message) {
- errors.add(TurbineDiagnostic.format(severity, ErrorKind.PROC, message));
+ diagnostics.add(TurbineDiagnostic.format(severity, ErrorKind.PROC, message));
}
/** A log for a specific source file. */
@@ -88,7 +86,7 @@ public class TurbineLog {
}
public void diagnostic(Diagnostic.Kind severity, int position, ErrorKind kind, Object... args) {
- errors.add(TurbineDiagnostic.format(severity, source, position, kind, args));
+ diagnostics.add(TurbineDiagnostic.format(severity, source, position, kind, args));
}
public void error(int position, ErrorKind kind, Object... args) {
diff --git a/java/com/google/turbine/lower/Lower.java b/java/com/google/turbine/lower/Lower.java
index 0f7bb90..971bbe4 100644
--- a/java/com/google/turbine/lower/Lower.java
+++ b/java/com/google/turbine/lower/Lower.java
@@ -185,7 +185,8 @@ public class Lower {
annotations,
innerClasses.build(),
/* typeAnnotations= */ ImmutableList.of(),
- moduleInfo);
+ moduleInfo,
+ /* transitiveJar= */ null);
symbols.addAll(sig.classes);
return ClassWriter.writeClass(classfile);
}
@@ -279,7 +280,8 @@ public class Lower {
annotations,
inners,
typeAnnotations,
- /* module= */ null);
+ /* module= */ null,
+ /* transitiveJar= */ null);
symbols.addAll(sig.classes);
diff --git a/java/com/google/turbine/lower/LowerSignature.java b/java/com/google/turbine/lower/LowerSignature.java
index 13a7b9f..a08c7e8 100644
--- a/java/com/google/turbine/lower/LowerSignature.java
+++ b/java/com/google/turbine/lower/LowerSignature.java
@@ -128,15 +128,13 @@ public class LowerSignature {
* unnecessary.
*/
public String methodSignature(
- Env<ClassSymbol, TypeBoundClass> env,
- SourceTypeBoundClass.MethodInfo method,
- ClassSymbol sym) {
+ Env<ClassSymbol, TypeBoundClass> env, TypeBoundClass.MethodInfo method, ClassSymbol sym) {
if (!needsMethodSig(sym, env, method)) {
return null;
}
ImmutableList<Sig.TyParamSig> typarams = tyParamSig(method.tyParams(), env);
ImmutableList.Builder<Sig.TySig> fparams = ImmutableList.builder();
- for (SourceTypeBoundClass.ParamInfo t : method.parameters()) {
+ for (TypeBoundClass.ParamInfo t : method.parameters()) {
if (t.synthetic()) {
continue;
}
@@ -161,7 +159,7 @@ public class LowerSignature {
}
private boolean needsMethodSig(
- ClassSymbol sym, Env<ClassSymbol, TypeBoundClass> env, SourceTypeBoundClass.MethodInfo m) {
+ ClassSymbol sym, Env<ClassSymbol, TypeBoundClass> env, TypeBoundClass.MethodInfo m) {
if ((env.get(sym).access() & TurbineFlag.ACC_ENUM) == TurbineFlag.ACC_ENUM
&& m.name().equals("<init>")) {
// JDK-8024694: javac always expects signature attribute for enum constructors
@@ -176,7 +174,7 @@ public class LowerSignature {
if (m.returnType() != null && needsSig(m.returnType())) {
return true;
}
- for (SourceTypeBoundClass.ParamInfo t : m.parameters()) {
+ for (TypeBoundClass.ParamInfo t : m.parameters()) {
if (t.synthetic()) {
continue;
}
@@ -262,14 +260,14 @@ public class LowerSignature {
private ImmutableList<Sig.TyParamSig> tyParamSig(
Map<TyVarSymbol, TyVarInfo> px, Env<ClassSymbol, TypeBoundClass> env) {
ImmutableList.Builder<Sig.TyParamSig> result = ImmutableList.builder();
- for (Map.Entry<TyVarSymbol, SourceTypeBoundClass.TyVarInfo> entry : px.entrySet()) {
+ for (Map.Entry<TyVarSymbol, TyVarInfo> entry : px.entrySet()) {
result.add(tyParamSig(entry.getKey(), entry.getValue(), env));
}
return result.build();
}
private Sig.TyParamSig tyParamSig(
- TyVarSymbol sym, SourceTypeBoundClass.TyVarInfo info, Env<ClassSymbol, TypeBoundClass> env) {
+ TyVarSymbol sym, TyVarInfo info, Env<ClassSymbol, TypeBoundClass> env) {
String identifier = sym.name();
Sig.TySig cbound = null;
diff --git a/java/com/google/turbine/main/Main.java b/java/com/google/turbine/main/Main.java
index 1e60ae6..59563b6 100644
--- a/java/com/google/turbine/main/Main.java
+++ b/java/com/google/turbine/main/Main.java
@@ -70,7 +70,7 @@ import java.util.jar.Manifest;
import java.util.zip.ZipEntry;
/** Main entry point for the turbine CLI. */
-public class Main {
+public final class Main {
private static final int BUFFER_SIZE = 65536;
@@ -256,9 +256,10 @@ public class Main {
ClassPathBinder.bindClasspath(toPaths(classpath)),
Processing.initializeProcessors(
/* javacopts= */ options.javacOpts(),
- /* processorPath= */ options.processorPath(),
/* processorNames= */ options.processors(),
- /* builtinProcessors= */ options.builtinProcessors()),
+ Processing.processorLoader(
+ /* processorPath= */ options.processorPath(),
+ /* builtinProcessors= */ options.builtinProcessors())),
bootclasspath,
/* moduleVersion=*/ Optional.empty());
}
@@ -332,6 +333,14 @@ public class Main {
return;
}
Path path = Paths.get(options.gensrcOutput().get());
+ if (Files.isDirectory(path)) {
+ for (SourceFile source : generatedSources.values()) {
+ Path to = path.resolve(source.path());
+ Files.createDirectories(to.getParent());
+ Files.write(to, source.source().getBytes(UTF_8));
+ }
+ return;
+ }
try (OutputStream os = Files.newOutputStream(path);
BufferedOutputStream bos = new BufferedOutputStream(os, BUFFER_SIZE);
JarOutputStream jos = new JarOutputStream(bos)) {
@@ -349,6 +358,14 @@ public class Main {
return;
}
Path path = Paths.get(options.resourceOutput().get());
+ if (Files.isDirectory(path)) {
+ for (Map.Entry<String, byte[]> resource : generatedResources.entrySet()) {
+ Path to = path.resolve(resource.getKey());
+ Files.createDirectories(to.getParent());
+ Files.write(to, resource.getValue());
+ }
+ return;
+ }
try (OutputStream os = Files.newOutputStream(path);
BufferedOutputStream bos = new BufferedOutputStream(os, BUFFER_SIZE);
JarOutputStream jos = new JarOutputStream(bos)) {
@@ -465,4 +482,6 @@ public class Main {
}
return result.build();
}
+
+ private Main() {}
}
diff --git a/java/com/google/turbine/model/TurbineFlag.java b/java/com/google/turbine/model/TurbineFlag.java
index 48e88e7..c138d46 100644
--- a/java/com/google/turbine/model/TurbineFlag.java
+++ b/java/com/google/turbine/model/TurbineFlag.java
@@ -22,7 +22,7 @@ package com.google.turbine.model;
* <p>See tables 4.1-A, 4.5-A, 4.6-A, and 4.7.6-A in JVMS 4:
* https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html
*/
-public class TurbineFlag {
+public final class TurbineFlag {
public static final int ACC_PUBLIC = 0x0001;
public static final int ACC_PRIVATE = 0x0002;
public static final int ACC_PROTECTED = 0x0004;
@@ -54,4 +54,6 @@ public class TurbineFlag {
/** Synthetic constructors (e.g. of inner classes and enums). */
public static final int ACC_SYNTH_CTOR = 1 << 18;
+
+ private TurbineFlag() {}
}
diff --git a/java/com/google/turbine/options/TurbineOptions.java b/java/com/google/turbine/options/TurbineOptions.java
index 4dcc408..c104c54 100644
--- a/java/com/google/turbine/options/TurbineOptions.java
+++ b/java/com/google/turbine/options/TurbineOptions.java
@@ -70,17 +70,6 @@ public abstract class TurbineOptions {
/** The output jar. */
public abstract Optional<String> output();
- /**
- * The output jar.
- *
- * @deprecated use {@link #output} instead.
- */
- @Deprecated
- @Nullable
- public String outputFile() {
- return output().orElse(null);
- }
-
/** Paths to annotation processor artifacts. */
public abstract ImmutableList<String> processorPath();
@@ -160,56 +149,20 @@ public abstract class TurbineOptions {
public abstract static class Builder {
public abstract Builder setOutput(String output);
- /** @deprecated use {@link #setClassPath(ImmutableList)} instead. */
- @Deprecated
- public Builder addClassPathEntries(Iterable<String> sources) {
- return setClassPath(ImmutableList.copyOf(sources));
- }
-
public abstract Builder setClassPath(ImmutableList<String> classPath);
public abstract Builder setBootClassPath(ImmutableList<String> bootClassPath);
- /** @deprecated use {@link #setBootClassPath(ImmutableList)} instead. */
- @Deprecated
- public Builder addBootClassPathEntries(Iterable<String> sources) {
- return setBootClassPath(ImmutableList.copyOf(sources));
- }
-
public abstract Builder setRelease(String release);
public abstract Builder setSystem(String system);
public abstract Builder setSources(ImmutableList<String> sources);
- /** @deprecated use {@link #setSources(ImmutableList)} instead. */
- @Deprecated
- public Builder addSources(Iterable<String> sources) {
- return setSources(ImmutableList.copyOf(sources));
- }
-
- /** @deprecated use {@link #setProcessorPath(ImmutableList)} instead. */
- @Deprecated
- public Builder addProcessorPathEntries(Iterable<String> processorPath) {
- return setProcessorPath(ImmutableList.copyOf(processorPath));
- }
-
public abstract Builder setProcessorPath(ImmutableList<String> processorPath);
- /** @deprecated use {@link #setProcessors(ImmutableList)} instead. */
- @Deprecated
- public Builder addProcessors(Iterable<String> processors) {
- return setProcessors(ImmutableList.copyOf(processors));
- }
-
public abstract Builder setProcessors(ImmutableList<String> processors);
- /** @deprecated use {@link #setBuiltinProcessors(ImmutableList)} instead. */
- @Deprecated
- public Builder addBuiltinProcessors(Iterable<String> builtinProcessors) {
- return setBuiltinProcessors(ImmutableList.copyOf(builtinProcessors));
- }
-
public abstract Builder setBuiltinProcessors(ImmutableList<String> builtinProcessors);
public abstract Builder setSourceJars(ImmutableList<String> sourceJars);
@@ -222,12 +175,6 @@ public abstract class TurbineOptions {
public abstract Builder setInjectingRuleKind(String injectingRuleKind);
- /** @deprecated use {@link #setDepsArtifacts(ImmutableList)} instead. */
- @Deprecated
- public Builder addAllDepsArtifacts(Iterable<String> depsArtifacts) {
- return setDepsArtifacts(ImmutableList.copyOf(depsArtifacts));
- }
-
public abstract Builder setDepsArtifacts(ImmutableList<String> depsArtifacts);
public abstract Builder setHelp(boolean help);
@@ -241,12 +188,6 @@ public abstract class TurbineOptions {
public abstract Builder setReducedClasspathMode(ReducedClasspathMode reducedClasspathMode);
- /** @deprecated use {@link #setDirectJars(ImmutableList)} instead. */
- @Deprecated
- public Builder addDirectJars(Iterable<String> directJars) {
- return setDirectJars(ImmutableList.copyOf(directJars));
- }
-
public abstract Builder setDirectJars(ImmutableList<String> jars);
public abstract Builder setProfile(String profile);
@@ -261,4 +202,11 @@ public abstract class TurbineOptions {
public abstract TurbineOptions build();
}
+
+ // TODO(b/188833569): remove when AutoValue adds @Nullable to Object if its on the classpath
+ @Override
+ public abstract boolean equals(@Nullable Object other);
+
+ @Override
+ public abstract int hashCode();
}
diff --git a/java/com/google/turbine/options/TurbineOptionsParser.java b/java/com/google/turbine/options/TurbineOptionsParser.java
index 17d4bf6..4a8ff16 100644
--- a/java/com/google/turbine/options/TurbineOptionsParser.java
+++ b/java/com/google/turbine/options/TurbineOptionsParser.java
@@ -30,10 +30,9 @@ import java.nio.file.Paths;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.Iterator;
-import org.checkerframework.checker.nullness.qual.Nullable;
/** A command line options parser for {@link TurbineOptions}. */
-public class TurbineOptionsParser {
+public final class TurbineOptionsParser {
/**
* Parses command line options into {@link TurbineOptions}, expanding any {@code @params} files.
@@ -57,17 +56,17 @@ public class TurbineOptionsParser {
private static void parse(TurbineOptions.Builder builder, Deque<String> argumentDeque) {
while (!argumentDeque.isEmpty()) {
- String next = argumentDeque.pollFirst();
+ String next = argumentDeque.removeFirst();
switch (next) {
case "--output":
- builder.setOutput(readOne(argumentDeque));
+ builder.setOutput(readOne(next, argumentDeque));
break;
case "--source_jars":
builder.setSourceJars(readList(argumentDeque));
break;
case "--temp_dir":
// TODO(cushon): remove this when Bazel no longer passes the flag
- readOne(argumentDeque);
+ readOne(next, argumentDeque);
break;
case "--processors":
builder.setProcessors(readList(argumentDeque));
@@ -85,10 +84,10 @@ public class TurbineOptionsParser {
builder.setBootClassPath(readList(argumentDeque));
break;
case "--release":
- builder.setRelease(readOne(argumentDeque));
+ builder.setRelease(readOne(next, argumentDeque));
break;
case "--system":
- builder.setSystem(readOne(argumentDeque));
+ builder.setSystem(readOne(next, argumentDeque));
break;
case "--javacopts":
{
@@ -100,11 +99,12 @@ public class TurbineOptionsParser {
case "--sources":
builder.setSources(readList(argumentDeque));
break;
+ case "--output_deps_proto":
case "--output_deps":
- builder.setOutputDeps(readOne(argumentDeque));
+ builder.setOutputDeps(readOne(next, argumentDeque));
break;
case "--output_manifest_proto":
- builder.setOutputManifest(readOne(argumentDeque));
+ builder.setOutputManifest(readOne(next, argumentDeque));
break;
case "--direct_dependencies":
builder.setDirectJars(readList(argumentDeque));
@@ -113,10 +113,10 @@ public class TurbineOptionsParser {
builder.setDepsArtifacts(readList(argumentDeque));
break;
case "--target_label":
- builder.setTargetLabel(readOne(argumentDeque));
+ builder.setTargetLabel(readOne(next, argumentDeque));
break;
case "--injecting_rule_kind":
- builder.setInjectingRuleKind(readOne(argumentDeque));
+ builder.setInjectingRuleKind(readOne(next, argumentDeque));
break;
case "--javac_fallback":
case "--nojavac_fallback":
@@ -129,26 +129,37 @@ public class TurbineOptionsParser {
builder.setReducedClasspathMode(ReducedClasspathMode.NONE);
break;
case "--reduce_classpath_mode":
- builder.setReducedClasspathMode(ReducedClasspathMode.valueOf(readOne(argumentDeque)));
+ builder.setReducedClasspathMode(
+ ReducedClasspathMode.valueOf(readOne(next, argumentDeque)));
break;
case "--full_classpath_length":
- builder.setFullClasspathLength(Integer.parseInt(readOne(argumentDeque)));
+ builder.setFullClasspathLength(Integer.parseInt(readOne(next, argumentDeque)));
break;
case "--reduced_classpath_length":
- builder.setReducedClasspathLength(Integer.parseInt(readOne(argumentDeque)));
+ builder.setReducedClasspathLength(Integer.parseInt(readOne(next, argumentDeque)));
break;
case "--profile":
- builder.setProfile(readOne(argumentDeque));
+ builder.setProfile(readOne(next, argumentDeque));
break;
+ case "--generated_sources_output":
case "--gensrc_output":
- builder.setGensrcOutput(readOne(argumentDeque));
+ builder.setGensrcOutput(readOne(next, argumentDeque));
break;
case "--resource_output":
- builder.setResourceOutput(readOne(argumentDeque));
+ builder.setResourceOutput(readOne(next, argumentDeque));
break;
case "--help":
builder.setHelp(true);
break;
+ case "--experimental_fix_deps_tool":
+ case "--strict_java_deps":
+ case "--native_header_output":
+ // accepted (and ignored) for compatibility with JavaBuilder command lines
+ readOne(next, argumentDeque);
+ break;
+ case "--compress_jar":
+ // accepted (and ignored) for compatibility with JavaBuilder command lines
+ break;
default:
throw new IllegalArgumentException("unknown option: " + next);
}
@@ -190,20 +201,22 @@ public class TurbineOptionsParser {
}
}
- /** Returns the value of an option, or {@code null}. */
- @Nullable
- private static String readOne(Deque<String> argumentDeque) {
- if (argumentDeque.isEmpty() || argumentDeque.peekFirst().startsWith("-")) {
- return null;
+ /**
+ * Returns the value of an option, or throws {@link IllegalArgumentException} if the value is not
+ * present.
+ */
+ private static String readOne(String flag, Deque<String> argumentDeque) {
+ if (argumentDeque.isEmpty() || argumentDeque.getFirst().startsWith("-")) {
+ throw new IllegalArgumentException("missing required argument for: " + flag);
}
- return argumentDeque.pollFirst();
+ return argumentDeque.removeFirst();
}
/** Returns a list of option values. */
private static ImmutableList<String> readList(Deque<String> argumentDeque) {
ImmutableList.Builder<String> result = ImmutableList.builder();
- while (!argumentDeque.isEmpty() && !argumentDeque.peekFirst().startsWith("--")) {
- result.add(argumentDeque.pollFirst());
+ while (!argumentDeque.isEmpty() && !argumentDeque.getFirst().startsWith("--")) {
+ result.add(argumentDeque.removeFirst());
}
return result.build();
}
@@ -215,7 +228,7 @@ public class TurbineOptionsParser {
private static ImmutableList<String> readJavacopts(Deque<String> argumentDeque) {
ImmutableList.Builder<String> result = ImmutableList.builder();
while (!argumentDeque.isEmpty()) {
- String arg = argumentDeque.pollFirst();
+ String arg = argumentDeque.removeFirst();
if (arg.equals("--")) {
return result.build();
}
@@ -237,4 +250,6 @@ public class TurbineOptionsParser {
}
}
}
+
+ private TurbineOptionsParser() {}
}
diff --git a/java/com/google/turbine/options/package-info.java b/java/com/google/turbine/options/package-info.java
new file mode 100644
index 0000000..9c12bf8
--- /dev/null
+++ b/java/com/google/turbine/options/package-info.java
@@ -0,0 +1,17 @@
+/*
+ * Copyright 2021 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.turbine.options;
diff --git a/java/com/google/turbine/parse/ConstExpressionParser.java b/java/com/google/turbine/parse/ConstExpressionParser.java
index e49d51c..ba51814 100644
--- a/java/com/google/turbine/parse/ConstExpressionParser.java
+++ b/java/com/google/turbine/parse/ConstExpressionParser.java
@@ -506,12 +506,15 @@ public class ConstExpressionParser {
return term1;
}
eat();
- if (op == TurbineOperatorKind.TERNARY) {
- term1 = ternary(term1);
- } else if (op == TurbineOperatorKind.ASSIGN) {
- term1 = assign(term1, op);
- } else {
- term1 = new Tree.Binary(position, term1, expression(op.prec()), op);
+ switch (op) {
+ case TERNARY:
+ term1 = ternary(term1);
+ break;
+ case ASSIGN:
+ term1 = assign(term1, op);
+ break;
+ default:
+ term1 = new Tree.Binary(position, term1, expression(op.prec()), op);
}
if (term1 == null) {
return null;
@@ -568,6 +571,7 @@ public class ConstExpressionParser {
throw new AssertionError();
}
eat();
+ int pos = position;
Tree.ConstVarName constVarName = (Tree.ConstVarName) qualIdent();
if (constVarName == null) {
return null;
@@ -577,10 +581,10 @@ public class ConstExpressionParser {
if (token == Token.LPAREN) {
eat();
while (token != Token.RPAREN) {
- int pos = position;
+ int argPos = position;
Tree.Expression expression = expression();
if (expression == null) {
- throw TurbineError.format(lexer.source(), pos, ErrorKind.INVALID_ANNOTATION_ARGUMENT);
+ throw TurbineError.format(lexer.source(), argPos, ErrorKind.INVALID_ANNOTATION_ARGUMENT);
}
args.add(expression);
if (token != Token.COMMA) {
@@ -592,11 +596,19 @@ public class ConstExpressionParser {
eat();
}
}
- return new Tree.AnnoExpr(position, new Tree.Anno(position, name, args.build()));
+ return new Tree.AnnoExpr(pos, new Tree.Anno(pos, name, args.build()));
}
@CheckReturnValue
private TurbineError error(ErrorKind kind, Object... args) {
return TurbineError.format(lexer.source(), lexer.position(), kind, args);
}
+
+ public int f() {
+ return helper(1, 2);
+ }
+
+ private int helper(int x, int y) {
+ return x + y;
+ }
}
diff --git a/java/com/google/turbine/parse/Parser.java b/java/com/google/turbine/parse/Parser.java
index 4a090b3..af1eabf 100644
--- a/java/com/google/turbine/parse/Parser.java
+++ b/java/com/google/turbine/parse/Parser.java
@@ -519,7 +519,15 @@ public class Parser {
interfaces.add(classty());
} while (maybe(Token.COMMA));
}
- eat(Token.LBRACE);
+ switch (token) {
+ case LBRACE:
+ next();
+ break;
+ case EXTENDS:
+ throw error(ErrorKind.EXTENDS_AFTER_IMPLEMENTS);
+ default:
+ throw error(ErrorKind.EXPECTED_TOKEN, Token.LBRACE);
+ }
ImmutableList<Tree> members = classMembers();
eat(Token.RBRACE);
return new TyDecl(
@@ -748,7 +756,9 @@ public class Parser {
}
if (token == Token.DOT) {
next();
- // TODO(cushon): is this cast OK?
+ if (!result.kind().equals(Kind.CLASS_TY)) {
+ throw error(token);
+ }
result = classty((ClassTy) result);
}
result = maybeDims(maybeAnnos(), result);
diff --git a/java/com/google/turbine/parse/StreamLexer.java b/java/com/google/turbine/parse/StreamLexer.java
index 2e20c26..991b5fd 100644
--- a/java/com/google/turbine/parse/StreamLexer.java
+++ b/java/com/google/turbine/parse/StreamLexer.java
@@ -29,7 +29,7 @@ public class StreamLexer implements Lexer {
private final UnicodeEscapePreprocessor reader;
/** The current input character. */
- private char ch;
+ private int ch;
/** The start position of the current token. */
private int position;
@@ -353,7 +353,7 @@ public class StreamLexer implements Lexer {
eat();
return Token.ELLIPSIS;
} else {
- throw error(ErrorKind.UNEXPECTED_INPUT, ch);
+ throw inputError();
}
}
case '0':
@@ -384,7 +384,7 @@ public class StreamLexer implements Lexer {
case '\'':
throw error(ErrorKind.EMPTY_CHARACTER_LITERAL);
default:
- value = ch;
+ value = (char) ch;
eat();
}
if (ch == '\'') {
@@ -419,7 +419,7 @@ public class StreamLexer implements Lexer {
}
// falls through
default:
- sb.append(ch);
+ sb.appendCodePoint(ch);
eat();
continue STRING;
}
@@ -430,7 +430,7 @@ public class StreamLexer implements Lexer {
// TODO(cushon): the style guide disallows non-ascii identifiers
return identifier();
}
- throw error(ErrorKind.UNEXPECTED_INPUT, ch);
+ throw inputError();
}
}
}
@@ -511,7 +511,7 @@ public class StreamLexer implements Lexer {
}
}
default:
- throw error(ErrorKind.UNEXPECTED_INPUT, ch);
+ throw inputError();
}
}
@@ -623,7 +623,7 @@ public class StreamLexer implements Lexer {
eat();
break;
default:
- throw error(ErrorKind.UNEXPECTED_INPUT, ch);
+ throw inputError();
}
OUTER:
while (true) {
@@ -658,7 +658,7 @@ public class StreamLexer implements Lexer {
case '9':
continue OUTER;
default:
- throw error(ErrorKind.UNEXPECTED_INPUT, ch);
+ throw inputError();
}
}
case 'A':
@@ -695,7 +695,7 @@ public class StreamLexer implements Lexer {
if ('0' <= ch && ch <= '9') {
eat();
} else {
- throw error(ErrorKind.UNEXPECTED_INPUT, ch);
+ throw inputError();
}
OUTER:
while (true) {
@@ -707,7 +707,7 @@ public class StreamLexer implements Lexer {
if ('0' <= ch && ch <= '9') {
continue OUTER;
} else {
- throw error(ErrorKind.UNEXPECTED_INPUT, ch);
+ throw inputError();
}
case '0':
case '1':
@@ -746,7 +746,7 @@ public class StreamLexer implements Lexer {
eat();
break;
default:
- throw error(ErrorKind.UNEXPECTED_INPUT, ch);
+ throw inputError();
}
OUTER:
while (true) {
@@ -760,7 +760,7 @@ public class StreamLexer implements Lexer {
case '1':
continue OUTER;
default:
- throw error(ErrorKind.UNEXPECTED_INPUT, ch);
+ throw inputError();
}
case '0':
case '1':
@@ -798,7 +798,7 @@ public class StreamLexer implements Lexer {
eat();
break;
default:
- throw error(ErrorKind.UNEXPECTED_INPUT, ch);
+ throw inputError();
}
OUTER:
while (true) {
@@ -818,7 +818,7 @@ public class StreamLexer implements Lexer {
case '7':
continue OUTER;
default:
- throw error(ErrorKind.UNEXPECTED_INPUT, ch);
+ throw inputError();
}
case '0':
case '1':
@@ -992,7 +992,7 @@ public class StreamLexer implements Lexer {
}
case '/':
// handled with comments
- throw error(ErrorKind.UNEXPECTED_INPUT, ch);
+ throw inputError();
case '%':
eat();
@@ -1011,7 +1011,7 @@ public class StreamLexer implements Lexer {
return Token.XOR;
}
default:
- throw error(ErrorKind.UNEXPECTED_INPUT, ch);
+ throw inputError();
}
}
@@ -1141,6 +1141,12 @@ public class StreamLexer implements Lexer {
}
}
+ private TurbineError inputError() {
+ return error(
+ ErrorKind.UNEXPECTED_INPUT,
+ Character.isBmpCodePoint(ch) ? Character.toString((char) ch) : String.format("U+%X", ch));
+ }
+
private TurbineError error(ErrorKind kind, Object... args) {
return TurbineError.format(reader.source(), reader.position(), kind, args);
}
diff --git a/java/com/google/turbine/parse/UnicodeEscapePreprocessor.java b/java/com/google/turbine/parse/UnicodeEscapePreprocessor.java
index 3f38561..4146ca5 100644
--- a/java/com/google/turbine/parse/UnicodeEscapePreprocessor.java
+++ b/java/com/google/turbine/parse/UnicodeEscapePreprocessor.java
@@ -30,7 +30,7 @@ public class UnicodeEscapePreprocessor {
private final String input;
private int idx = 0;
- private char ch;
+ private int ch;
private boolean evenLeadingSlashes = true;
public UnicodeEscapePreprocessor(SourceFile source) {
@@ -49,7 +49,7 @@ public class UnicodeEscapePreprocessor {
}
/** Returns the next unescaped Unicode input character. */
- public char next() {
+ public int next() {
eat();
if (ch == '\\' && evenLeadingSlashes) {
unicodeEscape();
@@ -88,7 +88,7 @@ public class UnicodeEscapePreprocessor {
}
/** Consumes a hex digit. */
- private int hexDigit(char d) {
+ private int hexDigit(int d) {
switch (d) {
case '0':
case '1':
@@ -130,8 +130,20 @@ public class UnicodeEscapePreprocessor {
* it terminates the input avoids some bounds checks in the lexer.
*/
private void eat() {
- ch = done() ? ASCII_SUB : input.charAt(idx);
+ char hi = done() ? ASCII_SUB : input.charAt(idx);
idx++;
+ if (!Character.isHighSurrogate(hi)) {
+ ch = hi;
+ return;
+ }
+ if (done()) {
+ throw error(ErrorKind.UNPAIRED_SURROGATE, (int) hi);
+ }
+ char lo = input.charAt(idx++);
+ if (!Character.isLowSurrogate(lo)) {
+ throw error(ErrorKind.UNPAIRED_SURROGATE, (int) hi);
+ }
+ ch = Character.toCodePoint(hi, lo);
}
public SourceFile source() {
diff --git a/java/com/google/turbine/parse/VariableInitializerParser.java b/java/com/google/turbine/parse/VariableInitializerParser.java
index 4ad9272..7f4d40e 100644
--- a/java/com/google/turbine/parse/VariableInitializerParser.java
+++ b/java/com/google/turbine/parse/VariableInitializerParser.java
@@ -40,10 +40,10 @@ import java.util.List;
* <p>That handles everything except multi-variable declarations (int x = 1, y = 2;), which in
* hindsight were probably a mistake. Multi-variable declarations contain a list of name and
* initializer pairs separated by commas. The initializer expressions may also contain commas, so
- * it's non-trivial to split on initializer boundaries. For example, consider `int x = a < b, c =
- * d;`. We can't tell looking at the prefix `a < b, c` whether that's a less-than expression
- * followed by another initializer, or the start of a generic type: `a<b, c>.foo()`. Distinguishing
- * between these cases requires arbitrary lookahead.
+ * it's non-trivial to split on initializer boundaries. For example, consider {@code int x = a < b,
+ * c = d;}. We can't tell looking at the prefix {@code a < b, c} whether that's a less-than
+ * expression followed by another initializer, or the start of a generic type: {@code a<b, c>.foo(}.
+ * Distinguishing between these cases requires arbitrary lookahead.
*
* <p>The preprocessor seems to be operationally correct. It's possible there are edge cases that it
* doesn't handle, but it's extremely rare for compile-time constant multi-variable declarations to
@@ -330,6 +330,8 @@ public class VariableInitializerParser {
depth--;
next();
break;
+ case EOF:
+ throw error(ErrorKind.UNEXPECTED_EOF);
default:
next();
break;
diff --git a/java/com/google/turbine/processing/TurbineAnnotationMirror.java b/java/com/google/turbine/processing/TurbineAnnotationMirror.java
index 5ea3de1..df3bd19 100644
--- a/java/com/google/turbine/processing/TurbineAnnotationMirror.java
+++ b/java/com/google/turbine/processing/TurbineAnnotationMirror.java
@@ -18,6 +18,7 @@ package com.google.turbine.processing;
import static com.google.common.base.Preconditions.checkState;
import static com.google.common.collect.Iterables.getLast;
+import static java.util.Objects.requireNonNull;
import com.google.common.base.Joiner;
import com.google.common.base.Supplier;
@@ -115,8 +116,13 @@ class TurbineAnnotationMirror implements TurbineAnnotationValueMirror, Annotatio
ImmutableMap.Builder<ExecutableElement, AnnotationValue> result =
ImmutableMap.builder();
for (Map.Entry<String, Const> value : anno.values().entrySet()) {
+ // requireNonNull is safe because `elements` contains an entry for every method.
+ // Any element values pairs without a corresponding method in the annotation
+ // definition are weeded out in ConstEvaluator.evaluateAnnotation, and don't
+ // appear in the AnnoInfo.
+ MethodInfo methodInfo = requireNonNull(elements.get().get(value.getKey()));
result.put(
- factory.executableElement(elements.get().get(value.getKey()).sym()),
+ factory.executableElement(methodInfo.sym()),
annotationValue(factory, value.getValue()));
}
return result.build();
diff --git a/java/com/google/turbine/processing/TurbineAnnotationProxy.java b/java/com/google/turbine/processing/TurbineAnnotationProxy.java
index c39f310..967ead9 100644
--- a/java/com/google/turbine/processing/TurbineAnnotationProxy.java
+++ b/java/com/google/turbine/processing/TurbineAnnotationProxy.java
@@ -17,6 +17,7 @@
package com.google.turbine.processing;
import static com.google.common.base.Preconditions.checkArgument;
+import static java.util.Objects.requireNonNull;
import com.google.turbine.binder.bound.EnumConstantValue;
import com.google.turbine.binder.bound.TurbineAnnotationValue;
@@ -131,14 +132,15 @@ class TurbineAnnotationProxy implements InvocationHandler {
private static Object constArrayValue(
Class<?> returnType, ModelFactory factory, ClassLoader loader, ArrayInitValue value) {
- if (returnType.getComponentType().equals(Class.class)) {
+ Class<?> componentType = requireNonNull(returnType.getComponentType());
+ if (componentType.equals(Class.class)) {
List<TypeMirror> result = new ArrayList<>();
for (Const element : value.elements()) {
result.add(factory.asTypeMirror(((TurbineClassValue) element).type()));
}
throw new MirroredTypesException(result);
}
- Object result = Array.newInstance(returnType.getComponentType(), value.elements().size());
+ Object result = Array.newInstance(componentType, value.elements().size());
int idx = 0;
for (Const element : value.elements()) {
Object v = constValue(returnType, factory, loader, element);
diff --git a/java/com/google/turbine/processing/TurbineElement.java b/java/com/google/turbine/processing/TurbineElement.java
index c22a442..f4f1675 100644
--- a/java/com/google/turbine/processing/TurbineElement.java
+++ b/java/com/google/turbine/processing/TurbineElement.java
@@ -46,7 +46,7 @@ import com.google.turbine.diag.TurbineError.ErrorKind;
import com.google.turbine.model.Const;
import com.google.turbine.model.Const.ArrayInitValue;
import com.google.turbine.model.TurbineFlag;
-import com.google.turbine.model.TurbineTyKind;
+import com.google.turbine.tree.Tree;
import com.google.turbine.tree.Tree.MethDecl;
import com.google.turbine.tree.Tree.TyDecl;
import com.google.turbine.tree.Tree.VarDecl;
@@ -158,7 +158,8 @@ public abstract class TurbineElement implements Element {
continue;
}
if (anno.sym().equals(metadata.repeatable())) {
- ArrayInitValue arrayValue = (ArrayInitValue) anno.values().get("value");
+ // requireNonNull is safe because java.lang.annotation.Repeatable declares `value`.
+ ArrayInitValue arrayValue = (ArrayInitValue) requireNonNull(anno.values().get("value"));
for (Const element : arrayValue.elements()) {
result.add(
TurbineAnnotationProxy.create(
@@ -262,11 +263,16 @@ public abstract class TurbineElement implements Element {
return factory.asTypeMirror(info.superClassType());
}
if (info instanceof SourceTypeBoundClass) {
- // support simple name for stuff that doesn't exist
+ // support simple names for stuff that doesn't exist
TyDecl decl = ((SourceTypeBoundClass) info).decl();
if (decl.xtnds().isPresent()) {
- return factory.asTypeMirror(
- ErrorTy.create(decl.xtnds().get().name().value()));
+ ArrayDeque<Tree.Ident> flat = new ArrayDeque<>();
+ for (Tree.ClassTy curr = decl.xtnds().get();
+ curr != null;
+ curr = curr.base().orElse(null)) {
+ flat.addFirst(curr.name());
+ }
+ return factory.asTypeMirror(ErrorTy.create(flat));
}
}
return factory.noType();
@@ -785,18 +791,12 @@ public abstract class TurbineElement implements Element {
@Override
public ElementKind getKind() {
- return info().name().equals("<init>") ? ElementKind.CONSTRUCTOR : ElementKind.METHOD;
+ return sym.name().equals("<init>") ? ElementKind.CONSTRUCTOR : ElementKind.METHOD;
}
@Override
public Set<Modifier> getModifiers() {
- int access = info().access();
- if (factory.getSymbol(info().sym().owner()).kind() == TurbineTyKind.INTERFACE) {
- if ((access & (TurbineFlag.ACC_ABSTRACT | TurbineFlag.ACC_STATIC)) == 0) {
- access |= TurbineFlag.ACC_DEFAULT;
- }
- }
- return asModifierSet(ModifierOwner.METHOD, access);
+ return asModifierSet(ModifierOwner.METHOD, info().access());
}
@Override
diff --git a/java/com/google/turbine/processing/TurbineElements.java b/java/com/google/turbine/processing/TurbineElements.java
index 9da210e..7ede6e3 100644
--- a/java/com/google/turbine/processing/TurbineElements.java
+++ b/java/com/google/turbine/processing/TurbineElements.java
@@ -131,7 +131,7 @@ public class TurbineElements implements Elements {
if (!(element instanceof TurbineElement)) {
throw new IllegalArgumentException(element.toString());
}
- for (AnnoInfo a : ((TurbineTypeElement) element).annos()) {
+ for (AnnoInfo a : ((TurbineElement) element).annos()) {
if (a.sym().equals(ClassSymbol.DEPRECATED)) {
return true;
}
@@ -265,8 +265,8 @@ public class TurbineElements implements Elements {
}
/**
- * Returns true if an element with the given {@code visibility} and located in package {@from} is
- * visible to elements in package {@code to}.
+ * Returns true if an element with the given {@code visibility} and located in package {@code
+ * from} is visible to elements in package {@code to}.
*/
private static boolean isVisible(
PackageSymbol from, PackageSymbol to, TurbineVisibility visibility) {
diff --git a/java/com/google/turbine/processing/TurbineFiler.java b/java/com/google/turbine/processing/TurbineFiler.java
index 186eb7f..45cdc22 100644
--- a/java/com/google/turbine/processing/TurbineFiler.java
+++ b/java/com/google/turbine/processing/TurbineFiler.java
@@ -18,6 +18,7 @@ package com.google.turbine.processing;
import static com.google.common.base.Preconditions.checkArgument;
import static java.nio.charset.StandardCharsets.UTF_8;
+import static java.util.Objects.requireNonNull;
import com.google.common.base.Function;
import com.google.common.base.Supplier;
@@ -361,7 +362,7 @@ public class TurbineFiler implements Filer {
@Override
public URI toUri() {
try {
- return loader.getResource(path).toURI();
+ return requireNonNull(loader.getResource(path)).toURI();
} catch (URISyntaxException e) {
throw new AssertionError(e);
}
diff --git a/java/com/google/turbine/processing/TurbineProcessingEnvironment.java b/java/com/google/turbine/processing/TurbineProcessingEnvironment.java
index 726d075..8b44e75 100644
--- a/java/com/google/turbine/processing/TurbineProcessingEnvironment.java
+++ b/java/com/google/turbine/processing/TurbineProcessingEnvironment.java
@@ -26,7 +26,7 @@ import javax.lang.model.util.Elements;
import javax.lang.model.util.Types;
import org.checkerframework.checker.nullness.qual.Nullable;
-/** Turbine's {@link ProcessingEnvironment). */
+/** Turbine's {@link ProcessingEnvironment}. */
public class TurbineProcessingEnvironment implements ProcessingEnvironment {
private final Filer filer;
diff --git a/java/com/google/turbine/processing/TurbineTypes.java b/java/com/google/turbine/processing/TurbineTypes.java
index f65f921..7d2e6c0 100644
--- a/java/com/google/turbine/processing/TurbineTypes.java
+++ b/java/com/google/turbine/processing/TurbineTypes.java
@@ -825,7 +825,7 @@ public class TurbineTypes implements Types {
@Override
public List<? extends TypeMirror> directSupertypes(TypeMirror m) {
- return factory.asTypeMirrors(directSupertypes(asTurbineType(m)));
+ return factory.asTypeMirrors(deannotate(directSupertypes(asTurbineType(m))));
}
public ImmutableList<Type> directSupertypes(Type t) {
@@ -882,7 +882,12 @@ public class TurbineTypes implements Types {
@Override
public TypeMirror erasure(TypeMirror typeMirror) {
- return factory.asTypeMirror(erasure(asTurbineType(typeMirror)));
+ Type t = erasure(asTurbineType(typeMirror));
+ if (t.tyKind() == TyKind.CLASS_TY) {
+ // bug-parity with javac
+ t = deannotate(t);
+ }
+ return factory.asTypeMirror(t);
}
private Type erasure(Type type) {
@@ -896,6 +901,50 @@ public class TurbineTypes implements Types {
});
}
+ /**
+ * Remove some type annotation metadata for bug-compatibility with javac, which does this
+ * inconsistently (see https://bugs.openjdk.java.net/browse/JDK-8042981).
+ */
+ private static Type deannotate(Type ty) {
+ switch (ty.tyKind()) {
+ case CLASS_TY:
+ return deannotateClassTy((Type.ClassTy) ty);
+ case ARRAY_TY:
+ return deannotateArrayTy((Type.ArrayTy) ty);
+ case TY_VAR:
+ case INTERSECTION_TY:
+ case WILD_TY:
+ case METHOD_TY:
+ case PRIM_TY:
+ case VOID_TY:
+ case ERROR_TY:
+ case NONE_TY:
+ return ty;
+ }
+ throw new AssertionError(ty.tyKind());
+ }
+
+ private static ImmutableList<Type> deannotate(ImmutableList<Type> types) {
+ ImmutableList.Builder<Type> result = ImmutableList.builder();
+ for (Type type : types) {
+ result.add(deannotate(type));
+ }
+ return result.build();
+ }
+
+ private static Type.ArrayTy deannotateArrayTy(Type.ArrayTy ty) {
+ return ArrayTy.create(deannotate(ty.elementType()), /* annos= */ ImmutableList.of());
+ }
+
+ public static Type.ClassTy deannotateClassTy(Type.ClassTy ty) {
+ ImmutableList.Builder<Type.ClassTy.SimpleClassTy> classes = ImmutableList.builder();
+ for (Type.ClassTy.SimpleClassTy c : ty.classes()) {
+ classes.add(
+ SimpleClassTy.create(c.sym(), deannotate(c.targs()), /* annos= */ ImmutableList.of()));
+ }
+ return ClassTy.create(classes.build());
+ }
+
@Override
public TypeElement boxedClass(PrimitiveType p) {
return factory.typeElement(boxedClass(((PrimTy) asTurbineType(p)).primkind()));
diff --git a/java/com/google/turbine/type/Type.java b/java/com/google/turbine/type/Type.java
index daba2ae..bdddc6c 100644
--- a/java/com/google/turbine/type/Type.java
+++ b/java/com/google/turbine/type/Type.java
@@ -246,11 +246,14 @@ public interface Type {
@Override
public final String toString() {
StringBuilder sb = new StringBuilder();
- for (AnnoInfo anno : annos()) {
- sb.append(anno);
+ sb.append(elementType());
+ if (!annos().isEmpty()) {
sb.append(' ');
+ for (AnnoInfo anno : annos()) {
+ sb.append(anno);
+ sb.append(' ');
+ }
}
- sb.append(elementType());
sb.append("[]");
return sb.toString();
}
diff --git a/java/com/google/turbine/types/Deannotate.java b/java/com/google/turbine/types/Deannotate.java
new file mode 100644
index 0000000..1edb11f
--- /dev/null
+++ b/java/com/google/turbine/types/Deannotate.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2021 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.turbine.types;
+
+import com.google.common.collect.ImmutableList;
+import com.google.turbine.type.Type;
+
+/** Removes type annotation metadata. */
+public class Deannotate {
+ public static Type deannotate(Type ty) {
+ switch (ty.tyKind()) {
+ case CLASS_TY:
+ return deannotateClassTy((Type.ClassTy) ty);
+ case ARRAY_TY:
+ return Type.ArrayTy.create(
+ deannotate(((Type.ArrayTy) ty).elementType()), ImmutableList.of());
+ case INTERSECTION_TY:
+ return Type.IntersectionTy.create(deannotate(((Type.IntersectionTy) ty).bounds()));
+ case WILD_TY:
+ return deannotateWildTy((Type.WildTy) ty);
+ case METHOD_TY:
+ return deannotateMethodTy((Type.MethodTy) ty);
+ case PRIM_TY:
+ return Type.PrimTy.create(((Type.PrimTy) ty).primkind(), ImmutableList.of());
+ case TY_VAR:
+ return Type.TyVar.create(((Type.TyVar) ty).sym(), ImmutableList.of());
+ case VOID_TY:
+ case ERROR_TY:
+ case NONE_TY:
+ return ty;
+ }
+ throw new AssertionError(ty.tyKind());
+ }
+
+ private static ImmutableList<Type> deannotate(ImmutableList<Type> types) {
+ ImmutableList.Builder<Type> result = ImmutableList.builder();
+ for (Type type : types) {
+ result.add(deannotate(type));
+ }
+ return result.build();
+ }
+
+ public static Type.ClassTy deannotateClassTy(Type.ClassTy ty) {
+ ImmutableList.Builder<Type.ClassTy.SimpleClassTy> classes = ImmutableList.builder();
+ for (Type.ClassTy.SimpleClassTy c : ty.classes()) {
+ classes.add(
+ Type.ClassTy.SimpleClassTy.create(c.sym(), deannotate(c.targs()), ImmutableList.of()));
+ }
+ return Type.ClassTy.create(classes.build());
+ }
+
+ private static Type deannotateWildTy(Type.WildTy ty) {
+ switch (ty.boundKind()) {
+ case NONE:
+ return Type.WildUnboundedTy.create(ImmutableList.of());
+ case LOWER:
+ return Type.WildLowerBoundedTy.create(ty.bound(), ImmutableList.of());
+ case UPPER:
+ return Type.WildUpperBoundedTy.create(ty.bound(), ImmutableList.of());
+ }
+ throw new AssertionError(ty.boundKind());
+ }
+
+ private static Type deannotateMethodTy(Type.MethodTy ty) {
+ return Type.MethodTy.create(
+ ty.tyParams(),
+ deannotate(ty.returnType()),
+ ty.receiverType() != null ? deannotate(ty.receiverType()) : null,
+ deannotate(ty.parameters()),
+ deannotate(ty.thrown()));
+ }
+
+ private Deannotate() {}
+}
diff --git a/java/com/google/turbine/types/Erasure.java b/java/com/google/turbine/types/Erasure.java
index 9042897..4b6fbc1 100644
--- a/java/com/google/turbine/types/Erasure.java
+++ b/java/com/google/turbine/types/Erasure.java
@@ -19,7 +19,6 @@ package com.google.turbine.types;
import com.google.common.base.Function;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
-import com.google.turbine.binder.bound.SourceTypeBoundClass;
import com.google.turbine.binder.bound.TypeBoundClass.TyVarInfo;
import com.google.turbine.binder.sym.TyVarSymbol;
import com.google.turbine.type.Type;
@@ -32,8 +31,8 @@ import com.google.turbine.type.Type.TyVar;
import com.google.turbine.type.Type.WildTy;
/** Generic type erasure. */
-public class Erasure {
- public static Type erase(Type ty, Function<TyVarSymbol, SourceTypeBoundClass.TyVarInfo> tenv) {
+public final class Erasure {
+ public static Type erase(Type ty, Function<TyVarSymbol, TyVarInfo> tenv) {
switch (ty.tyKind()) {
case CLASS_TY:
return eraseClassTy((Type.ClassTy) ty);
@@ -70,14 +69,12 @@ public class Erasure {
return ty.bounds().isEmpty() ? ClassTy.OBJECT : erase(ty.bounds().get(0), tenv);
}
- private static Type eraseTyVar(
- TyVar ty, Function<TyVarSymbol, SourceTypeBoundClass.TyVarInfo> tenv) {
- SourceTypeBoundClass.TyVarInfo info = tenv.apply(ty.sym());
+ private static Type eraseTyVar(TyVar ty, Function<TyVarSymbol, TyVarInfo> tenv) {
+ TyVarInfo info = tenv.apply(ty.sym());
return erase(info.upperBound(), tenv);
}
- private static Type.ArrayTy eraseArrayTy(
- Type.ArrayTy ty, Function<TyVarSymbol, SourceTypeBoundClass.TyVarInfo> tenv) {
+ private static Type.ArrayTy eraseArrayTy(Type.ArrayTy ty, Function<TyVarSymbol, TyVarInfo> tenv) {
return ArrayTy.create(erase(ty.elementType(), tenv), ty.annos());
}
@@ -112,4 +109,6 @@ public class Erasure {
erase(ty.parameters(), tenv),
erase(ty.thrown(), tenv));
}
+
+ private Erasure() {}
}
diff --git a/java/com/google/turbine/zip/Zip.java b/java/com/google/turbine/zip/Zip.java
index 48d4697..fa0f0e0 100644
--- a/java/com/google/turbine/zip/Zip.java
+++ b/java/com/google/turbine/zip/Zip.java
@@ -71,7 +71,7 @@ import java.util.zip.ZipException;
* header is present only if ENDTOT in EOCD header is 0xFFFF.
* </ul>
*/
-public class Zip {
+public final class Zip {
static final int ZIP64_ENDSIG = 0x06064b50;
@@ -335,4 +335,6 @@ public class Zip {
&& (buf.get(index + 2) == i)
&& (buf.get(index + 3) == j);
}
+
+ private Zip() {}
}