aboutsummaryrefslogtreecommitdiff
path: root/java/com/google/turbine
diff options
context:
space:
mode:
Diffstat (limited to 'java/com/google/turbine')
-rw-r--r--java/com/google/turbine/binder/Binder.java154
-rw-r--r--java/com/google/turbine/binder/CanonicalTypeBinder.java7
-rw-r--r--java/com/google/turbine/binder/ClassPath.java5
-rw-r--r--java/com/google/turbine/binder/ClassPathBinder.java12
-rw-r--r--java/com/google/turbine/binder/CompUnitPreprocessor.java3
-rw-r--r--java/com/google/turbine/binder/ConstBinder.java33
-rw-r--r--java/com/google/turbine/binder/ConstEvaluator.java257
-rw-r--r--java/com/google/turbine/binder/CtSymClassBinder.java5
-rw-r--r--java/com/google/turbine/binder/DisambiguateTypeAnnotations.java68
-rw-r--r--java/com/google/turbine/binder/HierarchyBinder.java9
-rw-r--r--java/com/google/turbine/binder/JimageClassBinder.java29
-rw-r--r--java/com/google/turbine/binder/ModuleBinder.java22
-rw-r--r--java/com/google/turbine/binder/Processing.java556
-rw-r--r--java/com/google/turbine/binder/Resolve.java21
-rw-r--r--java/com/google/turbine/binder/TypeBinder.java93
-rw-r--r--java/com/google/turbine/binder/bound/AnnotationMetadata.java2
-rw-r--r--java/com/google/turbine/binder/bound/EnumConstantValue.java5
-rw-r--r--java/com/google/turbine/binder/bound/TurbineAnnotationValue.java (renamed from java/com/google/turbine/binder/bound/AnnotationValue.java)31
-rw-r--r--java/com/google/turbine/binder/bound/TypeBoundClass.java68
-rw-r--r--java/com/google/turbine/binder/bytecode/BytecodeBinder.java52
-rw-r--r--java/com/google/turbine/binder/bytecode/BytecodeBoundClass.java47
-rw-r--r--java/com/google/turbine/binder/lookup/CompoundTopLevelIndex.java8
-rw-r--r--java/com/google/turbine/binder/lookup/ImportIndex.java3
-rw-r--r--java/com/google/turbine/binder/lookup/PackageScope.java53
-rw-r--r--java/com/google/turbine/binder/lookup/SimpleTopLevelIndex.java26
-rw-r--r--java/com/google/turbine/binder/lookup/TopLevelIndex.java3
-rw-r--r--java/com/google/turbine/binder/sym/ClassSymbol.java30
-rw-r--r--java/com/google/turbine/binder/sym/FieldSymbol.java2
-rw-r--r--java/com/google/turbine/binder/sym/MethodSymbol.java11
-rw-r--r--java/com/google/turbine/binder/sym/PackageSymbol.java54
-rw-r--r--java/com/google/turbine/binder/sym/ParamSymbol.java66
-rw-r--r--java/com/google/turbine/binder/sym/Symbol.java4
-rw-r--r--java/com/google/turbine/binder/sym/TyVarSymbol.java2
-rw-r--r--java/com/google/turbine/bytecode/AnnotationWriter.java10
-rw-r--r--java/com/google/turbine/bytecode/AttributeWriter.java2
-rw-r--r--java/com/google/turbine/bytecode/ClassFile.java66
-rw-r--r--java/com/google/turbine/bytecode/ClassReader.java51
-rw-r--r--java/com/google/turbine/bytecode/ClassWriter.java2
-rw-r--r--java/com/google/turbine/bytecode/ConstantPool.java3
-rw-r--r--java/com/google/turbine/bytecode/sig/SigWriter.java4
-rw-r--r--java/com/google/turbine/deps/Dependencies.java72
-rw-r--r--java/com/google/turbine/deps/Transitive.java5
-rw-r--r--java/com/google/turbine/diag/SourceFile.java31
-rw-r--r--java/com/google/turbine/diag/TurbineDiagnostic.java101
-rw-r--r--java/com/google/turbine/diag/TurbineError.java16
-rw-r--r--java/com/google/turbine/diag/TurbineLog.java50
-rw-r--r--java/com/google/turbine/lower/Lower.java30
-rw-r--r--java/com/google/turbine/lower/LowerSignature.java20
-rw-r--r--java/com/google/turbine/main/Main.java289
-rw-r--r--java/com/google/turbine/model/Const.java116
-rw-r--r--java/com/google/turbine/options/TurbineOptions.java374
-rw-r--r--java/com/google/turbine/options/TurbineOptionsParser.java47
-rw-r--r--java/com/google/turbine/parse/ConstExpressionParser.java22
-rw-r--r--java/com/google/turbine/parse/IteratorLexer.java8
-rw-r--r--java/com/google/turbine/parse/Lexer.java3
-rw-r--r--java/com/google/turbine/parse/Parser.java125
-rw-r--r--java/com/google/turbine/parse/StreamLexer.java51
-rw-r--r--java/com/google/turbine/parse/UnicodeEscapePreprocessor.java15
-rw-r--r--java/com/google/turbine/parse/VariableInitializerParser.java11
-rw-r--r--java/com/google/turbine/processing/ClassHierarchy.java170
-rw-r--r--java/com/google/turbine/processing/ModelFactory.java391
-rw-r--r--java/com/google/turbine/processing/TurbineAnnotationMirror.java349
-rw-r--r--java/com/google/turbine/processing/TurbineAnnotationProxy.java176
-rw-r--r--java/com/google/turbine/processing/TurbineAnnotationValueMirror.java25
-rw-r--r--java/com/google/turbine/processing/TurbineElement.java1298
-rw-r--r--java/com/google/turbine/processing/TurbineElements.java360
-rw-r--r--java/com/google/turbine/processing/TurbineFiler.java435
-rw-r--r--java/com/google/turbine/processing/TurbineMessager.java252
-rw-r--r--java/com/google/turbine/processing/TurbineName.java67
-rw-r--r--java/com/google/turbine/processing/TurbineProcessingEnvironment.java105
-rw-r--r--java/com/google/turbine/processing/TurbineRoundEnvironment.java99
-rw-r--r--java/com/google/turbine/processing/TurbineTypeMirror.java770
-rw-r--r--java/com/google/turbine/processing/TurbineTypes.java1132
-rw-r--r--java/com/google/turbine/tree/Pretty.java2
-rw-r--r--java/com/google/turbine/tree/Tree.java37
-rw-r--r--java/com/google/turbine/type/AnnoInfo.java33
-rw-r--r--java/com/google/turbine/type/Type.java302
-rw-r--r--java/com/google/turbine/types/Canonicalize.java16
-rw-r--r--java/com/google/turbine/types/Erasure.java51
79 files changed, 8488 insertions, 847 deletions
diff --git a/java/com/google/turbine/binder/Binder.java b/java/com/google/turbine/binder/Binder.java
index cffe291..0e3f41f 100644
--- a/java/com/google/turbine/binder/Binder.java
+++ b/java/com/google/turbine/binder/Binder.java
@@ -16,11 +16,13 @@
package com.google.turbine.binder;
+import com.google.auto.value.AutoValue;
import com.google.common.base.Splitter;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.turbine.binder.CompUnitPreprocessor.PreprocessedCompUnit;
+import com.google.turbine.binder.Processing.ProcessorInfo;
import com.google.turbine.binder.Resolve.CanonicalResolver;
import com.google.turbine.binder.bound.BoundClass;
import com.google.turbine.binder.bound.HeaderBoundClass;
@@ -51,6 +53,7 @@ import com.google.turbine.binder.lookup.WildImportIndex;
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.TurbineError;
import com.google.turbine.diag.TurbineError.ErrorKind;
import com.google.turbine.diag.TurbineLog;
@@ -60,19 +63,56 @@ import com.google.turbine.tree.Tree;
import com.google.turbine.tree.Tree.CompUnit;
import com.google.turbine.tree.Tree.ModDecl;
import com.google.turbine.type.Type;
-import java.util.List;
+import java.time.Duration;
import java.util.Optional;
+import javax.annotation.processing.Processor;
/** The entry point for analysis. */
public class Binder {
/** Binds symbols and types to the given compilation units. */
public static BindingResult bind(
- List<CompUnit> units,
+ ImmutableList<CompUnit> units,
ClassPath classpath,
ClassPath bootclasspath,
Optional<String> moduleVersion) {
+ return bind(units, classpath, Processing.ProcessorInfo.empty(), bootclasspath, moduleVersion);
+ }
+ /** Binds symbols and types to the given compilation units. */
+ public static BindingResult bind(
+ ImmutableList<CompUnit> units,
+ ClassPath classpath,
+ ProcessorInfo processorInfo,
+ 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()) {
+ br =
+ Processing.process(
+ log, units, classpath, processorInfo, bootclasspath, br, moduleVersion);
+ }
+ log.maybeThrow();
+ return br;
+ }
+
+ static BindingResult bind(
+ TurbineLog log,
+ ImmutableList<CompUnit> units,
+ ImmutableMap<String, SourceFile> generatedSources,
+ ImmutableMap<String, byte[]> generatedClasses,
+ ClassPath classpath,
+ ClassPath bootclasspath,
+ Optional<String> moduleVersion) {
ImmutableList<PreprocessedCompUnit> preProcessedUnits = CompUnitPreprocessor.preprocess(units);
SimpleEnv<ClassSymbol, SourceBoundClass> ienv = bindSourceBoundClasses(preProcessedUnits);
@@ -91,8 +131,6 @@ public class Binder {
CompoundEnv<ModuleSymbol, ModuleInfo> classPathModuleEnv =
CompoundEnv.of(classpath.moduleEnv()).append(bootclasspath.moduleEnv());
- TurbineLog log = new TurbineLog();
-
BindPackagesResult bindPackagesResult =
bindPackages(log, ienv, tli, preProcessedUnits, classPathEnv);
@@ -108,11 +146,12 @@ public class Binder {
henv,
CompoundEnv.<ClassSymbol, HeaderBoundClass>of(classPathEnv).append(henv));
- log.maybeThrow();
-
tenv =
constants(
- syms, tenv, CompoundEnv.<ClassSymbol, TypeBoundClass>of(classPathEnv).append(tenv));
+ syms,
+ tenv,
+ CompoundEnv.<ClassSymbol, TypeBoundClass>of(classPathEnv).append(tenv),
+ log);
tenv =
disambiguateTypeAnnotations(
syms, tenv, CompoundEnv.<ClassSymbol, TypeBoundClass>of(classPathEnv).append(tenv));
@@ -125,13 +164,22 @@ public class Binder {
modules,
CompoundEnv.<ClassSymbol, TypeBoundClass>of(classPathEnv).append(tenv),
classPathModuleEnv,
- moduleVersion);
+ moduleVersion,
+ log);
ImmutableMap.Builder<ClassSymbol, SourceTypeBoundClass> result = ImmutableMap.builder();
for (ClassSymbol sym : syms) {
result.put(sym, tenv.get(sym));
}
- return new BindingResult(result.build(), boundModules, classPathEnv);
+
+ return new BindingResult(
+ result.build(),
+ boundModules,
+ classPathEnv,
+ tli,
+ generatedSources,
+ generatedClasses,
+ Statistics.empty());
}
/** Records enclosing declarations of member classes, and group classes by compilation unit. */
@@ -262,7 +310,8 @@ public class Binder {
SimpleEnv<ModuleSymbol, PackageSourceBoundModule> modules,
CompoundEnv<ClassSymbol, TypeBoundClass> env,
CompoundEnv<ModuleSymbol, ModuleInfo> moduleEnv,
- Optional<String> moduleVersion) {
+ Optional<String> moduleVersion,
+ TurbineLog log) {
// Allow resolution of modules in the current compilation. Currently this is only needed for
// version strings in requires directives.
moduleEnv =
@@ -288,7 +337,9 @@ public class Binder {
});
ImmutableList.Builder<SourceModuleInfo> bound = ImmutableList.builder();
for (PackageSourceBoundModule module : modules.asMap().values()) {
- bound.add(ModuleBinder.bind(module, env, moduleEnv, moduleVersion));
+ bound.add(
+ ModuleBinder.bind(
+ module, env, moduleEnv, moduleVersion, log.withSource(module.source())));
}
return bound.build();
}
@@ -296,7 +347,8 @@ public class Binder {
private static Env<ClassSymbol, SourceTypeBoundClass> constants(
ImmutableSet<ClassSymbol> syms,
Env<ClassSymbol, SourceTypeBoundClass> env,
- CompoundEnv<ClassSymbol, TypeBoundClass> baseEnv) {
+ CompoundEnv<ClassSymbol, TypeBoundClass> baseEnv,
+ TurbineLog log) {
// Prepare to lazily evaluate constant fields in each compilation unit.
// The laziness is necessary since constant fields can reference other
@@ -322,7 +374,8 @@ public class Binder {
info.source(),
info.scope(),
env1,
- baseEnv)
+ baseEnv,
+ log.withSource(info.source()))
.evalFieldInitializer(field.decl().init().get(), field.type());
} catch (LazyEnv.LazyBindingError e) {
// fields initializers are allowed to reference the field being initialized,
@@ -342,7 +395,9 @@ public class Binder {
SimpleEnv.Builder<ClassSymbol, SourceTypeBoundClass> builder = SimpleEnv.builder();
for (ClassSymbol sym : syms) {
- builder.put(sym, new ConstBinder(constenv, sym, baseEnv, env.get(sym)).bind());
+ SourceTypeBoundClass base = env.get(sym);
+ builder.put(
+ sym, new ConstBinder(constenv, sym, baseEnv, base, log.withSource(base.source())).bind());
}
return builder.build();
}
@@ -387,19 +442,57 @@ public class Binder {
return builder.build();
}
+ /** Statistics about annotation processing. */
+ @AutoValue
+ public abstract static class Statistics {
+
+ /**
+ * The total elapsed time spent in {@link Processor#init} and {@link Processor#process} across
+ * all rounds for each annotation processor.
+ */
+ public abstract ImmutableMap<String, Duration> processingTime();
+
+ /**
+ * Serialized protos containing processor-specific metrics. Currently only supported for Dagger.
+ */
+ public abstract ImmutableMap<String, byte[]> processorMetrics();
+
+ public static Statistics create(
+ ImmutableMap<String, Duration> processingTime,
+ ImmutableMap<String, byte[]> processorMetrics) {
+ return new AutoValue_Binder_Statistics(processingTime, processorMetrics);
+ }
+
+ public static Statistics empty() {
+ return create(ImmutableMap.of(), ImmutableMap.of());
+ }
+ }
+
/** The result of binding: bound nodes for sources in the compilation, and the classpath. */
public static class BindingResult {
private final ImmutableMap<ClassSymbol, SourceTypeBoundClass> units;
private final ImmutableList<SourceModuleInfo> modules;
private final CompoundEnv<ClassSymbol, BytecodeBoundClass> classPathEnv;
+ private final TopLevelIndex tli;
+ private final ImmutableMap<String, SourceFile> generatedSources;
+ private final ImmutableMap<String, byte[]> generatedClasses;
+ private final Statistics statistics;
public BindingResult(
ImmutableMap<ClassSymbol, SourceTypeBoundClass> units,
ImmutableList<SourceModuleInfo> modules,
- CompoundEnv<ClassSymbol, BytecodeBoundClass> classPathEnv) {
+ CompoundEnv<ClassSymbol, BytecodeBoundClass> classPathEnv,
+ TopLevelIndex tli,
+ ImmutableMap<String, SourceFile> generatedSources,
+ ImmutableMap<String, byte[]> generatedClasses,
+ Statistics statistics) {
this.units = units;
this.modules = modules;
this.classPathEnv = classPathEnv;
+ this.tli = tli;
+ this.generatedSources = generatedSources;
+ this.generatedClasses = generatedClasses;
+ this.statistics = statistics;
}
/** Bound nodes for sources in the compilation. */
@@ -415,5 +508,36 @@ public class Binder {
public CompoundEnv<ClassSymbol, BytecodeBoundClass> classPathEnv() {
return classPathEnv;
}
+
+ public TopLevelIndex tli() {
+ return tli;
+ }
+
+ public ImmutableMap<String, SourceFile> generatedSources() {
+ return generatedSources;
+ }
+
+ public ImmutableMap<String, byte[]> generatedClasses() {
+ return generatedClasses;
+ }
+
+ public Statistics statistics() {
+ return statistics;
+ }
+
+ public BindingResult withGeneratedClasses(ImmutableMap<String, byte[]> generatedClasses) {
+ return new BindingResult(
+ units, modules, classPathEnv, tli, generatedSources, generatedClasses, statistics);
+ }
+
+ public BindingResult withGeneratedSources(ImmutableMap<String, SourceFile> generatedSources) {
+ return new BindingResult(
+ units, modules, classPathEnv, tli, generatedSources, generatedClasses, statistics);
+ }
+
+ public BindingResult withStatistics(Statistics statistics) {
+ return new BindingResult(
+ units, modules, classPathEnv, tli, generatedSources, generatedClasses, statistics);
+ }
}
}
diff --git a/java/com/google/turbine/binder/CanonicalTypeBinder.java b/java/com/google/turbine/binder/CanonicalTypeBinder.java
index 934ec54..a2f045a 100644
--- a/java/com/google/turbine/binder/CanonicalTypeBinder.java
+++ b/java/com/google/turbine/binder/CanonicalTypeBinder.java
@@ -147,8 +147,8 @@ public class CanonicalTypeBinder {
ClassSymbol sym,
ParamInfo base) {
return new ParamInfo(
+ base.sym(),
Canonicalize.canonicalize(source, position, env, sym, base.type()),
- base.name(),
base.annotations(),
base.access());
}
@@ -162,8 +162,9 @@ public class CanonicalTypeBinder {
ImmutableMap.Builder<TyVarSymbol, TyVarInfo> result = ImmutableMap.builder();
for (Map.Entry<TyVarSymbol, TyVarInfo> e : tps.entrySet()) {
TyVarInfo info = e.getValue();
- Type bound = Canonicalize.canonicalize(source, position, env, sym, info.bound());
- result.put(e.getKey(), new TyVarInfo((IntersectionTy) bound, info.annotations()));
+ IntersectionTy upperBound =
+ (IntersectionTy) Canonicalize.canonicalize(source, position, env, sym, info.upperBound());
+ result.put(e.getKey(), new TyVarInfo(upperBound, /* lowerBound= */ null, info.annotations()));
}
return result.build();
}
diff --git a/java/com/google/turbine/binder/ClassPath.java b/java/com/google/turbine/binder/ClassPath.java
index d3461bf..eeea7c5 100644
--- a/java/com/google/turbine/binder/ClassPath.java
+++ b/java/com/google/turbine/binder/ClassPath.java
@@ -16,6 +16,7 @@
package com.google.turbine.binder;
+import com.google.common.base.Supplier;
import com.google.turbine.binder.bound.ModuleInfo;
import com.google.turbine.binder.bytecode.BytecodeBoundClass;
import com.google.turbine.binder.env.Env;
@@ -24,7 +25,7 @@ import com.google.turbine.binder.sym.ClassSymbol;
import com.google.turbine.binder.sym.ModuleSymbol;
/**
- * A compilation classpath, e.g. the user or platform class path. Maybe backed by a search path of
+ * A compilation classpath, e.g. the user or platform class path. May be backed by a search path of
* jar files, or a jrtfs filesystem.
*/
public interface ClassPath {
@@ -36,4 +37,6 @@ public interface ClassPath {
/** The classpath's top level index. */
TopLevelIndex index();
+
+ Supplier<byte[]> resource(String path);
}
diff --git a/java/com/google/turbine/binder/ClassPathBinder.java b/java/com/google/turbine/binder/ClassPathBinder.java
index 5d8db86..8aead80 100644
--- a/java/com/google/turbine/binder/ClassPathBinder.java
+++ b/java/com/google/turbine/binder/ClassPathBinder.java
@@ -53,6 +53,7 @@ public class ClassPathBinder {
Map<ClassSymbol, BytecodeBoundClass> transitive = new LinkedHashMap<>();
Map<ClassSymbol, BytecodeBoundClass> map = new HashMap<>();
Map<ModuleSymbol, ModuleInfo> modules = new HashMap<>();
+ Map<String, Supplier<byte[]>> resources = new HashMap<>();
Env<ClassSymbol, BytecodeBoundClass> benv =
new Env<ClassSymbol, BytecodeBoundClass>() {
@Override
@@ -62,7 +63,7 @@ public class ClassPathBinder {
};
for (Path path : paths) {
try {
- bindJar(path, map, modules, benv, transitive);
+ bindJar(path, map, modules, benv, transitive, resources);
} catch (IOException e) {
throw new IOException("error reading " + path, e);
}
@@ -89,6 +90,11 @@ public class ClassPathBinder {
public TopLevelIndex index() {
return index;
}
+
+ @Override
+ public Supplier<byte[]> resource(String path) {
+ return resources.get(path);
+ }
};
}
@@ -97,12 +103,14 @@ public class ClassPathBinder {
Map<ClassSymbol, BytecodeBoundClass> env,
Map<ModuleSymbol, ModuleInfo> modules,
Env<ClassSymbol, BytecodeBoundClass> benv,
- Map<ClassSymbol, BytecodeBoundClass> transitive)
+ Map<ClassSymbol, BytecodeBoundClass> transitive,
+ Map<String, Supplier<byte[]>> resources)
throws IOException {
// TODO(cushon): don't leak file descriptors
for (Zip.Entry ze : new Zip.ZipIterable(path)) {
String name = ze.name();
if (!name.endsWith(".class")) {
+ resources.put(name, toByteArrayOrDie(ze));
continue;
}
if (name.startsWith(TRANSITIVE_PREFIX)) {
diff --git a/java/com/google/turbine/binder/CompUnitPreprocessor.java b/java/com/google/turbine/binder/CompUnitPreprocessor.java
index 85ff1c0..ed70e88 100644
--- a/java/com/google/turbine/binder/CompUnitPreprocessor.java
+++ b/java/com/google/turbine/binder/CompUnitPreprocessor.java
@@ -219,6 +219,7 @@ public class CompUnitPreprocessor {
Optional.empty(),
ImmutableList.of(),
ImmutableList.of(),
- TurbineTyKind.INTERFACE);
+ TurbineTyKind.INTERFACE,
+ /* javadoc= */ null);
}
}
diff --git a/java/com/google/turbine/binder/ConstBinder.java b/java/com/google/turbine/binder/ConstBinder.java
index 0f989dd..3a41e94 100644
--- a/java/com/google/turbine/binder/ConstBinder.java
+++ b/java/com/google/turbine/binder/ConstBinder.java
@@ -33,6 +33,7 @@ import com.google.turbine.binder.env.Env;
import com.google.turbine.binder.sym.ClassSymbol;
import com.google.turbine.binder.sym.FieldSymbol;
import com.google.turbine.binder.sym.TyVarSymbol;
+import com.google.turbine.diag.TurbineLog.TurbineLogWithSource;
import com.google.turbine.model.Const;
import com.google.turbine.model.Const.ArrayInitValue;
import com.google.turbine.model.Const.Kind;
@@ -63,19 +64,29 @@ public class ConstBinder {
private final SourceTypeBoundClass base;
private final CompoundEnv<ClassSymbol, TypeBoundClass> env;
private final ConstEvaluator constEvaluator;
+ private final TurbineLogWithSource log;
public ConstBinder(
Env<FieldSymbol, Value> constantEnv,
ClassSymbol origin,
CompoundEnv<ClassSymbol, TypeBoundClass> env,
- SourceTypeBoundClass base) {
+ SourceTypeBoundClass base,
+ TurbineLogWithSource log) {
this.constantEnv = constantEnv;
this.origin = origin;
this.base = base;
this.env = env;
+ this.log = log;
this.constEvaluator =
new ConstEvaluator(
- origin, origin, base.memberImports(), base.source(), base.scope(), constantEnv, env);
+ origin,
+ origin,
+ base.memberImports(),
+ base.source(),
+ base.scope(),
+ constantEnv,
+ env,
+ log);
}
public SourceTypeBoundClass bind() {
@@ -87,7 +98,8 @@ public class ConstBinder {
base.source(),
base.enclosingScope(),
constantEnv,
- env)
+ env,
+ log)
.evaluateAnnotations(base.annotations());
ImmutableList<TypeBoundClass.FieldInfo> fields = fields(base.fields());
ImmutableList<MethodInfo> methods = bindMethods(base.methods());
@@ -149,7 +161,7 @@ public class ConstBinder {
private ParamInfo bindParameter(ParamInfo base) {
ImmutableList<AnnoInfo> annos = constEvaluator.evaluateAnnotations(base.annotations());
- return new ParamInfo(bindType(base.type()), base.name(), annos, base.access());
+ return new ParamInfo(base.sym(), bindType(base.type()), annos, base.access());
}
static AnnotationMetadata bindAnnotationMetadata(
@@ -161,7 +173,11 @@ public class ConstBinder {
ImmutableSet<TurbineElementType> target = null;
ClassSymbol repeatable = null;
for (AnnoInfo annotation : annotations) {
- switch (annotation.sym().binaryName()) {
+ ClassSymbol sym = annotation.sym();
+ if (sym == null) {
+ continue;
+ }
+ switch (sym.binaryName()) {
case "java/lang/annotation/Retention":
retention = bindRetention(annotation);
break;
@@ -286,7 +302,8 @@ public class ConstBinder {
result.put(
entry.getKey(),
new TyVarInfo(
- (IntersectionTy) bindType(info.bound()),
+ (IntersectionTy) bindType(info.upperBound()),
+ /* lowerBound= */ null,
constEvaluator.evaluateAnnotations(info.annotations())));
}
return result.build();
@@ -318,12 +335,12 @@ public class ConstBinder {
return WildLowerBoundedTy.create(
bindType(wildTy.bound()),
constEvaluator.evaluateAnnotations(wildTy.annotations()));
- default:
- throw new AssertionError(wildTy.boundKind());
}
+ throw new AssertionError(wildTy.boundKind());
}
case PRIM_TY:
case VOID_TY:
+ case ERROR_TY:
return type;
case INTERSECTION_TY:
return IntersectionTy.create(bindTypes(((IntersectionTy) type).bounds()));
diff --git a/java/com/google/turbine/binder/ConstEvaluator.java b/java/com/google/turbine/binder/ConstEvaluator.java
index 0e837a4..9d5f042 100644
--- a/java/com/google/turbine/binder/ConstEvaluator.java
+++ b/java/com/google/turbine/binder/ConstEvaluator.java
@@ -22,8 +22,8 @@ import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Iterables;
-import com.google.turbine.binder.bound.AnnotationValue;
import com.google.turbine.binder.bound.EnumConstantValue;
+import com.google.turbine.binder.bound.TurbineAnnotationValue;
import com.google.turbine.binder.bound.TurbineClassValue;
import com.google.turbine.binder.bound.TypeBoundClass;
import com.google.turbine.binder.bound.TypeBoundClass.FieldInfo;
@@ -38,8 +38,10 @@ import com.google.turbine.binder.sym.ClassSymbol;
import com.google.turbine.binder.sym.FieldSymbol;
import com.google.turbine.binder.sym.Symbol;
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.TurbineLogWithSource;
import com.google.turbine.model.Const;
import com.google.turbine.model.Const.ConstCastError;
import com.google.turbine.model.Const.Value;
@@ -91,14 +93,17 @@ public strictfp class ConstEvaluator {
private final Scope scope;
+ private final TurbineLogWithSource log;
+
public ConstEvaluator(
ClassSymbol origin,
ClassSymbol owner,
MemberImportIndex memberImports,
SourceFile source,
Scope scope,
- Env<FieldSymbol, Const.Value> values,
- CompoundEnv<ClassSymbol, TypeBoundClass> env) {
+ Env<FieldSymbol, Value> values,
+ CompoundEnv<ClassSymbol, TypeBoundClass> env,
+ TurbineLogWithSource log) {
this.origin = origin;
this.owner = owner;
@@ -107,6 +112,7 @@ public strictfp class ConstEvaluator {
this.values = values;
this.env = env;
this.scope = scope;
+ this.log = log;
}
/** Evaluates the given expression's value. */
@@ -139,9 +145,8 @@ public strictfp class ConstEvaluator {
case SHORT:
case BYTE:
case NULL:
- default:
- throw new AssertionError(a.constantTypeKind());
}
+ throw new AssertionError(a.constantTypeKind());
}
case VOID_TY:
throw new AssertionError(t.kind());
@@ -162,7 +167,7 @@ public strictfp class ConstEvaluator {
case ANNO_EXPR:
return evalAnno(((Tree.AnnoExpr) t).value());
default:
- throw new AssertionError(t.kind());
+ throw error(t.position(), ErrorKind.EXPRESSION_ERROR);
}
}
@@ -178,7 +183,7 @@ public strictfp class ConstEvaluator {
case VOID_TY:
return Type.VOID;
case CLASS_TY:
- return Type.ClassTy.asNonParametricClassTy(resolveClass((ClassTy) type));
+ return resolveClass((ClassTy) type);
case ARR_TY:
return Type.ArrayTy.create(
evalClassLiteralType(((Tree.ArrTy) type).elem()), ImmutableList.of());
@@ -191,19 +196,20 @@ public strictfp class ConstEvaluator {
* Resolves the {@link ClassSymbol} for the given {@link Tree.ClassTy}, with handling for
* non-canonical qualified type names.
*
- * <p>Similar to {@link HierarchyBinder#resolveClass}, except we can't unconditionally consider
+ * <p>Similar to {@code HierarchyBinder#resolveClass}, except we can't unconditionally consider
* members of the current class (e.g. when binding constants inside annotations on that class),
* and when we do want to consider members we can rely on them being in the current scope (it
* isn't completed during the hierarchy phase).
*/
- private ClassSymbol resolveClass(ClassTy classTy) {
+ private Type resolveClass(ClassTy classTy) {
ArrayDeque<Ident> flat = new ArrayDeque<>();
for (ClassTy curr = classTy; curr != null; curr = curr.base().orElse(null)) {
flat.addFirst(curr.name());
}
LookupResult result = scope.lookup(new LookupKey(ImmutableList.copyOf(flat)));
if (result == null) {
- throw error(classTy.position(), ErrorKind.CANNOT_RESOLVE, flat.peekFirst());
+ log.error(classTy.position(), ErrorKind.CANNOT_RESOLVE, flat.peekFirst());
+ return Type.ErrorTy.create(flat);
}
if (result.sym().symKind() != Symbol.Kind.CLASS) {
throw error(classTy.position(), ErrorKind.UNEXPECTED_TYPE_PARAMETER, flat.peekFirst());
@@ -212,7 +218,7 @@ public strictfp class ConstEvaluator {
for (Ident bit : result.remaining()) {
classSym = resolveNext(classTy.position(), classSym, bit);
}
- return classSym;
+ return Type.ClassTy.asNonParametricClassTy(classSym);
}
private ClassSymbol resolveNext(int position, ClassSymbol sym, Ident bit) {
@@ -372,41 +378,41 @@ public strictfp class ConstEvaluator {
}
switch (t.op()) {
case NOT:
- return unaryNegate(expr);
+ return unaryNegate(t.position(), expr);
case BITWISE_COMP:
- return bitwiseComp(expr);
+ return bitwiseComp(t.position(), expr);
case UNARY_PLUS:
- return unaryPlus(expr);
+ return unaryPlus(t.position(), expr);
case NEG:
- return unaryMinus(expr);
+ return unaryMinus(t.position(), expr);
default:
throw new AssertionError(t.op());
}
}
- private Value unaryNegate(Value expr) {
+ private Value unaryNegate(int position, Value expr) {
switch (expr.constantTypeKind()) {
case BOOLEAN:
return new Const.BooleanValue(!expr.asBoolean().value());
default:
- throw new AssertionError(expr.constantTypeKind());
+ throw error(position, ErrorKind.OPERAND_TYPE, expr.constantTypeKind());
}
}
- private Value bitwiseComp(Value expr) {
- expr = promoteUnary(expr);
+ private Value bitwiseComp(int position, Value expr) {
+ expr = promoteUnary(position, expr);
switch (expr.constantTypeKind()) {
case INT:
return new Const.IntValue(~expr.asInteger().value());
case LONG:
return new Const.LongValue(~expr.asLong().value());
default:
- throw new AssertionError(expr.constantTypeKind());
+ throw error(position, ErrorKind.OPERAND_TYPE, expr.constantTypeKind());
}
}
- private Value unaryPlus(Value expr) {
- expr = promoteUnary(expr);
+ private Value unaryPlus(int position, Value expr) {
+ expr = promoteUnary(position, expr);
switch (expr.constantTypeKind()) {
case INT:
return new Const.IntValue(+expr.asInteger().value());
@@ -417,12 +423,12 @@ public strictfp class ConstEvaluator {
case DOUBLE:
return new Const.DoubleValue(+expr.asDouble().value());
default:
- throw new AssertionError(expr.constantTypeKind());
+ throw error(position, ErrorKind.OPERAND_TYPE, expr.constantTypeKind());
}
}
- private Value unaryMinus(Value expr) {
- expr = promoteUnary(expr);
+ private Value unaryMinus(int position, Value expr) {
+ expr = promoteUnary(position, expr);
switch (expr.constantTypeKind()) {
case INT:
return new Const.IntValue(-expr.asInteger().value());
@@ -433,7 +439,7 @@ public strictfp class ConstEvaluator {
case DOUBLE:
return new Const.DoubleValue(-expr.asDouble().value());
default:
- throw new AssertionError(expr.constantTypeKind());
+ throw error(position, ErrorKind.OPERAND_TYPE, expr.constantTypeKind());
}
}
@@ -460,12 +466,12 @@ public strictfp class ConstEvaluator {
}
}
- static Const.Value add(Const.Value a, Const.Value b) {
+ private Const.Value add(int position, Const.Value a, Const.Value b) {
if (a.constantTypeKind() == TurbineConstantTypeKind.STRING
|| b.constantTypeKind() == TurbineConstantTypeKind.STRING) {
return new Const.StringValue(a.asString().value() + b.asString().value());
}
- TurbineConstantTypeKind type = promoteBinary(a, b);
+ TurbineConstantTypeKind type = promoteBinary(position, a, b);
a = coerce(a, type);
b = coerce(b, type);
switch (type) {
@@ -478,12 +484,12 @@ public strictfp class ConstEvaluator {
case DOUBLE:
return new Const.DoubleValue(a.asDouble().value() + b.asDouble().value());
default:
- throw new AssertionError(type);
+ throw error(position, ErrorKind.OPERAND_TYPE, type);
}
}
- static Const.Value subtract(Const.Value a, Const.Value b) {
- TurbineConstantTypeKind type = promoteBinary(a, b);
+ private Const.Value subtract(int position, Const.Value a, Const.Value b) {
+ TurbineConstantTypeKind type = promoteBinary(position, a, b);
a = coerce(a, type);
b = coerce(b, type);
switch (type) {
@@ -496,12 +502,12 @@ public strictfp class ConstEvaluator {
case DOUBLE:
return new Const.DoubleValue(a.asDouble().value() - b.asDouble().value());
default:
- throw new AssertionError(type);
+ throw error(position, ErrorKind.OPERAND_TYPE, type);
}
}
- static Const.Value mult(Const.Value a, Const.Value b) {
- TurbineConstantTypeKind type = promoteBinary(a, b);
+ private Const.Value mult(int position, Const.Value a, Const.Value b) {
+ TurbineConstantTypeKind type = promoteBinary(position, a, b);
a = coerce(a, type);
b = coerce(b, type);
switch (type) {
@@ -514,12 +520,12 @@ public strictfp class ConstEvaluator {
case DOUBLE:
return new Const.DoubleValue(a.asDouble().value() * b.asDouble().value());
default:
- throw new AssertionError(type);
+ throw error(position, ErrorKind.OPERAND_TYPE, type);
}
}
- static Const.Value divide(Const.Value a, Const.Value b) {
- TurbineConstantTypeKind type = promoteBinary(a, b);
+ private Const.Value divide(int position, Const.Value a, Const.Value b) {
+ TurbineConstantTypeKind type = promoteBinary(position, a, b);
a = coerce(a, type);
b = coerce(b, type);
switch (type) {
@@ -532,12 +538,12 @@ public strictfp class ConstEvaluator {
case DOUBLE:
return new Const.DoubleValue(a.asDouble().value() / b.asDouble().value());
default:
- throw new AssertionError(type);
+ throw error(position, ErrorKind.OPERAND_TYPE, type);
}
}
- static Const.Value mod(Const.Value a, Const.Value b) {
- TurbineConstantTypeKind type = promoteBinary(a, b);
+ private Const.Value mod(int position, Const.Value a, Const.Value b) {
+ TurbineConstantTypeKind type = promoteBinary(position, a, b);
a = coerce(a, type);
b = coerce(b, type);
switch (type) {
@@ -550,17 +556,17 @@ public strictfp class ConstEvaluator {
case DOUBLE:
return new Const.DoubleValue(a.asDouble().value() % b.asDouble().value());
default:
- throw new AssertionError(type);
+ throw error(position, ErrorKind.OPERAND_TYPE, type);
}
}
- static final int INT_SHIFT_MASK = 0b11111;
+ private static final int INT_SHIFT_MASK = 0b11111;
- static final int LONG_SHIFT_MASK = 0b111111;
+ private static final int LONG_SHIFT_MASK = 0b111111;
- static Const.Value shiftLeft(Const.Value a, Const.Value b) {
- a = promoteUnary(a);
- b = promoteUnary(b);
+ private Const.Value shiftLeft(int position, Const.Value a, Const.Value b) {
+ a = promoteUnary(position, a);
+ b = promoteUnary(position, b);
switch (a.constantTypeKind()) {
case INT:
return new Const.IntValue(
@@ -568,13 +574,13 @@ public strictfp class ConstEvaluator {
case LONG:
return new Const.LongValue(a.asLong().value() << (b.asInteger().value() & LONG_SHIFT_MASK));
default:
- throw new AssertionError(a.constantTypeKind());
+ throw error(position, ErrorKind.OPERAND_TYPE, a.constantTypeKind());
}
}
- static Const.Value shiftRight(Const.Value a, Const.Value b) {
- a = promoteUnary(a);
- b = promoteUnary(b);
+ private Const.Value shiftRight(int position, Const.Value a, Const.Value b) {
+ a = promoteUnary(position, a);
+ b = promoteUnary(position, b);
switch (a.constantTypeKind()) {
case INT:
return new Const.IntValue(
@@ -582,13 +588,13 @@ public strictfp class ConstEvaluator {
case LONG:
return new Const.LongValue(a.asLong().value() >> (b.asInteger().value() & LONG_SHIFT_MASK));
default:
- throw new AssertionError(a.constantTypeKind());
+ throw error(position, ErrorKind.OPERAND_TYPE, a.constantTypeKind());
}
}
- static Const.Value unsignedShiftRight(Const.Value a, Const.Value b) {
- a = promoteUnary(a);
- b = promoteUnary(b);
+ private Const.Value unsignedShiftRight(int position, Const.Value a, Const.Value b) {
+ a = promoteUnary(position, a);
+ b = promoteUnary(position, b);
switch (a.constantTypeKind()) {
case INT:
return new Const.IntValue(
@@ -597,12 +603,12 @@ public strictfp class ConstEvaluator {
return new Const.LongValue(
a.asLong().value() >>> (b.asInteger().value() & LONG_SHIFT_MASK));
default:
- throw new AssertionError(a.constantTypeKind());
+ throw error(position, ErrorKind.OPERAND_TYPE, a.constantTypeKind());
}
}
- static Const.Value lessThan(Const.Value a, Const.Value b) {
- TurbineConstantTypeKind type = promoteBinary(a, b);
+ private Const.Value lessThan(int position, Const.Value a, Const.Value b) {
+ TurbineConstantTypeKind type = promoteBinary(position, a, b);
a = coerce(a, type);
b = coerce(b, type);
switch (type) {
@@ -615,12 +621,12 @@ public strictfp class ConstEvaluator {
case DOUBLE:
return new Const.BooleanValue(a.asDouble().value() < b.asDouble().value());
default:
- throw new AssertionError(type);
+ throw error(position, ErrorKind.OPERAND_TYPE, type);
}
}
- static Const.Value lessThanEqual(Const.Value a, Const.Value b) {
- TurbineConstantTypeKind type = promoteBinary(a, b);
+ private Const.Value lessThanEqual(int position, Const.Value a, Const.Value b) {
+ TurbineConstantTypeKind type = promoteBinary(position, a, b);
a = coerce(a, type);
b = coerce(b, type);
switch (type) {
@@ -633,12 +639,12 @@ public strictfp class ConstEvaluator {
case DOUBLE:
return new Const.BooleanValue(a.asDouble().value() <= b.asDouble().value());
default:
- throw new AssertionError(type);
+ throw error(position, ErrorKind.OPERAND_TYPE, type);
}
}
- static Const.Value greaterThan(Const.Value a, Const.Value b) {
- TurbineConstantTypeKind type = promoteBinary(a, b);
+ private Const.Value greaterThan(int position, Const.Value a, Const.Value b) {
+ TurbineConstantTypeKind type = promoteBinary(position, a, b);
a = coerce(a, type);
b = coerce(b, type);
switch (type) {
@@ -651,12 +657,12 @@ public strictfp class ConstEvaluator {
case DOUBLE:
return new Const.BooleanValue(a.asDouble().value() > b.asDouble().value());
default:
- throw new AssertionError(type);
+ throw error(position, ErrorKind.OPERAND_TYPE, type);
}
}
- static Const.Value greaterThanEqual(Const.Value a, Const.Value b) {
- TurbineConstantTypeKind type = promoteBinary(a, b);
+ private Const.Value greaterThanEqual(int position, Const.Value a, Const.Value b) {
+ TurbineConstantTypeKind type = promoteBinary(position, a, b);
a = coerce(a, type);
b = coerce(b, type);
switch (type) {
@@ -669,11 +675,11 @@ public strictfp class ConstEvaluator {
case DOUBLE:
return new Const.BooleanValue(a.asDouble().value() >= b.asDouble().value());
default:
- throw new AssertionError(type);
+ throw error(position, ErrorKind.OPERAND_TYPE, type);
}
}
- static Const.Value equal(Const.Value a, Const.Value b) {
+ private Const.Value equal(int position, Const.Value a, Const.Value b) {
switch (a.constantTypeKind()) {
case STRING:
return new Const.BooleanValue(a.asString().value().equals(b.asString().value()));
@@ -682,7 +688,7 @@ public strictfp class ConstEvaluator {
default:
break;
}
- TurbineConstantTypeKind type = promoteBinary(a, b);
+ TurbineConstantTypeKind type = promoteBinary(position, a, b);
a = coerce(a, type);
b = coerce(b, type);
switch (type) {
@@ -695,11 +701,11 @@ public strictfp class ConstEvaluator {
case DOUBLE:
return new Const.BooleanValue(a.asDouble().value() == b.asDouble().value());
default:
- throw new AssertionError(type);
+ throw error(position, ErrorKind.OPERAND_TYPE, type);
}
}
- static Const.Value notEqual(Const.Value a, Const.Value b) {
+ private Const.Value notEqual(int position, Const.Value a, Const.Value b) {
switch (a.constantTypeKind()) {
case STRING:
return new Const.BooleanValue(!a.asString().value().equals(b.asString().value()));
@@ -708,7 +714,7 @@ public strictfp class ConstEvaluator {
default:
break;
}
- TurbineConstantTypeKind type = promoteBinary(a, b);
+ TurbineConstantTypeKind type = promoteBinary(position, a, b);
a = coerce(a, type);
b = coerce(b, type);
switch (type) {
@@ -721,18 +727,18 @@ public strictfp class ConstEvaluator {
case DOUBLE:
return new Const.BooleanValue(a.asDouble().value() != b.asDouble().value());
default:
- throw new AssertionError(type);
+ throw error(position, ErrorKind.OPERAND_TYPE, type);
}
}
- static Const.Value bitwiseAnd(Const.Value a, Const.Value b) {
+ private Const.Value bitwiseAnd(int position, Const.Value a, Const.Value b) {
switch (a.constantTypeKind()) {
case BOOLEAN:
return new Const.BooleanValue(a.asBoolean().value() & b.asBoolean().value());
default:
break;
}
- TurbineConstantTypeKind type = promoteBinary(a, b);
+ TurbineConstantTypeKind type = promoteBinary(position, a, b);
a = coerce(a, type);
b = coerce(b, type);
switch (type) {
@@ -741,18 +747,18 @@ public strictfp class ConstEvaluator {
case LONG:
return new Const.LongValue(a.asLong().value() & b.asLong().value());
default:
- throw new AssertionError(type);
+ throw error(position, ErrorKind.OPERAND_TYPE, type);
}
}
- static Const.Value bitwiseOr(Const.Value a, Const.Value b) {
+ private Const.Value bitwiseOr(int position, Const.Value a, Const.Value b) {
switch (a.constantTypeKind()) {
case BOOLEAN:
return new Const.BooleanValue(a.asBoolean().value() | b.asBoolean().value());
default:
break;
}
- TurbineConstantTypeKind type = promoteBinary(a, b);
+ TurbineConstantTypeKind type = promoteBinary(position, a, b);
a = coerce(a, type);
b = coerce(b, type);
switch (type) {
@@ -761,18 +767,18 @@ public strictfp class ConstEvaluator {
case LONG:
return new Const.LongValue(a.asLong().value() | b.asLong().value());
default:
- throw new AssertionError(type);
+ throw error(position, ErrorKind.OPERAND_TYPE, type);
}
}
- static Const.Value bitwiseXor(Const.Value a, Const.Value b) {
+ private Const.Value bitwiseXor(int position, Const.Value a, Const.Value b) {
switch (a.constantTypeKind()) {
case BOOLEAN:
return new Const.BooleanValue(a.asBoolean().value() ^ b.asBoolean().value());
default:
break;
}
- TurbineConstantTypeKind type = promoteBinary(a, b);
+ TurbineConstantTypeKind type = promoteBinary(position, a, b);
a = coerce(a, type);
b = coerce(b, type);
switch (type) {
@@ -781,7 +787,7 @@ public strictfp class ConstEvaluator {
case LONG:
return new Const.LongValue(a.asLong().value() ^ b.asLong().value());
default:
- throw new AssertionError(type);
+ throw error(position, ErrorKind.OPERAND_TYPE, type);
}
}
@@ -793,49 +799,49 @@ public strictfp class ConstEvaluator {
}
switch (t.op()) {
case PLUS:
- return add(lhs, rhs);
+ return add(t.position(), lhs, rhs);
case MINUS:
- return subtract(lhs, rhs);
+ return subtract(t.position(), lhs, rhs);
case MULT:
- return mult(lhs, rhs);
+ return mult(t.position(), lhs, rhs);
case DIVIDE:
- return divide(lhs, rhs);
+ return divide(t.position(), lhs, rhs);
case MODULO:
- return mod(lhs, rhs);
+ return mod(t.position(), lhs, rhs);
case SHIFT_LEFT:
- return shiftLeft(lhs, rhs);
+ return shiftLeft(t.position(), lhs, rhs);
case SHIFT_RIGHT:
- return shiftRight(lhs, rhs);
+ return shiftRight(t.position(), lhs, rhs);
case UNSIGNED_SHIFT_RIGHT:
- return unsignedShiftRight(lhs, rhs);
+ return unsignedShiftRight(t.position(), lhs, rhs);
case LESS_THAN:
- return lessThan(lhs, rhs);
+ return lessThan(t.position(), lhs, rhs);
case GREATER_THAN:
- return greaterThan(lhs, rhs);
+ return greaterThan(t.position(), lhs, rhs);
case LESS_THAN_EQ:
- return lessThanEqual(lhs, rhs);
+ return lessThanEqual(t.position(), lhs, rhs);
case GREATER_THAN_EQ:
- return greaterThanEqual(lhs, rhs);
+ return greaterThanEqual(t.position(), lhs, rhs);
case EQUAL:
- return equal(lhs, rhs);
+ return equal(t.position(), lhs, rhs);
case NOT_EQUAL:
- return notEqual(lhs, rhs);
+ return notEqual(t.position(), lhs, rhs);
case AND:
return new Const.BooleanValue(lhs.asBoolean().value() && rhs.asBoolean().value());
case OR:
return new Const.BooleanValue(lhs.asBoolean().value() || rhs.asBoolean().value());
case BITWISE_AND:
- return bitwiseAnd(lhs, rhs);
+ return bitwiseAnd(t.position(), lhs, rhs);
case BITWISE_XOR:
- return bitwiseXor(lhs, rhs);
+ return bitwiseXor(t.position(), lhs, rhs);
case BITWISE_OR:
- return bitwiseOr(lhs, rhs);
+ return bitwiseOr(t.position(), lhs, rhs);
default:
throw new AssertionError(t.op());
}
}
- private static Const.Value promoteUnary(Const.Value v) {
+ private Const.Value promoteUnary(int position, Value v) {
switch (v.constantTypeKind()) {
case CHAR:
case SHORT:
@@ -847,13 +853,13 @@ public strictfp class ConstEvaluator {
case DOUBLE:
return v;
default:
- throw new AssertionError(v.constantTypeKind());
+ throw error(position, ErrorKind.OPERAND_TYPE, v.constantTypeKind());
}
}
- private static TurbineConstantTypeKind promoteBinary(Const.Value a, Const.Value b) {
- a = promoteUnary(a);
- b = promoteUnary(b);
+ private TurbineConstantTypeKind promoteBinary(int position, Const.Value a, Const.Value b) {
+ a = promoteUnary(position, a);
+ b = promoteUnary(position, b);
switch (a.constantTypeKind()) {
case INT:
switch (b.constantTypeKind()) {
@@ -863,7 +869,7 @@ public strictfp class ConstEvaluator {
case FLOAT:
return b.constantTypeKind();
default:
- throw new AssertionError(b.constantTypeKind());
+ throw error(position, ErrorKind.OPERAND_TYPE, b.constantTypeKind());
}
case LONG:
switch (b.constantTypeKind()) {
@@ -874,7 +880,7 @@ public strictfp class ConstEvaluator {
case FLOAT:
return b.constantTypeKind();
default:
- throw new AssertionError(b.constantTypeKind());
+ throw error(position, ErrorKind.OPERAND_TYPE, b.constantTypeKind());
}
case FLOAT:
switch (b.constantTypeKind()) {
@@ -885,7 +891,7 @@ public strictfp class ConstEvaluator {
case DOUBLE:
return TurbineConstantTypeKind.DOUBLE;
default:
- throw new AssertionError(b.constantTypeKind());
+ throw error(position, ErrorKind.OPERAND_TYPE, b.constantTypeKind());
}
case DOUBLE:
switch (b.constantTypeKind()) {
@@ -895,10 +901,10 @@ public strictfp class ConstEvaluator {
case DOUBLE:
return TurbineConstantTypeKind.DOUBLE;
default:
- throw new AssertionError(b.constantTypeKind());
+ throw error(position, ErrorKind.OPERAND_TYPE, b.constantTypeKind());
}
default:
- throw new AssertionError(a.constantTypeKind());
+ throw error(position, ErrorKind.OPERAND_TYPE, a.constantTypeKind());
}
}
@@ -915,9 +921,17 @@ public strictfp class ConstEvaluator {
* expression trees.
*/
AnnoInfo evaluateAnnotation(AnnoInfo info) {
+ // bail if annotation has not been resolved
+ if (info.sym() == null) {
+ return info;
+ }
+
Map<String, Type> template = new LinkedHashMap<>();
- for (MethodInfo method : env.get(info.sym()).methods()) {
- template.put(method.name(), method.returnType());
+ TypeBoundClass annoClass = env.get(info.sym());
+ if (annoClass != null) {
+ for (MethodInfo method : annoClass.methods()) {
+ template.put(method.name(), method.returnType());
+ }
}
Map<String, Const> values = new LinkedHashMap<>();
@@ -952,7 +966,7 @@ public strictfp class ConstEvaluator {
return info.withValues(ImmutableMap.copyOf(values));
}
- private AnnotationValue evalAnno(Tree.Anno t) {
+ private TurbineAnnotationValue evalAnno(Tree.Anno t) {
LookupResult result = scope.lookup(new LookupKey(t.name()));
if (result == null) {
throw error(
@@ -968,8 +982,8 @@ public strictfp class ConstEvaluator {
if (sym == null) {
return null;
}
- AnnoInfo annoInfo = evaluateAnnotation(new AnnoInfo(source, sym, t, null));
- return new AnnotationValue(annoInfo.sym(), annoInfo.values());
+ AnnoInfo annoInfo = evaluateAnnotation(new AnnoInfo(source, sym, t, ImmutableMap.of()));
+ return new TurbineAnnotationValue(annoInfo);
}
private Const.ArrayInitValue evalArrayInit(ArrayInit t) {
@@ -994,6 +1008,9 @@ public strictfp class ConstEvaluator {
}
switch (ty.tyKind()) {
case PRIM_TY:
+ if (!(value instanceof Const.Value)) {
+ throw error(tree.position(), ErrorKind.EXPRESSION_ERROR);
+ }
return coerce((Const.Value) value, ((Type.PrimTy) ty).primkind());
case CLASS_TY:
case TY_VAR:
@@ -1027,7 +1044,17 @@ public strictfp class ConstEvaluator {
return null;
}
return (Const.Value) cast(type, value);
- } catch (TurbineError | ConstCastError error) {
+ } catch (TurbineError error) {
+ for (TurbineDiagnostic diagnostic : error.diagnostics()) {
+ switch (diagnostic.kind()) {
+ case CANNOT_RESOLVE:
+ // assume this wasn't a constant
+ return null;
+ default: // fall out
+ }
+ }
+ throw error;
+ } catch (ConstCastError error) {
return null;
}
}
diff --git a/java/com/google/turbine/binder/CtSymClassBinder.java b/java/com/google/turbine/binder/CtSymClassBinder.java
index 84fa966..a6f1b3d 100644
--- a/java/com/google/turbine/binder/CtSymClassBinder.java
+++ b/java/com/google/turbine/binder/CtSymClassBinder.java
@@ -105,6 +105,11 @@ public class CtSymClassBinder {
public TopLevelIndex index() {
return index;
}
+
+ @Override
+ public Supplier<byte[]> resource(String input) {
+ return null;
+ }
};
}
diff --git a/java/com/google/turbine/binder/DisambiguateTypeAnnotations.java b/java/com/google/turbine/binder/DisambiguateTypeAnnotations.java
index 9ad20d2..7e3fbda 100644
--- a/java/com/google/turbine/binder/DisambiguateTypeAnnotations.java
+++ b/java/com/google/turbine/binder/DisambiguateTypeAnnotations.java
@@ -18,13 +18,14 @@ package com.google.turbine.binder;
import static com.google.common.collect.Iterables.getOnlyElement;
-import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableList.Builder;
import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Multimap;
-import com.google.turbine.binder.bound.AnnotationValue;
+import com.google.common.collect.MultimapBuilder;
+import com.google.turbine.binder.bound.AnnotationMetadata;
import com.google.turbine.binder.bound.SourceTypeBoundClass;
+import com.google.turbine.binder.bound.TurbineAnnotationValue;
import com.google.turbine.binder.bound.TypeBoundClass;
import com.google.turbine.binder.bound.TypeBoundClass.FieldInfo;
import com.google.turbine.binder.bound.TypeBoundClass.MethodInfo;
@@ -44,7 +45,6 @@ import com.google.turbine.type.Type.PrimTy;
import com.google.turbine.type.Type.TyVar;
import java.util.Collection;
import java.util.Map;
-import java.util.Set;
/**
* Disambiguate annotations on field, parameter, and method return types that could be declaration
@@ -139,7 +139,7 @@ public class DisambiguateTypeAnnotations {
base.type(),
base.annotations(),
declarationAnnotations);
- return new ParamInfo(type, base.name(), declarationAnnotations.build(), base.access());
+ return new ParamInfo(base.sym(), type, declarationAnnotations.build(), base.access());
}
/**
@@ -151,13 +151,13 @@ public class DisambiguateTypeAnnotations {
TurbineElementType declarationTarget,
Type type,
ImmutableList<AnnoInfo> annotations,
- Builder<AnnoInfo> declarationAnnotations) {
+ ImmutableList.Builder<AnnoInfo> declarationAnnotations) {
// desugar @Repeatable annotations before disambiguating: annotation containers may target
// a subset of the types targeted by their element annotation
annotations = groupRepeated(env, annotations);
ImmutableList.Builder<AnnoInfo> typeAnnotations = ImmutableList.builder();
for (AnnoInfo anno : annotations) {
- Set<TurbineElementType> target = env.get(anno.sym()).annotationMetadata().target();
+ ImmutableSet<TurbineElementType> target = getTarget(env, anno);
if (target.contains(TurbineElementType.TYPE_USE)) {
typeAnnotations.add(anno);
}
@@ -168,6 +168,23 @@ public class DisambiguateTypeAnnotations {
return addAnnotationsToType(type, typeAnnotations.build());
}
+ private static ImmutableSet<TurbineElementType> getTarget(
+ Env<ClassSymbol, TypeBoundClass> env, AnnoInfo anno) {
+ ClassSymbol sym = anno.sym();
+ if (sym == null) {
+ return AnnotationMetadata.DEFAULT_TARGETS;
+ }
+ TypeBoundClass info = env.get(sym);
+ if (info == null) {
+ return AnnotationMetadata.DEFAULT_TARGETS;
+ }
+ AnnotationMetadata metadata = info.annotationMetadata();
+ if (metadata == null) {
+ return AnnotationMetadata.DEFAULT_TARGETS;
+ }
+ return metadata.target();
+ }
+
private static ImmutableList<FieldInfo> bindFields(
Env<ClassSymbol, TypeBoundClass> env, ImmutableList<FieldInfo> fields) {
ImmutableList.Builder<FieldInfo> result = ImmutableList.builder();
@@ -218,6 +235,7 @@ public class DisambiguateTypeAnnotations {
TyVar tyVar = (TyVar) type;
return Type.TyVar.create(tyVar.sym(), appendAnnotations(tyVar.annos(), extra));
case VOID_TY:
+ case ERROR_TY:
return type;
case WILD_TY:
throw new AssertionError("unexpected wildcard type outside type argument context");
@@ -242,21 +260,33 @@ public class DisambiguateTypeAnnotations {
*/
public static ImmutableList<AnnoInfo> groupRepeated(
Env<ClassSymbol, TypeBoundClass> env, ImmutableList<AnnoInfo> annotations) {
- Multimap<ClassSymbol, AnnoInfo> repeated = ArrayListMultimap.create();
+ Multimap<ClassSymbol, AnnoInfo> repeated =
+ MultimapBuilder.linkedHashKeys().arrayListValues().build();
+ ImmutableList.Builder<AnnoInfo> result = ImmutableList.builder();
for (AnnoInfo anno : annotations) {
+ if (anno.sym() == null) {
+ result.add(anno);
+ continue;
+ }
repeated.put(anno.sym(), anno);
}
- Builder<AnnoInfo> result = ImmutableList.builder();
for (Map.Entry<ClassSymbol, Collection<AnnoInfo>> entry : repeated.asMap().entrySet()) {
ClassSymbol symbol = entry.getKey();
Collection<AnnoInfo> infos = entry.getValue();
if (infos.size() > 1) {
- Builder<Const> elements = ImmutableList.builder();
+ ImmutableList.Builder<Const> elements = ImmutableList.builder();
for (AnnoInfo element : infos) {
- elements.add(new AnnotationValue(element.sym(), element.values()));
+ elements.add(new TurbineAnnotationValue(element));
}
- ClassSymbol container = env.get(symbol).annotationMetadata().repeatable();
+ TypeBoundClass info = env.get(symbol);
+ if (info == null || info.annotationMetadata() == null) {
+ continue;
+ }
+ ClassSymbol container = info.annotationMetadata().repeatable();
if (container == null) {
+ if (isKotlinRepeatable(info)) {
+ continue;
+ }
AnnoInfo anno = infos.iterator().next();
throw TurbineError.format(
anno.source(), anno.position(), ErrorKind.NONREPEATABLE_ANNOTATION, symbol);
@@ -273,4 +303,18 @@ public class DisambiguateTypeAnnotations {
}
return result.build();
}
+
+ // Work-around for https://youtrack.jetbrains.net/issue/KT-34189.
+ // Kotlin stubs include repeated annotations that are valid in Kotlin (i.e. meta-annotated with
+ // @kotlin.annotation.Repeatable), even though they are invalid Java.
+ // TODO(b/142002426): kill this with fire
+ static boolean isKotlinRepeatable(TypeBoundClass info) {
+ for (AnnoInfo metaAnno : info.annotations()) {
+ if (metaAnno.sym() != null
+ && metaAnno.sym().binaryName().equals("kotlin/annotation/Repeatable")) {
+ return true;
+ }
+ }
+ return false;
+ }
}
diff --git a/java/com/google/turbine/binder/HierarchyBinder.java b/java/com/google/turbine/binder/HierarchyBinder.java
index 8549d3a..07d255c 100644
--- a/java/com/google/turbine/binder/HierarchyBinder.java
+++ b/java/com/google/turbine/binder/HierarchyBinder.java
@@ -16,6 +16,7 @@
package com.google.turbine.binder;
+import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.turbine.binder.bound.HeaderBoundClass;
@@ -68,6 +69,9 @@ public class HierarchyBinder {
ClassSymbol superclass;
if (decl.xtnds().isPresent()) {
superclass = resolveClass(decl.xtnds().get());
+ if (origin.equals(superclass)) {
+ log.error(decl.xtnds().get().position(), ErrorKind.CYCLIC_HIERARCHY, origin);
+ }
} else {
switch (decl.tykind()) {
case ENUM:
@@ -90,6 +94,9 @@ public class HierarchyBinder {
if (result == null) {
continue;
}
+ if (origin.equals(result)) {
+ log.error(i.position(), ErrorKind.CYCLIC_HIERARCHY, origin);
+ }
interfaces.add(result);
}
} else {
@@ -120,7 +127,7 @@ public class HierarchyBinder {
// Resolve the base symbol in the qualified name.
LookupResult result = lookup(ty, new LookupKey(ImmutableList.copyOf(flat)));
if (result == null) {
- log.error(ty.position(), ErrorKind.CANNOT_RESOLVE, ty);
+ log.error(ty.position(), ErrorKind.CANNOT_RESOLVE, Joiner.on('.').join(flat));
return null;
}
// Resolve pieces in the qualified name referring to member types.
diff --git a/java/com/google/turbine/binder/JimageClassBinder.java b/java/com/google/turbine/binder/JimageClassBinder.java
index e3d0865..d11dda1 100644
--- a/java/com/google/turbine/binder/JimageClassBinder.java
+++ b/java/com/google/turbine/binder/JimageClassBinder.java
@@ -33,6 +33,7 @@ import com.google.turbine.binder.bytecode.BytecodeBoundClass;
import com.google.turbine.binder.env.Env;
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;
@@ -150,7 +151,7 @@ public class JimageClassBinder {
String binaryName = modulePath.relativize(path).toString();
binaryName = binaryName.substring(0, binaryName.length() - ".class".length());
ClassSymbol sym = new ClassSymbol(binaryName);
- packageClassesBySimpleName.put(packageName, simpleName(sym), sym);
+ packageClassesBySimpleName.put(packageName, sym.simpleName(), sym);
JimageClassBinder.this.env.put(
sym, new BytecodeBoundClass(sym, toByteArrayOrDie(path), env, path.toString()));
}
@@ -176,16 +177,6 @@ public class JimageClassBinder {
});
}
- private static String simpleName(ClassSymbol sym) {
- int idx = sym.binaryName().lastIndexOf('/');
- return idx != -1 ? sym.binaryName().substring(idx + 1) : sym.binaryName();
- }
-
- private static String packageName(ClassSymbol sym) {
- int idx = sym.binaryName().lastIndexOf('/');
- return idx != -1 ? sym.binaryName().substring(0, idx) : "";
- }
-
private class JimageTopLevelIndex implements TopLevelIndex {
final Scope topLevelScope =
@@ -222,18 +213,23 @@ public class JimageClassBinder {
}
@Override
- public Scope lookupPackage(ImmutableList<String> name) {
+ public PackageScope lookupPackage(Iterable<String> name) {
String packageName = Joiner.on('/').join(name);
if (!initPackage(packageName)) {
return null;
}
- return new Scope() {
+ return new PackageScope() {
@Nullable
@Override
public LookupResult lookup(LookupKey lookupKey) {
ClassSymbol sym = packageClassesBySimpleName.get(packageName, lookupKey.first().value());
return sym != null ? new LookupResult(sym, lookupKey) : null;
}
+
+ @Override
+ public Iterable<ClassSymbol> classes() {
+ return packageClassesBySimpleName.row(packageName).values();
+ }
};
}
}
@@ -247,7 +243,7 @@ public class JimageClassBinder {
return new Env<ClassSymbol, BytecodeBoundClass>() {
@Override
public BytecodeBoundClass get(ClassSymbol sym) {
- return initPackage(packageName(sym)) ? env.get(sym) : null;
+ return initPackage(sym.packageName()) ? env.get(sym) : null;
}
};
}
@@ -266,5 +262,10 @@ public class JimageClassBinder {
public TopLevelIndex index() {
return index;
}
+
+ @Override
+ public Supplier<byte[]> resource(String input) {
+ return null;
+ }
}
}
diff --git a/java/com/google/turbine/binder/ModuleBinder.java b/java/com/google/turbine/binder/ModuleBinder.java
index afd8770..748ff39 100644
--- a/java/com/google/turbine/binder/ModuleBinder.java
+++ b/java/com/google/turbine/binder/ModuleBinder.java
@@ -40,6 +40,7 @@ import com.google.turbine.binder.sym.ClassSymbol;
import com.google.turbine.binder.sym.ModuleSymbol;
import com.google.turbine.diag.TurbineError;
import com.google.turbine.diag.TurbineError.ErrorKind;
+import com.google.turbine.diag.TurbineLog.TurbineLogWithSource;
import com.google.turbine.model.TurbineFlag;
import com.google.turbine.tree.Tree;
import com.google.turbine.tree.Tree.Ident;
@@ -60,8 +61,9 @@ public class ModuleBinder {
PackageSourceBoundModule module,
CompoundEnv<ClassSymbol, TypeBoundClass> env,
Env<ModuleSymbol, ModuleInfo> moduleEnv,
- Optional<String> moduleVersion) {
- return new ModuleBinder(module, env, moduleEnv, moduleVersion).bind();
+ Optional<String> moduleVersion,
+ TurbineLogWithSource log) {
+ return new ModuleBinder(module, env, moduleEnv, moduleVersion, log).bind();
}
private final PackageSourceBoundModule module;
@@ -69,16 +71,19 @@ public class ModuleBinder {
private final Env<ModuleSymbol, ModuleInfo> moduleEnv;
private final Optional<String> moduleVersion;
private final CompoundScope scope;
+ private final TurbineLogWithSource log;
public ModuleBinder(
PackageSourceBoundModule module,
CompoundEnv<ClassSymbol, TypeBoundClass> env,
Env<ModuleSymbol, ModuleInfo> moduleEnv,
- Optional<String> moduleVersion) {
+ Optional<String> moduleVersion,
+ TurbineLogWithSource log) {
this.module = module;
this.env = env;
this.moduleEnv = moduleEnv;
this.moduleVersion = moduleVersion;
+ this.log = log;
this.scope = module.scope().toScope(Resolve.resolveFunction(env, /* origin= */ null));
}
@@ -92,11 +97,12 @@ public class ModuleBinder {
module.source(),
scope,
/* values= */ new SimpleEnv<>(ImmutableMap.of()),
- env);
+ env,
+ log);
ImmutableList.Builder<AnnoInfo> annoInfos = ImmutableList.builder();
for (Tree.Anno annoTree : module.module().annos()) {
ClassSymbol sym = resolve(annoTree.position(), annoTree.name());
- annoInfos.add(new AnnoInfo(module.source(), sym, annoTree, null));
+ annoInfos.add(new AnnoInfo(module.source(), sym, annoTree, ImmutableMap.of()));
}
ImmutableList<AnnoInfo> annos = constEvaluator.evaluateAnnotations(annoInfos.build());
@@ -130,8 +136,6 @@ public class ModuleBinder {
case PROVIDES:
provides.add(bindProvides((ModProvides) directive));
break;
- default:
- throw new AssertionError(directive.kind());
}
}
if (!requiresJavaBase) {
@@ -181,11 +185,11 @@ public class ModuleBinder {
return new RequireInfo(moduleName, flags, requires != null ? requires.version() : null);
}
- private ExportInfo bindExports(ModExports directive) {
+ private static ExportInfo bindExports(ModExports directive) {
return new ExportInfo(directive.packageName(), directive.moduleNames());
}
- private OpenInfo bindOpens(ModOpens directive) {
+ private static OpenInfo bindOpens(ModOpens directive) {
return new OpenInfo(directive.packageName(), directive.moduleNames());
}
diff --git a/java/com/google/turbine/binder/Processing.java b/java/com/google/turbine/binder/Processing.java
new file mode 100644
index 0000000..ecdf195
--- /dev/null
+++ b/java/com/google/turbine/binder/Processing.java
@@ -0,0 +1,556 @@
+/*
+ * Copyright 2019 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.auto.value.AutoValue;
+import com.google.common.base.Function;
+import com.google.common.base.Joiner;
+import com.google.common.base.Stopwatch;
+import com.google.common.base.Supplier;
+import com.google.common.base.Throwables;
+import com.google.common.collect.ImmutableList;
+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.turbine.binder.Binder.BindingResult;
+import com.google.turbine.binder.Binder.Statistics;
+import com.google.turbine.binder.bound.SourceTypeBoundClass;
+import com.google.turbine.binder.bound.TypeBoundClass;
+import com.google.turbine.binder.bound.TypeBoundClass.FieldInfo;
+import com.google.turbine.binder.env.CompoundEnv;
+import com.google.turbine.binder.env.Env;
+import com.google.turbine.binder.env.SimpleEnv;
+import com.google.turbine.binder.sym.ClassSymbol;
+import com.google.turbine.binder.sym.Symbol;
+import com.google.turbine.diag.SourceFile;
+import com.google.turbine.diag.TurbineLog;
+import com.google.turbine.parse.Parser;
+import com.google.turbine.processing.ModelFactory;
+import com.google.turbine.processing.TurbineElements;
+import com.google.turbine.processing.TurbineFiler;
+import com.google.turbine.processing.TurbineMessager;
+import com.google.turbine.processing.TurbineProcessingEnvironment;
+import com.google.turbine.processing.TurbineRoundEnvironment;
+import com.google.turbine.processing.TurbineTypes;
+import com.google.turbine.tree.Tree.CompUnit;
+import com.google.turbine.type.AnnoInfo;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLClassLoader;
+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;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.Set;
+import java.util.regex.Pattern;
+import javax.annotation.processing.Processor;
+import javax.lang.model.SourceVersion;
+import javax.lang.model.element.TypeElement;
+import javax.tools.Diagnostic;
+import org.checkerframework.checker.nullness.qual.Nullable;
+
+/** Top level annotation processing logic, see also {@link Binder}. */
+public class Processing {
+
+ static BindingResult process(
+ TurbineLog log,
+ final ImmutableList<CompUnit> initialSources,
+ final ClassPath classpath,
+ ProcessorInfo processorInfo,
+ ClassPath bootclasspath,
+ BindingResult result,
+ Optional<String> moduleVersion) {
+
+ Set<String> seen = new HashSet<>();
+ for (CompUnit u : initialSources) {
+ if (u.source() != null) {
+ seen.add(u.source().path());
+ }
+ }
+
+ TurbineFiler filer =
+ new TurbineFiler(
+ seen,
+ new Function<String, Supplier<byte[]>>() {
+ @Nullable
+ @Override
+ public Supplier<byte[]> apply(@Nullable String input) {
+ // TODO(cushon): should annotation processors be allowed to generate code with
+ // dependencies between source and bytecode, or vice versa?
+ // Currently generated classes are not available on the classpath when compiling
+ // the compilation sources (including generated sources).
+ return classpath.resource(input);
+ }
+ },
+ processorInfo.loader());
+
+ Env<ClassSymbol, SourceTypeBoundClass> tenv = new SimpleEnv<>(result.units());
+ CompoundEnv<ClassSymbol, TypeBoundClass> env =
+ CompoundEnv.<ClassSymbol, TypeBoundClass>of(result.classPathEnv()).append(tenv);
+ ModelFactory factory = new ModelFactory(env, processorInfo.loader(), result.tli());
+
+ Map<String, byte[]> statistics = new LinkedHashMap<>();
+
+ TurbineTypes turbineTypes = new TurbineTypes(factory);
+ TurbineProcessingEnvironment processingEnv =
+ new TurbineProcessingEnvironment(
+ filer,
+ turbineTypes,
+ new TurbineElements(factory, turbineTypes),
+ new TurbineMessager(factory, log),
+ processorInfo.options(),
+ processorInfo.sourceVersion(),
+ processorInfo.loader(),
+ statistics);
+ Timers timers = new Timers();
+ for (Processor processor : processorInfo.processors()) {
+ try (Timers.Timer unused = timers.start(processor)) {
+ processor.init(processingEnv);
+ } catch (Throwable t) {
+ reportProcessorCrash(log, processor, t);
+ }
+ }
+
+ 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)));
+ }
+
+ Set<ClassSymbol> allSymbols = new HashSet<>();
+
+ ImmutableList.Builder<CompUnit> units =
+ ImmutableList.<CompUnit>builder().addAll(initialSources);
+
+ Set<Processor> toRun = new LinkedHashSet<>();
+
+ boolean errorRaised = false;
+
+ while (true) {
+ ImmutableSet<ClassSymbol> syms =
+ Sets.difference(result.units().keySet(), allSymbols).immutableCopy();
+ allSymbols.addAll(syms);
+ if (syms.isEmpty()) {
+ break;
+ }
+ ImmutableSetMultimap<ClassSymbol, Symbol> allAnnotations = getAllAnnotations(env, syms);
+ TurbineRoundEnvironment roundEnv = null;
+ for (Processor processor : processorInfo.processors()) {
+ Set<TypeElement> annotations = new HashSet<>();
+ Pattern pattern = wanted.get(processor);
+ boolean run = toRun.contains(processor);
+ for (ClassSymbol a : allAnnotations.keys()) {
+ if (pattern.matcher(a.toString()).matches()) {
+ annotations.add(factory.typeElement(a));
+ run = true;
+ }
+ }
+ if (run) {
+ toRun.add(processor);
+ if (roundEnv == null) {
+ roundEnv =
+ new TurbineRoundEnvironment(factory, syms, false, errorRaised, allAnnotations);
+ }
+ try (Timers.Timer unused = timers.start(processor)) {
+ // discard the result of Processor#process because 'claiming' annotations is a bad idea
+ // TODO(cushon): consider disallowing this, or reporting a diagnostic
+ processor.process(annotations, roundEnv);
+ } catch (Throwable t) {
+ reportProcessorCrash(log, processor, t);
+ }
+ }
+ }
+ Collection<SourceFile> files = filer.finishRound();
+ if (files.isEmpty()) {
+ break;
+ }
+ for (SourceFile file : files) {
+ units.add(Parser.parse(file));
+ }
+ errorRaised = log.errorRaised();
+ if (errorRaised) {
+ log.maybeThrow();
+ }
+ log.clear();
+ result =
+ Binder.bind(
+ log,
+ units.build(),
+ filer.generatedSources(),
+ filer.generatedClasses(),
+ classpath,
+ bootclasspath,
+ moduleVersion);
+ tenv = new SimpleEnv<>(result.units());
+ env = CompoundEnv.<ClassSymbol, TypeBoundClass>of(result.classPathEnv()).append(tenv);
+ factory.round(env, result.tli());
+ }
+
+ TurbineRoundEnvironment roundEnv = null;
+ for (Processor processor : toRun) {
+ if (roundEnv == null) {
+ roundEnv =
+ new TurbineRoundEnvironment(
+ factory,
+ ImmutableSet.of(),
+ /* processingOver= */ true,
+ errorRaised,
+ ImmutableSetMultimap.of());
+ }
+ try (Timers.Timer unused = timers.start(processor)) {
+ processor.process(ImmutableSet.of(), roundEnv);
+ } catch (Throwable t) {
+ reportProcessorCrash(log, processor, t);
+ }
+ }
+
+ Collection<SourceFile> files = filer.finishRound();
+ if (!files.isEmpty()) {
+ // processors aren't supposed to generate sources on the final processing round, but javac
+ // tolerates it anyway
+ // TODO(cushon): consider disallowing this, or reporting a diagnostic
+ for (SourceFile file : files) {
+ units.add(Parser.parse(file));
+ }
+ result =
+ Binder.bind(
+ log,
+ units.build(),
+ filer.generatedSources(),
+ filer.generatedClasses(),
+ classpath,
+ bootclasspath,
+ moduleVersion);
+ log.maybeThrow();
+ }
+
+ if (!filer.generatedClasses().isEmpty()) {
+ // add any generated class files to the output
+ // TODO(cushon): consider handling generated classes after each round
+ result = result.withGeneratedClasses(filer.generatedClasses());
+ }
+ if (!filer.generatedSources().isEmpty()) {
+ result = result.withGeneratedSources(filer.generatedSources());
+ }
+
+ result =
+ result.withStatistics(Statistics.create(timers.build(), ImmutableMap.copyOf(statistics)));
+
+ return result;
+ }
+
+ private static void reportProcessorCrash(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. */
+ private static ImmutableSetMultimap<ClassSymbol, Symbol> getAllAnnotations(
+ Env<ClassSymbol, TypeBoundClass> env, Iterable<ClassSymbol> syms) {
+ ImmutableSetMultimap.Builder<ClassSymbol, Symbol> result = ImmutableSetMultimap.builder();
+ for (ClassSymbol sym : syms) {
+ TypeBoundClass info = env.get(sym);
+ for (AnnoInfo annoInfo : info.annotations()) {
+ if (sym.simpleName().equals("package-info")) {
+ addAnno(result, annoInfo, sym.owner());
+ } else {
+ addAnno(result, annoInfo, sym);
+ }
+ }
+ for (ClassSymbol inheritedAnno :
+ inheritedAnnotations(new HashSet<>(), info.superclass(), env)) {
+ result.put(inheritedAnno, sym);
+ }
+ for (TypeBoundClass.MethodInfo method : info.methods()) {
+ for (AnnoInfo annoInfo : method.annotations()) {
+ addAnno(result, annoInfo, method.sym());
+ }
+ for (TypeBoundClass.ParamInfo param : method.parameters()) {
+ for (AnnoInfo annoInfo : param.annotations()) {
+ addAnno(result, annoInfo, param.sym());
+ }
+ }
+ }
+ for (FieldInfo field : info.fields()) {
+ for (AnnoInfo annoInfo : field.annotations()) {
+ addAnno(result, annoInfo, field.sym());
+ }
+ }
+ }
+ return result.build();
+ }
+
+ // TODO(cushon): consider memoizing this (or isAnnotationInherited) if they show up in profiles
+ private static Set<ClassSymbol> inheritedAnnotations(
+ Set<ClassSymbol> seen, ClassSymbol sym, Env<ClassSymbol, TypeBoundClass> env) {
+ ImmutableSet.Builder<ClassSymbol> result = ImmutableSet.builder();
+ ClassSymbol curr = sym;
+ while (curr != null && seen.add(curr)) {
+ TypeBoundClass info = env.get(curr);
+ if (info == null) {
+ break;
+ }
+ for (AnnoInfo anno : info.annotations()) {
+ ClassSymbol annoSym = anno.sym();
+ if (annoSym == null) {
+ continue;
+ }
+ if (isAnnotationInherited(env, annoSym)) {
+ result.add(annoSym);
+ }
+ }
+ curr = info.superclass();
+ }
+ return result.build();
+ }
+
+ private static boolean isAnnotationInherited(
+ Env<ClassSymbol, TypeBoundClass> env, ClassSymbol sym) {
+ TypeBoundClass annoInfo = env.get(sym);
+ if (annoInfo == null) {
+ return false;
+ }
+ for (AnnoInfo anno : annoInfo.annotations()) {
+ if (Objects.equals(anno.sym(), ClassSymbol.INHERITED)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private static void addAnno(
+ ImmutableSetMultimap.Builder<ClassSymbol, Symbol> result, AnnoInfo annoInfo, Symbol owner) {
+ ClassSymbol sym = annoInfo.sym();
+ if (sym != null) {
+ result.put(sym, owner);
+ }
+ }
+
+ public static ProcessorInfo initializeProcessors(
+ ImmutableList<String> javacopts,
+ ImmutableList<String> processorPath,
+ ImmutableSet<String> processorNames,
+ ImmutableSet<String> builtinProcessors)
+ throws MalformedURLException {
+ ClassLoader processorLoader = null;
+ 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);
+ }
+ }
+ processorOptions = processorOptions(javacopts);
+ } else {
+ processorOptions = ImmutableMap.of();
+ }
+ 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;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ return ProcessorInfo.create(
+ processors.build(), processorLoader, processorOptions, sourceVersion);
+ }
+
+ private static URL[] toUrls(ImmutableList<String> processorPath) throws MalformedURLException {
+ URL[] urls = new URL[processorPath.size()];
+ int i = 0;
+ for (String path : processorPath) {
+ urls[i++] = Paths.get(path).toUri().toURL();
+ }
+ return urls;
+ }
+
+ public static ClassLoader getPlatformClassLoader() {
+ try {
+ return (ClassLoader) ClassLoader.class.getMethod("getPlatformClassLoader").invoke(null);
+ } catch (ReflectiveOperationException e) {
+ // In earlier releases, set 'null' as the parent to delegate to the boot class loader.
+ return null;
+ }
+ }
+
+ private static ImmutableMap<String, String> processorOptions(ImmutableList<String> javacopts) {
+ Map<String, String> result = new LinkedHashMap<>(); // ImmutableMap.Builder rejects duplicates
+ for (String javacopt : javacopts) {
+ if (javacopt.startsWith("-A")) {
+ javacopt = javacopt.substring("-A".length());
+ int idx = javacopt.indexOf('=');
+ String key;
+ String value;
+ if (idx != -1) {
+ key = javacopt.substring(0, idx);
+ value = javacopt.substring(idx + 1);
+ } else {
+ key = javacopt;
+ value = javacopt;
+ }
+ result.put(key, value);
+ }
+ }
+ return ImmutableMap.copyOf(result);
+ }
+
+ /** Information about any annotation processing performed by this compilation. */
+ @AutoValue
+ public abstract static class ProcessorInfo {
+
+ abstract ImmutableList<Processor> processors();
+
+ /**
+ * The classloader to use for annotation processor implementations, and any annotations they
+ * access reflectively.
+ */
+ @Nullable
+ abstract ClassLoader loader();
+
+ /** Command line annotation processing options, passed to javac with {@code -Akey=value}. */
+ abstract ImmutableMap<String, String> options();
+
+ public abstract SourceVersion sourceVersion();
+
+ public static ProcessorInfo create(
+ ImmutableList<Processor> processors,
+ @Nullable ClassLoader loader,
+ ImmutableMap<String, String> options,
+ SourceVersion sourceVersion) {
+ return new AutoValue_Processing_ProcessorInfo(processors, loader, options, sourceVersion);
+ }
+
+ static ProcessorInfo empty() {
+ return create(
+ /* processors= */ ImmutableList.of(),
+ /* loader= */ null,
+ /* options= */ ImmutableMap.of(),
+ /* sourceVersion= */ SourceVersion.latest());
+ }
+ }
+
+ private static class Timers {
+ private final Map<Class<?>, Stopwatch> processorTimers = new LinkedHashMap<>();
+
+ Timer start(Processor processor) {
+ Class<? extends Processor> clazz = processor.getClass();
+ Stopwatch sw = processorTimers.get(clazz);
+ if (sw == null) {
+ sw = Stopwatch.createUnstarted();
+ processorTimers.put(clazz, sw);
+ }
+ sw.start();
+ return new Timer(sw);
+ }
+
+ private static class Timer implements AutoCloseable {
+
+ private final Stopwatch sw;
+
+ public Timer(Stopwatch sw) {
+ this.sw = sw;
+ }
+
+ @Override
+ public void close() {
+ sw.stop();
+ }
+ }
+
+ 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());
+ }
+ return result.build();
+ }
+ }
+}
diff --git a/java/com/google/turbine/binder/Resolve.java b/java/com/google/turbine/binder/Resolve.java
index 0a61844..28a8be3 100644
--- a/java/com/google/turbine/binder/Resolve.java
+++ b/java/com/google/turbine/binder/Resolve.java
@@ -136,12 +136,11 @@ public class Resolve {
return true;
case PROTECTED:
case PACKAGE:
- return Objects.equals(packageName(sym), packagename);
+ return Objects.equals(sym.packageName(), packagename);
case PRIVATE:
return false;
- default:
- throw new AssertionError(visibility);
}
+ throw new AssertionError(visibility);
}
}
@@ -206,24 +205,12 @@ public class Resolve {
case PROTECTED:
return true;
case PACKAGE:
- return Objects.equals(packageName(owner), packageName(origin));
+ return Objects.equals(owner.packageName(), origin.packageName());
case PRIVATE:
// Private members of lexically enclosing declarations are not handled,
// since this visibility check is only used for inherited members.
return owner.equals(origin);
- default:
- throw new AssertionError(visibility);
}
- }
-
- private static String packageName(ClassSymbol sym) {
- if (sym == null) {
- return null;
- }
- int idx = sym.binaryName().lastIndexOf('/');
- if (idx == -1) {
- return null;
- }
- return sym.binaryName().substring(0, idx);
+ throw new AssertionError(visibility);
}
}
diff --git a/java/com/google/turbine/binder/TypeBinder.java b/java/com/google/turbine/binder/TypeBinder.java
index 8cf71e1..7b01856 100644
--- a/java/com/google/turbine/binder/TypeBinder.java
+++ b/java/com/google/turbine/binder/TypeBinder.java
@@ -34,6 +34,7 @@ import com.google.turbine.binder.lookup.Scope;
import com.google.turbine.binder.sym.ClassSymbol;
import com.google.turbine.binder.sym.FieldSymbol;
import com.google.turbine.binder.sym.MethodSymbol;
+import com.google.turbine.binder.sym.ParamSymbol;
import com.google.turbine.binder.sym.Symbol;
import com.google.turbine.binder.sym.TyVarSymbol;
import com.google.turbine.diag.TurbineError.ErrorKind;
@@ -266,22 +267,23 @@ public class TypeBinder {
if (hasConstructor()) {
return ImmutableList.of();
}
+ MethodSymbol symbol = new MethodSymbol(-1, owner, "<init>");
ImmutableList<ParamInfo> formals;
if (hasEnclosingInstance(base)) {
- formals = ImmutableList.of(enclosingInstanceParameter());
+ formals = ImmutableList.of(enclosingInstanceParameter(symbol));
} else {
formals = ImmutableList.of();
}
return ImmutableList.of(
- syntheticConstructor(formals, TurbineVisibility.fromAccess(base.access())));
+ syntheticConstructor(symbol, formals, TurbineVisibility.fromAccess(base.access())));
}
private MethodInfo syntheticConstructor(
- ImmutableList<ParamInfo> formals, TurbineVisibility visibility) {
+ MethodSymbol symbol, ImmutableList<ParamInfo> formals, TurbineVisibility visibility) {
int access = visibility.flag();
access |= (base.access() & TurbineFlag.ACC_STRICT);
return new MethodInfo(
- new MethodSymbol(owner, "<init>"),
+ symbol,
ImmutableMap.of(),
Type.VOID,
formals,
@@ -293,7 +295,7 @@ public class TypeBinder {
null);
}
- private ParamInfo enclosingInstanceParameter() {
+ private ParamInfo enclosingInstanceParameter(MethodSymbol owner) {
int access = TurbineFlag.ACC_FINAL;
if ((base.access() & TurbineFlag.ACC_PRIVATE) == TurbineFlag.ACC_PRIVATE) {
access |= TurbineFlag.ACC_SYNTHETIC;
@@ -311,37 +313,40 @@ public class TypeBinder {
sym = info.owner();
}
return new ParamInfo(
+ new ParamSymbol(owner, "this$" + enclosingInstances),
Type.ClassTy.asNonParametricClassTy(base.owner()),
- "this$" + enclosingInstances,
ImmutableList.of(),
access);
}
- private static final ImmutableList<ParamInfo> ENUM_CTOR_PARAMS =
- ImmutableList.of(
- new ParamInfo(
- Type.ClassTy.STRING,
- "$enum$name",
- ImmutableList.of(),
- /*synthetic*/
- TurbineFlag.ACC_SYNTHETIC),
- new ParamInfo(
- Type.PrimTy.create(TurbineConstantTypeKind.INT, ImmutableList.of()),
- "$enum$ordinal",
- ImmutableList.of(),
- /*synthetic*/
- TurbineFlag.ACC_SYNTHETIC));
+ private static ImmutableList<ParamInfo> enumCtorParams(MethodSymbol owner) {
+ return ImmutableList.of(
+ new ParamInfo(
+ new ParamSymbol(owner, "$enum$name"),
+ Type.ClassTy.STRING,
+ ImmutableList.of(),
+ /*synthetic*/
+ TurbineFlag.ACC_SYNTHETIC),
+ new ParamInfo(
+ new ParamSymbol(owner, "$enum$ordinal"),
+ Type.PrimTy.create(TurbineConstantTypeKind.INT, ImmutableList.of()),
+ ImmutableList.of(),
+ /*synthetic*/
+ TurbineFlag.ACC_SYNTHETIC));
+ }
private ImmutableList<MethodInfo> syntheticEnumMethods() {
ImmutableList.Builder<MethodInfo> methods = ImmutableList.builder();
int access = 0;
access |= (base.access() & TurbineFlag.ACC_STRICT);
if (!hasConstructor()) {
- methods.add(syntheticConstructor(ENUM_CTOR_PARAMS, TurbineVisibility.PRIVATE));
+ MethodSymbol symbol = new MethodSymbol(-1, owner, "<init>");
+ methods.add(syntheticConstructor(symbol, enumCtorParams(symbol), TurbineVisibility.PRIVATE));
}
+ MethodSymbol valuesMethod = new MethodSymbol(-2, owner, "values");
methods.add(
new MethodInfo(
- new MethodSymbol(owner, "values"),
+ valuesMethod,
ImmutableMap.of(),
Type.ArrayTy.create(Type.ClassTy.asNonParametricClassTy(owner), ImmutableList.of()),
ImmutableList.of(),
@@ -351,14 +356,18 @@ public class TypeBinder {
null,
ImmutableList.of(),
null));
+ MethodSymbol valueOfMethod = new MethodSymbol(-3, owner, "valueOf");
methods.add(
new MethodInfo(
- new MethodSymbol(owner, "valueOf"),
+ valueOfMethod,
ImmutableMap.of(),
Type.ClassTy.asNonParametricClassTy(owner),
ImmutableList.of(
new ParamInfo(
- Type.ClassTy.STRING, "name", ImmutableList.of(), TurbineFlag.ACC_MANDATED)),
+ new ParamSymbol(valueOfMethod, "name"),
+ Type.ClassTy.STRING,
+ ImmutableList.of(),
+ TurbineFlag.ACC_MANDATED)),
ImmutableList.of(),
access | TurbineFlag.ACC_PUBLIC | TurbineFlag.ACC_STATIC,
null,
@@ -387,38 +396,38 @@ public class TypeBinder {
for (Tree.TyParam tree : trees) {
TyVarSymbol sym = symbols.get(tree.name().value());
ImmutableList.Builder<Type> bounds = ImmutableList.builder();
- if (tree.bounds().isEmpty()) {
- bounds.add(Type.ClassTy.OBJECT);
- } else {
- for (Tree bound : tree.bounds()) {
- bounds.add(bindTy(scope, bound));
- }
+ for (Tree bound : tree.bounds()) {
+ bounds.add(bindTy(scope, bound));
}
ImmutableList<AnnoInfo> annotations = bindAnnotations(scope, tree.annos());
- result.put(sym, new TyVarInfo(IntersectionTy.create(bounds.build()), annotations));
+ result.put(
+ sym,
+ new TyVarInfo(
+ IntersectionTy.create(bounds.build()), /* lowerBound= */ null, annotations));
}
return result.build();
}
private List<MethodInfo> bindMethods(CompoundScope scope, ImmutableList<Tree> members) {
List<MethodInfo> methods = new ArrayList<>();
+ int idx = 0;
for (Tree member : members) {
if (member.kind() == Tree.Kind.METH_DECL) {
- methods.add(bindMethod(scope, (Tree.MethDecl) member));
+ methods.add(bindMethod(idx++, scope, (Tree.MethDecl) member));
}
}
return methods;
}
- private MethodInfo bindMethod(CompoundScope scope, Tree.MethDecl t) {
+ private MethodInfo bindMethod(int idx, CompoundScope scope, Tree.MethDecl t) {
- MethodSymbol sym = new MethodSymbol(owner, t.name().value());
+ MethodSymbol sym = new MethodSymbol(idx, owner, t.name().value());
ImmutableMap<String, TyVarSymbol> typeParameters;
{
ImmutableMap.Builder<String, TyVarSymbol> builder = ImmutableMap.builder();
for (Tree.TyParam pt : t.typarams()) {
- builder.put(pt.name().value(), new TyVarSymbol(owner, pt.name().value()));
+ builder.put(pt.name().value(), new TyVarSymbol(sym, pt.name().value()));
}
typeParameters = builder.build();
}
@@ -439,9 +448,9 @@ public class TypeBinder {
String name = t.name().value();
if (name.equals("<init>")) {
if (hasEnclosingInstance(base)) {
- parameters.add(enclosingInstanceParameter());
+ parameters.add(enclosingInstanceParameter(sym));
} else if (base.kind() == TurbineTyKind.ENUM && name.equals("<init>")) {
- parameters.addAll(ENUM_CTOR_PARAMS);
+ parameters.addAll(enumCtorParams(sym));
}
}
ParamInfo receiver = null;
@@ -452,8 +461,8 @@ public class TypeBinder {
}
ParamInfo param =
new ParamInfo(
+ new ParamSymbol(sym, p.name().value()),
bindTy(scope, p.ty()),
- p.name().value(),
bindAnnotations(scope, p.annos()), /*synthetic*/
access);
if (p.name().value().equals("this")) {
@@ -561,7 +570,7 @@ public class TypeBinder {
ImmutableList<Ident> name = tree.name();
LookupResult lookupResult = scope.lookup(new LookupKey(name));
ClassSymbol sym = resolveAnnoSymbol(tree, name, lookupResult);
- result.add(new AnnoInfo(base.source(), sym, tree, null));
+ result.add(new AnnoInfo(base.source(), sym, tree, ImmutableMap.of()));
}
return result.build();
}
@@ -648,7 +657,7 @@ public class TypeBinder {
LookupResult result = scope.lookup(new LookupKey(names));
if (result == null || result.sym() == null) {
log.error(names.get(0).position(), ErrorKind.CANNOT_RESOLVE, Joiner.on('.').join(names));
- return Type.ErrorTy.create();
+ return Type.ErrorTy.create(names);
}
Symbol sym = result.sym();
int annoIdx = flat.size() - result.remaining().size() - 1;
@@ -660,7 +669,7 @@ public class TypeBinder {
case TY_PARAM:
if (!result.remaining().isEmpty()) {
log.error(t.position(), ErrorKind.TYPE_PARAMETER_QUALIFIER);
- return Type.ErrorTy.create();
+ return Type.ErrorTy.create(names);
}
return Type.TyVar.create((TyVarSymbol) sym, annos);
default:
@@ -684,7 +693,7 @@ public class TypeBinder {
Tree.ClassTy curr = flat.get(idx);
sym = resolveNext(sym, curr.name());
if (sym == null) {
- return Type.ErrorTy.create();
+ return Type.ErrorTy.create(bits);
}
annotations = bindAnnotations(scope, curr.annos());
diff --git a/java/com/google/turbine/binder/bound/AnnotationMetadata.java b/java/com/google/turbine/binder/bound/AnnotationMetadata.java
index 9c81a5f..31860b6 100644
--- a/java/com/google/turbine/binder/bound/AnnotationMetadata.java
+++ b/java/com/google/turbine/binder/bound/AnnotationMetadata.java
@@ -30,7 +30,7 @@ import java.util.EnumSet;
*/
public class AnnotationMetadata {
- private static final ImmutableSet<TurbineElementType> DEFAULT_TARGETS = getDefaultElements();
+ public static final ImmutableSet<TurbineElementType> DEFAULT_TARGETS = getDefaultElements();
private static ImmutableSet<TurbineElementType> getDefaultElements() {
EnumSet<TurbineElementType> values = EnumSet.allOf(TurbineElementType.class);
diff --git a/java/com/google/turbine/binder/bound/EnumConstantValue.java b/java/com/google/turbine/binder/bound/EnumConstantValue.java
index 4083ae3..e99c6ed 100644
--- a/java/com/google/turbine/binder/bound/EnumConstantValue.java
+++ b/java/com/google/turbine/binder/bound/EnumConstantValue.java
@@ -46,4 +46,9 @@ public class EnumConstantValue extends Const {
public boolean equals(Object obj) {
return obj instanceof EnumConstantValue && sym().equals(((EnumConstantValue) obj).sym());
}
+
+ @Override
+ public String toString() {
+ return sym.toString();
+ }
}
diff --git a/java/com/google/turbine/binder/bound/AnnotationValue.java b/java/com/google/turbine/binder/bound/TurbineAnnotationValue.java
index fd4ffab..808d603 100644
--- a/java/com/google/turbine/binder/bound/AnnotationValue.java
+++ b/java/com/google/turbine/binder/bound/TurbineAnnotationValue.java
@@ -19,22 +19,20 @@ package com.google.turbine.binder.bound;
import com.google.common.collect.ImmutableMap;
import com.google.turbine.binder.sym.ClassSymbol;
import com.google.turbine.model.Const;
-import java.util.Objects;
+import com.google.turbine.type.AnnoInfo;
/** An annotation literal constant. */
-public class AnnotationValue extends Const {
+public class TurbineAnnotationValue extends Const {
- private final ClassSymbol sym;
- private final ImmutableMap<String, Const> values;
+ private final AnnoInfo info;
- public AnnotationValue(ClassSymbol sym, ImmutableMap<String, Const> values) {
- this.sym = sym;
- this.values = values;
+ public TurbineAnnotationValue(AnnoInfo info) {
+ this.info = info;
}
@Override
public String toString() {
- return String.format("@%s", sym);
+ return info.toString();
}
@Override
@@ -44,25 +42,26 @@ public class AnnotationValue extends Const {
/** The annotation declaration. */
public ClassSymbol sym() {
- return sym;
+ return info.sym();
}
/** The annotation literal's element-value pairs. */
public ImmutableMap<String, Const> values() {
- return values;
+ return info.values();
}
@Override
public int hashCode() {
- return Objects.hash(sym, values);
+ return info.hashCode();
}
@Override
public boolean equals(Object obj) {
- if (!(obj instanceof AnnotationValue)) {
- return false;
- }
- AnnotationValue that = (AnnotationValue) obj;
- return sym().equals(that.sym()) && values().equals(that.values());
+ return obj instanceof TurbineAnnotationValue
+ && info().equals(((TurbineAnnotationValue) obj).info());
+ }
+
+ public AnnoInfo info() {
+ return info;
}
}
diff --git a/java/com/google/turbine/binder/bound/TypeBoundClass.java b/java/com/google/turbine/binder/bound/TypeBoundClass.java
index 350d311..e8933ac 100644
--- a/java/com/google/turbine/binder/bound/TypeBoundClass.java
+++ b/java/com/google/turbine/binder/bound/TypeBoundClass.java
@@ -16,10 +16,12 @@
package com.google.turbine.binder.bound;
+
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.turbine.binder.sym.FieldSymbol;
import com.google.turbine.binder.sym.MethodSymbol;
+import com.google.turbine.binder.sym.ParamSymbol;
import com.google.turbine.binder.sym.TyVarSymbol;
import com.google.turbine.model.Const;
import com.google.turbine.model.TurbineFlag;
@@ -28,6 +30,8 @@ import com.google.turbine.tree.Tree.MethDecl;
import com.google.turbine.type.AnnoInfo;
import com.google.turbine.type.Type;
import com.google.turbine.type.Type.IntersectionTy;
+import com.google.turbine.type.Type.MethodTy;
+import org.checkerframework.checker.nullness.qual.Nullable;
/** A bound node that augments {@link HeaderBoundClass} with type information. */
public interface TypeBoundClass extends HeaderBoundClass {
@@ -57,17 +61,29 @@ public interface TypeBoundClass extends HeaderBoundClass {
/** A type parameter declaration. */
class TyVarInfo {
- private final IntersectionTy bound;
+ private final IntersectionTy upperBound;
+ @Nullable private final Type lowerBound;
private final ImmutableList<AnnoInfo> annotations;
- public TyVarInfo(IntersectionTy bound, ImmutableList<AnnoInfo> annotations) {
- this.bound = bound;
+ public TyVarInfo(
+ IntersectionTy upperBound, @Nullable Type lowerBound, ImmutableList<AnnoInfo> annotations) {
+ this.upperBound = upperBound;
+ if (lowerBound != null) {
+ throw new IllegalArgumentException("TODO(cushon): support lower bounds");
+ }
+ this.lowerBound = lowerBound;
this.annotations = annotations;
}
- /** The bound. */
- public IntersectionTy bound() {
- return bound;
+ /** The upper bound. */
+ public IntersectionTy upperBound() {
+ return upperBound;
+ }
+
+ /** The lower bound. */
+ @Nullable
+ public Type lowerBound() {
+ return lowerBound;
}
/** Type parameter declaration annotations. */
@@ -148,7 +164,7 @@ public interface TypeBoundClass extends HeaderBoundClass {
private final Const defaultValue;
private final MethDecl decl;
private final ImmutableList<AnnoInfo> annotations;
- private final ParamInfo receiver;
+ private final @Nullable ParamInfo receiver;
public MethodInfo(
MethodSymbol sym,
@@ -160,7 +176,7 @@ public interface TypeBoundClass extends HeaderBoundClass {
Const defaultValue,
MethDecl decl,
ImmutableList<AnnoInfo> annotations,
- ParamInfo receiver) {
+ @Nullable ParamInfo receiver) {
this.sym = sym;
this.tyParams = tyParams;
this.returnType = returnType;
@@ -223,26 +239,50 @@ public interface TypeBoundClass extends HeaderBoundClass {
return annotations;
}
- /** Receiver parameter. */
- public ParamInfo receiver() {
+ /** Receiver parameter (see JLS 8.4.1), or {@code null}. */
+ public @Nullable ParamInfo receiver() {
return receiver;
}
+
+ public MethodTy asType() {
+ return MethodTy.create(
+ tyParams.keySet(),
+ returnType,
+ receiver != null ? receiver.type() : null,
+ asTypes(parameters),
+ exceptions);
+ }
+
+ private static ImmutableList<Type> asTypes(ImmutableList<ParamInfo> parameters) {
+ ImmutableList.Builder<Type> result = ImmutableList.builder();
+ for (ParamInfo param : parameters) {
+ if (!param.synthetic()) {
+ result.add(param.type());
+ }
+ }
+ return result.build();
+ }
}
/** A formal parameter declaration. */
class ParamInfo {
+ private final ParamSymbol sym;
private final Type type;
- private final String name;
private final int access;
private final ImmutableList<AnnoInfo> annotations;
- public ParamInfo(Type type, String name, ImmutableList<AnnoInfo> annotations, int access) {
+ public ParamInfo(ParamSymbol sym, Type type, ImmutableList<AnnoInfo> annotations, int access) {
+ this.sym = sym;
this.type = type;
- this.name = name;
this.access = access;
this.annotations = annotations;
}
+ /** The parameter's symbol. */
+ public ParamSymbol sym() {
+ return sym;
+ }
+
/** The parameter type. */
public Type type() {
return type;
@@ -263,7 +303,7 @@ public interface TypeBoundClass extends HeaderBoundClass {
/** The parameter's name. */
public String name() {
- return name;
+ return sym.name();
}
/** The parameter's modifiers. */
diff --git a/java/com/google/turbine/binder/bytecode/BytecodeBinder.java b/java/com/google/turbine/binder/bytecode/BytecodeBinder.java
index 1d2eecb..66d4cf0 100644
--- a/java/com/google/turbine/binder/bytecode/BytecodeBinder.java
+++ b/java/com/google/turbine/binder/bytecode/BytecodeBinder.java
@@ -18,9 +18,9 @@ package com.google.turbine.binder.bytecode;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
-import com.google.turbine.binder.bound.AnnotationValue;
import com.google.turbine.binder.bound.EnumConstantValue;
import com.google.turbine.binder.bound.ModuleInfo;
+import com.google.turbine.binder.bound.TurbineAnnotationValue;
import com.google.turbine.binder.bound.TurbineClassValue;
import com.google.turbine.binder.sym.ClassSymbol;
import com.google.turbine.binder.sym.FieldSymbol;
@@ -81,9 +81,8 @@ public class BytecodeBinder {
case UPPER:
return Type.WildUpperBoundedTy.create(
bindTy(((UpperBoundTySig) sig).bound(), scope), ImmutableList.of());
- default:
- throw new AssertionError(sig.boundKind());
}
+ throw new AssertionError(sig.boundKind());
}
static Type bindTy(Sig.TySig sig, Function<String, TyVarSymbol> scope) {
@@ -100,23 +99,22 @@ public class BytecodeBinder {
return wildTy((WildTySig) sig, scope);
case VOID_TY_SIG:
return Type.VOID;
- default:
- throw new AssertionError(sig.kind());
}
+ throw new AssertionError(sig.kind());
}
private static Type bindArrayTy(Sig.ArrayTySig arrayTySig, Function<String, TyVarSymbol> scope) {
return Type.ArrayTy.create(bindTy(arrayTySig.elementType(), scope), ImmutableList.of());
}
- public static Const bindValue(Type type, ElementValue value) {
+ public static Const bindValue(ElementValue value) {
switch (value.kind()) {
case ENUM:
return bindEnumValue((EnumConstValue) value);
case CONST:
- return bindConstValue(type, ((ConstValue) value).value());
+ return ((ConstValue) value).value();
case ARRAY:
- return bindArrayValue(type, (ArrayValue) value);
+ return bindArrayValue((ArrayValue) value);
case CLASS:
return new TurbineClassValue(
bindTy(
@@ -125,37 +123,45 @@ public class BytecodeBinder {
throw new IllegalStateException(x);
}));
case ANNOTATION:
- return bindAnnotationValue(type, ((ElementValue.AnnotationValue) value).annotation());
+ return bindAnnotationValue(((ElementValue.ConstTurbineAnnotationValue) value).annotation());
}
throw new AssertionError(value.kind());
}
- static AnnotationValue bindAnnotationValue(Type type, AnnotationInfo value) {
+ static TurbineAnnotationValue bindAnnotationValue(AnnotationInfo value) {
ClassSymbol sym = asClassSymbol(value.typeName());
ImmutableMap.Builder<String, Const> values = ImmutableMap.builder();
for (Map.Entry<String, ElementValue> e : value.elementValuePairs().entrySet()) {
- values.put(e.getKey(), bindValue(type, e.getValue()));
+ values.put(e.getKey(), bindValue(e.getValue()));
}
- return new AnnotationValue(sym, values.build());
+ return new TurbineAnnotationValue(new AnnoInfo(null, sym, null, values.build()));
}
static ImmutableList<AnnoInfo> bindAnnotations(List<AnnotationInfo> input) {
ImmutableList.Builder<AnnoInfo> result = ImmutableList.builder();
for (AnnotationInfo annotation : input) {
- AnnotationValue anno = bindAnnotationValue(Type.VOID, annotation);
- result.add(new AnnoInfo(null, anno.sym(), null, anno.values()));
+ TurbineAnnotationValue anno = bindAnnotationValue(annotation);
+ if (!shouldSkip(anno)) {
+ result.add(anno.info());
+ }
}
return result.build();
}
+ private static boolean shouldSkip(TurbineAnnotationValue anno) {
+ // ct.sym contains fake annotations without corresponding class files.
+ return anno.sym().equals(ClassSymbol.PROFILE_ANNOTATION)
+ || anno.sym().equals(ClassSymbol.PROPRIETARY_ANNOTATION);
+ }
+
private static ClassSymbol asClassSymbol(String s) {
return new ClassSymbol(s.substring(1, s.length() - 1));
}
- private static Const bindArrayValue(Type type, ArrayValue value) {
+ private static Const bindArrayValue(ArrayValue value) {
ImmutableList.Builder<Const> elements = ImmutableList.builder();
for (ElementValue element : value.elements()) {
- elements.add(bindValue(type, element));
+ elements.add(bindValue(element));
}
return new ArrayInitValue(elements.build());
}
@@ -164,30 +170,22 @@ public class BytecodeBinder {
if (type.tyKind() != Type.TyKind.PRIM_TY) {
return value;
}
+ // Deficient numberic types and booleans are all stored as ints in the class file,
+ // coerce them to the target type.
// TODO(b/32626659): this is not bug-compatible with javac
switch (((Type.PrimTy) type).primkind()) {
case CHAR:
return new Const.CharValue(value.asChar().value());
case SHORT:
return new Const.ShortValue(value.asShort().value());
- case INT:
- return new Const.IntValue(value.asInteger().value());
- case LONG:
- return new Const.LongValue(value.asLong().value());
- case FLOAT:
- return new Const.FloatValue(value.asFloat().value());
- case DOUBLE:
- return new Const.DoubleValue(value.asDouble().value());
case BOOLEAN:
// boolean constants are encoded as integers
return new Const.BooleanValue(value.asInteger().value() != 0);
case BYTE:
return new Const.ByteValue(value.asByte().value());
- case STRING:
- case NULL:
+ default:
return value;
}
- throw new AssertionError(type);
}
private static Const bindEnumValue(EnumConstValue value) {
diff --git a/java/com/google/turbine/binder/bytecode/BytecodeBoundClass.java b/java/com/google/turbine/binder/bytecode/BytecodeBoundClass.java
index 6ff8cb4..b992643 100644
--- a/java/com/google/turbine/binder/bytecode/BytecodeBoundClass.java
+++ b/java/com/google/turbine/binder/bytecode/BytecodeBoundClass.java
@@ -32,6 +32,7 @@ import com.google.turbine.binder.env.Env;
import com.google.turbine.binder.sym.ClassSymbol;
import com.google.turbine.binder.sym.FieldSymbol;
import com.google.turbine.binder.sym.MethodSymbol;
+import com.google.turbine.binder.sym.ParamSymbol;
import com.google.turbine.binder.sym.TyVarSymbol;
import com.google.turbine.bytecode.ClassFile;
import com.google.turbine.bytecode.ClassFile.AnnotationInfo;
@@ -332,7 +333,8 @@ public class BytecodeBoundClass implements BoundClass, HeaderBoundClass, TypeBou
for (Sig.TySig t : sig.interfaceBounds()) {
bounds.add(BytecodeBinder.bindTy(t, scope));
}
- return new TyVarInfo(IntersectionTy.create(bounds.build()), ImmutableList.of());
+ return new TyVarInfo(
+ IntersectionTy.create(bounds.build()), /* lowerBound= */ null, ImmutableList.of());
}
@Override
@@ -350,14 +352,17 @@ public class BytecodeBoundClass implements BoundClass, HeaderBoundClass, TypeBou
FieldSymbol fieldSym = new FieldSymbol(sym, cfi.name());
Type type =
BytecodeBinder.bindTy(
- new SigParser(cfi.descriptor()).parseType(),
+ new SigParser(firstNonNull(cfi.signature(), cfi.descriptor())).parseType(),
makeScope(env, sym, ImmutableMap.of()));
int access = cfi.access();
Const.Value value = cfi.value();
if (value != null) {
value = BytecodeBinder.bindConstValue(type, value);
}
- fields.add(new FieldInfo(fieldSym, type, access, ImmutableList.of(), null, value));
+ ImmutableList<AnnoInfo> annotations =
+ BytecodeBinder.bindAnnotations(cfi.annotations());
+ fields.add(
+ new FieldInfo(fieldSym, type, access, annotations, /* decl= */ null, value));
}
return fields.build();
}
@@ -374,15 +379,16 @@ public class BytecodeBoundClass implements BoundClass, HeaderBoundClass, TypeBou
@Override
public ImmutableList<MethodInfo> get() {
ImmutableList.Builder<MethodInfo> methods = ImmutableList.builder();
+ int idx = 0;
for (ClassFile.MethodInfo m : classFile.get().methods()) {
- methods.add(bindMethod(m));
+ methods.add(bindMethod(idx++, m));
}
return methods.build();
}
});
- private MethodInfo bindMethod(ClassFile.MethodInfo m) {
- MethodSymbol methodSymbol = new MethodSymbol(sym, m.name());
+ private MethodInfo bindMethod(int methodIdx, ClassFile.MethodInfo m) {
+ MethodSymbol methodSymbol = new MethodSymbol(methodIdx, sym, m.name());
Sig.MethodSig sig = new SigParser(firstNonNull(m.signature(), m.descriptor())).parseMethodSig();
ImmutableMap<String, TyVarSymbol> tyParams;
@@ -414,28 +420,43 @@ public class BytecodeBoundClass implements BoundClass, HeaderBoundClass, TypeBou
ImmutableList.Builder<ParamInfo> formals = ImmutableList.builder();
int idx = 0;
for (Sig.TySig tySig : sig.params()) {
- String name = null;
+ String name;
int access = 0;
if (idx < m.parameters().size()) {
ParameterInfo paramInfo = m.parameters().get(idx);
name = paramInfo.name();
- access = paramInfo.access();
+ // ignore parameter modifiers for bug-parity with javac:
+ // https://bugs.openjdk.java.net/browse/JDK-8226216
+ // access = paramInfo.access();
+ } else {
+ name = "arg" + idx;
}
ImmutableList<AnnoInfo> annotations =
(idx < m.parameterAnnotations().size())
? BytecodeBinder.bindAnnotations(m.parameterAnnotations().get(idx))
: ImmutableList.of();
- formals.add(new ParamInfo(BytecodeBinder.bindTy(tySig, scope), name, annotations, access));
+ formals.add(
+ new ParamInfo(
+ new ParamSymbol(methodSymbol, name),
+ BytecodeBinder.bindTy(tySig, scope),
+ annotations,
+ access));
idx++;
}
ImmutableList.Builder<Type> exceptions = ImmutableList.builder();
- for (TySig e : sig.exceptions()) {
- exceptions.add(BytecodeBinder.bindTy(e, scope));
+ if (!sig.exceptions().isEmpty()) {
+ for (TySig e : sig.exceptions()) {
+ exceptions.add(BytecodeBinder.bindTy(e, scope));
+ }
+ } else {
+ for (String e : m.exceptions()) {
+ exceptions.add(ClassTy.asNonParametricClassTy(new ClassSymbol(e)));
+ }
}
Const defaultValue =
- m.defaultValue() != null ? BytecodeBinder.bindValue(ret, m.defaultValue()) : null;
+ m.defaultValue() != null ? BytecodeBinder.bindValue(m.defaultValue()) : null;
ImmutableList<AnnoInfo> annotations = BytecodeBinder.bindAnnotations(m.annotations());
@@ -487,7 +508,7 @@ public class BytecodeBoundClass implements BoundClass, HeaderBoundClass, TypeBou
}
});
- private RetentionPolicy bindRetention(AnnotationInfo annotation) {
+ private static RetentionPolicy bindRetention(AnnotationInfo annotation) {
ElementValue val = annotation.elementValuePairs().get("value");
if (val.kind() != Kind.ENUM) {
return null;
diff --git a/java/com/google/turbine/binder/lookup/CompoundTopLevelIndex.java b/java/com/google/turbine/binder/lookup/CompoundTopLevelIndex.java
index de50a2e..b41edb0 100644
--- a/java/com/google/turbine/binder/lookup/CompoundTopLevelIndex.java
+++ b/java/com/google/turbine/binder/lookup/CompoundTopLevelIndex.java
@@ -62,14 +62,14 @@ public class CompoundTopLevelIndex implements TopLevelIndex {
}
@Override
- public Scope lookupPackage(ImmutableList<String> packagename) {
+ public PackageScope lookupPackage(Iterable<String> packagename) {
// When returning package scopes, build up a compound scope containing entries from all
// indices with matching packages.
- CompoundScope result = null;
+ PackageScope result = null;
for (TopLevelIndex index : indexes) {
- Scope packageScope = index.lookupPackage(packagename);
+ PackageScope packageScope = index.lookupPackage(packagename);
if (packageScope != null) {
- result = result == null ? CompoundScope.base(packageScope) : result.append(packageScope);
+ result = result == null ? packageScope : result.append(packageScope);
}
}
return result;
diff --git a/java/com/google/turbine/binder/lookup/ImportIndex.java b/java/com/google/turbine/binder/lookup/ImportIndex.java
index afa985a..fd57223 100644
--- a/java/com/google/turbine/binder/lookup/ImportIndex.java
+++ b/java/com/google/turbine/binder/lookup/ImportIndex.java
@@ -142,7 +142,8 @@ public class ImportIndex implements ImportScope {
TurbineLogWithSource log, TopLevelIndex cpi, ImportDecl i) {
LookupResult base = cpi.scope().lookup(new LookupKey(i.type()));
if (base == null) {
- log.error(i.position(), ErrorKind.SYMBOL_NOT_FOUND, Joiner.on(".").join(i.type()));
+ log.error(
+ i.position(), ErrorKind.SYMBOL_NOT_FOUND, new ClassSymbol(Joiner.on("/").join(i.type())));
return null;
}
return new ImportScope() {
diff --git a/java/com/google/turbine/binder/lookup/PackageScope.java b/java/com/google/turbine/binder/lookup/PackageScope.java
new file mode 100644
index 0000000..695e802
--- /dev/null
+++ b/java/com/google/turbine/binder/lookup/PackageScope.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2019 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.lookup;
+
+import com.google.common.collect.Iterables;
+import com.google.turbine.binder.sym.ClassSymbol;
+import org.checkerframework.checker.nullness.qual.Nullable;
+
+/**
+ * A scope that corresponds to a particular package, which supports iteration over its enclosed
+ * classes.
+ */
+public interface PackageScope extends Scope {
+
+ /** Returns the top-level classes enclosed by this package. */
+ Iterable<ClassSymbol> classes();
+
+ default PackageScope append(PackageScope next) {
+ return concat(this, next);
+ }
+
+ static PackageScope concat(PackageScope base, PackageScope next) {
+ return new PackageScope() {
+ @Override
+ public Iterable<ClassSymbol> classes() {
+ return Iterables.concat(base.classes(), next.classes());
+ }
+
+ @Override
+ public @Nullable LookupResult lookup(LookupKey lookupKey) {
+ LookupResult result = base.lookup(lookupKey);
+ if (result != null) {
+ return result;
+ }
+ return next.lookup(lookupKey);
+ }
+ };
+ }
+}
diff --git a/java/com/google/turbine/binder/lookup/SimpleTopLevelIndex.java b/java/com/google/turbine/binder/lookup/SimpleTopLevelIndex.java
index 2454319..4ec05bc 100644
--- a/java/com/google/turbine/binder/lookup/SimpleTopLevelIndex.java
+++ b/java/com/google/turbine/binder/lookup/SimpleTopLevelIndex.java
@@ -16,6 +16,8 @@
package com.google.turbine.binder.lookup;
+import com.google.common.base.Supplier;
+import com.google.common.base.Suppliers;
import com.google.common.collect.ImmutableList;
import com.google.turbine.binder.sym.ClassSymbol;
import java.util.HashMap;
@@ -157,7 +159,7 @@ public class SimpleTopLevelIndex implements TopLevelIndex {
/** Returns a {@link Scope} that performs lookups in the given qualified package name. */
@Override
- public Scope lookupPackage(ImmutableList<String> packagename) {
+ public PackageScope lookupPackage(Iterable<String> packagename) {
Node curr = root;
for (String bit : packagename) {
curr = curr.lookup(bit);
@@ -168,7 +170,7 @@ public class SimpleTopLevelIndex implements TopLevelIndex {
return new PackageIndex(curr);
}
- static class PackageIndex implements Scope {
+ static class PackageIndex implements PackageScope {
private final Node node;
@@ -184,5 +186,25 @@ public class SimpleTopLevelIndex implements TopLevelIndex {
}
return null;
}
+
+ private final Supplier<ImmutableList<ClassSymbol>> classes =
+ Suppliers.memoize(
+ new Supplier<ImmutableList<ClassSymbol>>() {
+ @Override
+ public ImmutableList<ClassSymbol> get() {
+ ImmutableList.Builder<ClassSymbol> result = ImmutableList.builder();
+ for (Node child : node.children.values()) {
+ if (child.sym != null) {
+ result.add(child.sym);
+ }
+ }
+ return result.build();
+ }
+ });
+
+ @Override
+ public Iterable<ClassSymbol> classes() {
+ return classes.get();
+ }
}
}
diff --git a/java/com/google/turbine/binder/lookup/TopLevelIndex.java b/java/com/google/turbine/binder/lookup/TopLevelIndex.java
index 95782f5..a364119 100644
--- a/java/com/google/turbine/binder/lookup/TopLevelIndex.java
+++ b/java/com/google/turbine/binder/lookup/TopLevelIndex.java
@@ -16,7 +16,6 @@
package com.google.turbine.binder.lookup;
-import com.google.common.collect.ImmutableList;
/**
* An index of canonical type names.
@@ -36,5 +35,5 @@ public interface TopLevelIndex {
Scope scope();
/** Returns a scope to look up members of the given package. */
- Scope lookupPackage(ImmutableList<String> packagename);
+ PackageScope lookupPackage(Iterable<String> packagename);
}
diff --git a/java/com/google/turbine/binder/sym/ClassSymbol.java b/java/com/google/turbine/binder/sym/ClassSymbol.java
index 2adf15f..20513e7 100644
--- a/java/com/google/turbine/binder/sym/ClassSymbol.java
+++ b/java/com/google/turbine/binder/sym/ClassSymbol.java
@@ -33,6 +33,23 @@ public class ClassSymbol implements Symbol {
public static final ClassSymbol STRING = new ClassSymbol("java/lang/String");
public static final ClassSymbol ENUM = new ClassSymbol("java/lang/Enum");
public static final ClassSymbol ANNOTATION = new ClassSymbol("java/lang/annotation/Annotation");
+ public static final ClassSymbol INHERITED = new ClassSymbol("java/lang/annotation/Inherited");
+ public static final ClassSymbol CLONEABLE = new ClassSymbol("java/lang/Cloneable");
+ public static final ClassSymbol SERIALIZABLE = new ClassSymbol("java/io/Serializable");
+ public static final ClassSymbol DEPRECATED = new ClassSymbol("java/lang/Deprecated");
+ public static final ClassSymbol PROFILE_ANNOTATION = new ClassSymbol("jdk/Profile+Annotation");
+ public static final ClassSymbol PROPRIETARY_ANNOTATION =
+ new ClassSymbol("sun/Proprietary+Annotation");
+ public static final ClassSymbol ERROR = new ClassSymbol("<error>");
+
+ public static final ClassSymbol CHARACTER = new ClassSymbol("java/lang/Character");
+ public static final ClassSymbol SHORT = new ClassSymbol("java/lang/Short");
+ public static final ClassSymbol INTEGER = new ClassSymbol("java/lang/Integer");
+ public static final ClassSymbol LONG = new ClassSymbol("java/lang/Long");
+ public static final ClassSymbol FLOAT = new ClassSymbol("java/lang/Float");
+ public static final ClassSymbol DOUBLE = new ClassSymbol("java/lang/Double");
+ public static final ClassSymbol BOOLEAN = new ClassSymbol("java/lang/Boolean");
+ public static final ClassSymbol BYTE = new ClassSymbol("java/lang/Byte");
private final String className;
@@ -64,4 +81,17 @@ public class ClassSymbol implements Symbol {
public Kind symKind() {
return Kind.CLASS;
}
+
+ public String simpleName() {
+ return binaryName().substring(binaryName().lastIndexOf('/') + 1);
+ }
+
+ public String packageName() {
+ int idx = binaryName().lastIndexOf('/');
+ return idx != -1 ? binaryName().substring(0, idx) : "";
+ }
+
+ public PackageSymbol owner() {
+ return new PackageSymbol(packageName());
+ }
}
diff --git a/java/com/google/turbine/binder/sym/FieldSymbol.java b/java/com/google/turbine/binder/sym/FieldSymbol.java
index 21304e7..d6c3cbc 100644
--- a/java/com/google/turbine/binder/sym/FieldSymbol.java
+++ b/java/com/google/turbine/binder/sym/FieldSymbol.java
@@ -61,6 +61,6 @@ public class FieldSymbol implements Symbol {
@Override
public String toString() {
- return owner + "#" + name;
+ return name;
}
}
diff --git a/java/com/google/turbine/binder/sym/MethodSymbol.java b/java/com/google/turbine/binder/sym/MethodSymbol.java
index b2050f4..f4b211d 100644
--- a/java/com/google/turbine/binder/sym/MethodSymbol.java
+++ b/java/com/google/turbine/binder/sym/MethodSymbol.java
@@ -22,10 +22,17 @@ import java.util.Objects;
/** A method symbol. */
@Immutable
public class MethodSymbol implements Symbol {
+ /**
+ * The index of the method in its enclosing element. Used to implement equals and hashCode, since
+ * methods aren't uniquely identified by their name and owner.
+ */
+ private final int idx;
+
private final ClassSymbol owner;
private final String name;
- public MethodSymbol(ClassSymbol owner, String name) {
+ public MethodSymbol(int idx, ClassSymbol owner, String name) {
+ this.idx = idx;
this.owner = owner;
this.name = name;
}
@@ -56,7 +63,7 @@ public class MethodSymbol implements Symbol {
return false;
}
MethodSymbol other = (MethodSymbol) obj;
- return name().equals(other.name()) && owner().equals(other.owner());
+ return name().equals(other.name()) && owner().equals(other.owner()) && idx == other.idx;
}
@Override
diff --git a/java/com/google/turbine/binder/sym/PackageSymbol.java b/java/com/google/turbine/binder/sym/PackageSymbol.java
new file mode 100644
index 0000000..8354a34
--- /dev/null
+++ b/java/com/google/turbine/binder/sym/PackageSymbol.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2019 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.sym;
+
+import com.google.errorprone.annotations.Immutable;
+
+/** A package symbol. */
+@Immutable
+public class PackageSymbol implements Symbol {
+
+ final String binaryName;
+
+ public PackageSymbol(String binaryName) {
+ this.binaryName = binaryName;
+ }
+
+ @Override
+ public int hashCode() {
+ return binaryName.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return obj instanceof PackageSymbol && binaryName.equals(((PackageSymbol) obj).binaryName);
+ }
+
+ @Override
+ public String toString() {
+ return binaryName.replace('/', '.');
+ }
+
+ @Override
+ public Kind symKind() {
+ return Kind.PACKAGE;
+ }
+
+ public String binaryName() {
+ return binaryName;
+ }
+}
diff --git a/java/com/google/turbine/binder/sym/ParamSymbol.java b/java/com/google/turbine/binder/sym/ParamSymbol.java
new file mode 100644
index 0000000..328658e
--- /dev/null
+++ b/java/com/google/turbine/binder/sym/ParamSymbol.java
@@ -0,0 +1,66 @@
+/*
+ * 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.sym;
+
+import com.google.errorprone.annotations.Immutable;
+import java.util.Objects;
+
+/** A parameter symbol. */
+@Immutable
+public class ParamSymbol implements Symbol {
+ private final MethodSymbol owner;
+ private final String name;
+
+ public ParamSymbol(MethodSymbol owner, String name) {
+ this.owner = owner;
+ this.name = name;
+ }
+
+ /** The enclosing class. */
+ public MethodSymbol owner() {
+ return owner;
+ }
+
+ /** The parameter name. */
+ public String name() {
+ return name;
+ }
+
+ @Override
+ public Kind symKind() {
+ return Kind.PARAMETER;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(name, owner);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof ParamSymbol)) {
+ return false;
+ }
+ ParamSymbol other = (ParamSymbol) obj;
+ return name().equals(other.name()) && owner().equals(other.owner());
+ }
+
+ @Override
+ public String toString() {
+ return name;
+ }
+}
diff --git a/java/com/google/turbine/binder/sym/Symbol.java b/java/com/google/turbine/binder/sym/Symbol.java
index b2a7723..bc142cb 100644
--- a/java/com/google/turbine/binder/sym/Symbol.java
+++ b/java/com/google/turbine/binder/sym/Symbol.java
@@ -27,7 +27,9 @@ public interface Symbol {
TY_PARAM,
METHOD,
FIELD,
- MODULE
+ PARAMETER,
+ MODULE,
+ PACKAGE
}
/** The symbol kind. */
diff --git a/java/com/google/turbine/binder/sym/TyVarSymbol.java b/java/com/google/turbine/binder/sym/TyVarSymbol.java
index a73a79e..1ecec11 100644
--- a/java/com/google/turbine/binder/sym/TyVarSymbol.java
+++ b/java/com/google/turbine/binder/sym/TyVarSymbol.java
@@ -62,6 +62,6 @@ public class TyVarSymbol implements Symbol {
@Override
public String toString() {
- return owner + "#" + name;
+ return name;
}
}
diff --git a/java/com/google/turbine/bytecode/AnnotationWriter.java b/java/com/google/turbine/bytecode/AnnotationWriter.java
index 868b548..b547971 100644
--- a/java/com/google/turbine/bytecode/AnnotationWriter.java
+++ b/java/com/google/turbine/bytecode/AnnotationWriter.java
@@ -20,8 +20,8 @@ import com.google.common.collect.ImmutableList;
import com.google.common.io.ByteArrayDataOutput;
import com.google.turbine.bytecode.ClassFile.AnnotationInfo;
import com.google.turbine.bytecode.ClassFile.AnnotationInfo.ElementValue;
-import com.google.turbine.bytecode.ClassFile.AnnotationInfo.ElementValue.AnnotationValue;
import com.google.turbine.bytecode.ClassFile.AnnotationInfo.ElementValue.ArrayValue;
+import com.google.turbine.bytecode.ClassFile.AnnotationInfo.ElementValue.ConstTurbineAnnotationValue;
import com.google.turbine.bytecode.ClassFile.AnnotationInfo.ElementValue.ConstTurbineClassValue;
import com.google.turbine.bytecode.ClassFile.AnnotationInfo.ElementValue.ConstValue;
import com.google.turbine.bytecode.ClassFile.AnnotationInfo.ElementValue.EnumConstValue;
@@ -71,10 +71,8 @@ public class AnnotationWriter {
writeArrayElementValue((ArrayValue) value);
break;
case ANNOTATION:
- writeAnnotationElementValue((AnnotationValue) value);
+ writeAnnotationElementValue((ConstTurbineAnnotationValue) value);
break;
- default:
- throw new AssertionError(value.kind());
}
}
@@ -136,7 +134,7 @@ public class AnnotationWriter {
}
}
- private void writeAnnotationElementValue(AnnotationValue value) {
+ private void writeAnnotationElementValue(ConstTurbineAnnotationValue value) {
output.writeByte('@');
writeAnnotation(value.annotation());
}
@@ -178,8 +176,6 @@ public class AnnotationWriter {
output.writeByte(typeParameterBoundTarget.typeParameterIndex());
output.writeByte(typeParameterBoundTarget.boundIndex());
break;
- default:
- throw new AssertionError(target.kind());
}
}
}
diff --git a/java/com/google/turbine/bytecode/AttributeWriter.java b/java/com/google/turbine/bytecode/AttributeWriter.java
index 5e3ea95..c5ffd16 100644
--- a/java/com/google/turbine/bytecode/AttributeWriter.java
+++ b/java/com/google/turbine/bytecode/AttributeWriter.java
@@ -87,8 +87,6 @@ public class AttributeWriter {
case MODULE:
writeModule((Attribute.Module) attribute);
break;
- default:
- throw new AssertionError(attribute.kind());
}
}
diff --git a/java/com/google/turbine/bytecode/ClassFile.java b/java/com/google/turbine/bytecode/ClassFile.java
index 54b6983..8ee2aac 100644
--- a/java/com/google/turbine/bytecode/ClassFile.java
+++ b/java/com/google/turbine/bytecode/ClassFile.java
@@ -368,7 +368,7 @@ public class ClassFile {
public interface ElementValue {
/** The value kind. */
- Kind kind();
+ ElementValue.Kind kind();
/** Element value kinds. */
enum Kind {
@@ -391,8 +391,8 @@ public class ClassFile {
}
@Override
- public Kind kind() {
- return Kind.ENUM;
+ public ElementValue.Kind kind() {
+ return ElementValue.Kind.ENUM;
}
/** The type of the enum. */
@@ -417,8 +417,8 @@ public class ClassFile {
}
@Override
- public Kind kind() {
- return Kind.CONST;
+ public ElementValue.Kind kind() {
+ return ElementValue.Kind.CONST;
}
/** The constant value. */
@@ -437,8 +437,8 @@ public class ClassFile {
}
@Override
- public Kind kind() {
- return Kind.ARRAY;
+ public ElementValue.Kind kind() {
+ return ElementValue.Kind.ARRAY;
}
/** The elements of the array. */
@@ -457,8 +457,8 @@ public class ClassFile {
}
@Override
- public Kind kind() {
- return Kind.CLASS;
+ public ElementValue.Kind kind() {
+ return ElementValue.Kind.CLASS;
}
/** The class name. */
@@ -468,17 +468,17 @@ public class ClassFile {
}
/** A nested annotation value. */
- class AnnotationValue implements ElementValue {
+ class ConstTurbineAnnotationValue implements ElementValue {
private final AnnotationInfo annotation;
- public AnnotationValue(AnnotationInfo annotation) {
+ public ConstTurbineAnnotationValue(AnnotationInfo annotation) {
this.annotation = annotation;
}
@Override
- public Kind kind() {
- return Kind.ANNOTATION;
+ public ElementValue.Kind kind() {
+ return ElementValue.Kind.ANNOTATION;
}
/** The annotation. */
@@ -566,7 +566,7 @@ public class ClassFile {
}
/** Returns the target info kind. */
- public abstract Kind kind();
+ public abstract Target.Kind kind();
}
/** A JVMS 4.7.20.1 type_parameter_target. */
@@ -582,8 +582,8 @@ public class ClassFile {
}
@Override
- public Kind kind() {
- return Kind.TYPE_PARAMETER;
+ public Target.Kind kind() {
+ return Target.Kind.TYPE_PARAMETER;
}
}
@@ -596,8 +596,8 @@ public class ClassFile {
}
@Override
- public Kind kind() {
- return Kind.SUPERTYPE;
+ public Target.Kind kind() {
+ return Target.Kind.SUPERTYPE;
}
public int index() {
@@ -616,8 +616,8 @@ public class ClassFile {
}
@Override
- public Kind kind() {
- return Kind.TYPE_PARAMETER_BOUND;
+ public Target.Kind kind() {
+ return Target.Kind.TYPE_PARAMETER_BOUND;
}
public int typeParameterIndex() {
@@ -633,8 +633,8 @@ public class ClassFile {
public static final Target EMPTY_TARGET =
new Target() {
@Override
- public Kind kind() {
- return Kind.EMPTY;
+ public Target.Kind kind() {
+ return Target.Kind.EMPTY;
}
};
@@ -647,8 +647,8 @@ public class ClassFile {
}
@Override
- public Kind kind() {
- return Kind.FORMAL_PARAMETER;
+ public Target.Kind kind() {
+ return Target.Kind.FORMAL_PARAMETER;
}
public int index() {
@@ -665,8 +665,8 @@ public class ClassFile {
}
@Override
- public Kind kind() {
- return Kind.THROWS;
+ public Target.Kind kind() {
+ return Target.Kind.THROWS;
}
public int index() {
@@ -689,22 +689,22 @@ public class ClassFile {
/** Adds an array type_path_kind entry. */
public TypePath array() {
- return new TypePath(Kind.ARRAY, this);
+ return new TypePath(TypePath.Kind.ARRAY, this);
}
/** Adds a nested type type_path_kind entry. */
public TypePath nested() {
- return new TypePath(Kind.NESTED, this);
+ return new TypePath(TypePath.Kind.NESTED, this);
}
/** Adds a wildcard bound type_path_kind entry. */
public TypePath wild() {
- return new TypePath(Kind.WILDCARD_BOUND, this);
+ return new TypePath(TypePath.Kind.WILDCARD_BOUND, this);
}
/** Adds a type argument type_path_kind entry. */
public TypePath typeArgument(int idx) {
- return new TypePath(idx, Kind.TYPE_ARGUMENT, this);
+ return new TypePath(idx, TypePath.Kind.TYPE_ARGUMENT, this);
}
/** A type_path_kind. */
@@ -722,15 +722,15 @@ public class ClassFile {
}
private final TypePath parent;
- private final Kind kind;
+ private final TypePath.Kind kind;
private final int index;
- private TypePath(Kind kind, TypePath parent) {
+ private TypePath(TypePath.Kind kind, TypePath parent) {
// JVMS 4.7.20.2: type_argument_index is 0 if the bound kind is not TYPE_ARGUMENT
this(0, kind, parent);
}
- private TypePath(int index, Kind kind, TypePath parent) {
+ private TypePath(int index, TypePath.Kind kind, TypePath parent) {
this.index = index;
this.kind = kind;
this.parent = parent;
diff --git a/java/com/google/turbine/bytecode/ClassReader.java b/java/com/google/turbine/bytecode/ClassReader.java
index a894997..9c79b42 100644
--- a/java/com/google/turbine/bytecode/ClassReader.java
+++ b/java/com/google/turbine/bytecode/ClassReader.java
@@ -19,9 +19,10 @@ package com.google.turbine.bytecode;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.errorprone.annotations.CheckReturnValue;
+import com.google.errorprone.annotations.FormatMethod;
import com.google.turbine.bytecode.ClassFile.AnnotationInfo;
import com.google.turbine.bytecode.ClassFile.AnnotationInfo.ElementValue;
-import com.google.turbine.bytecode.ClassFile.AnnotationInfo.ElementValue.AnnotationValue;
+import com.google.turbine.bytecode.ClassFile.AnnotationInfo.ElementValue.ConstTurbineAnnotationValue;
import com.google.turbine.bytecode.ClassFile.AnnotationInfo.ElementValue.ConstTurbineClassValue;
import com.google.turbine.bytecode.ClassFile.AnnotationInfo.ElementValue.ConstValue;
import com.google.turbine.bytecode.ClassFile.AnnotationInfo.ElementValue.EnumConstValue;
@@ -33,6 +34,7 @@ import com.google.turbine.bytecode.ClassFile.ModuleInfo.ProvideInfo;
import com.google.turbine.bytecode.ClassFile.ModuleInfo.RequireInfo;
import com.google.turbine.bytecode.ClassFile.ModuleInfo.UseInfo;
import com.google.turbine.model.Const;
+import com.google.turbine.model.TurbineFlag;
import java.util.ArrayList;
import java.util.List;
import org.checkerframework.checker.nullness.qual.Nullable;
@@ -59,6 +61,7 @@ public class ClassReader {
this.reader = new ByteReader(bytes, 0);
}
+ @FormatMethod
@CheckReturnValue
Error error(String format, Object... args) {
StringBuilder sb = new StringBuilder();
@@ -72,7 +75,7 @@ public class ClassReader {
private ClassFile read() {
int magic = reader.u4();
if (magic != 0xcafebabe) {
- throw error("bad magic: 0x%x", path, magic);
+ throw error("bad magic: 0x%x", magic);
}
int minorVersion = reader.u2();
int majorVersion = reader.u2();
@@ -212,6 +215,11 @@ public class ClassReader {
for (int i = 0; i < numParameters; i++) {
String name = constantPool.utf8(reader.u2());
int access = reader.u2();
+ if ((access & (TurbineFlag.ACC_SYNTHETIC | TurbineFlag.ACC_MANDATED)) != 0) {
+ // ExecutableElement#getParameters doesn't expect synthetic or mandated
+ // parameters
+ continue;
+ }
parameters.add(new ParameterInfo(name, access));
}
}
@@ -321,17 +329,22 @@ public class ClassReader {
int tag = reader.u1();
switch (tag) {
case 'B':
+ return new ConstValue(readConst(constantPool).asByte());
case 'C':
+ return new ConstValue(readConst(constantPool).asChar());
+ case 'S':
+ return new ConstValue(readConst(constantPool).asShort());
case 'D':
case 'F':
case 'I':
case 'J':
- case 'S':
- case 'Z':
case 's':
+ return new ConstValue(readConst(constantPool));
+ case 'Z':
{
- int constValueIndex = reader.u2();
- return new ConstValue(constantPool.constant(constValueIndex));
+ Const.Value value = readConst(constantPool);
+ // boolean constants are encoded as integers
+ return new ConstValue(new Const.BooleanValue(value.asInteger().value() != 0));
}
case 'e':
{
@@ -348,7 +361,7 @@ public class ClassReader {
return new ConstTurbineClassValue(className);
}
case '@':
- return new AnnotationValue(readAnnotation(constantPool));
+ return new ConstTurbineAnnotationValue(readAnnotation(constantPool));
case '[':
{
int numValues = reader.u2();
@@ -363,6 +376,11 @@ public class ClassReader {
throw new AssertionError(String.format("bad tag value %c", tag));
}
+ private Const.Value readConst(ConstantPoolReader constantPool) {
+ int constValueIndex = reader.u2();
+ return constantPool.constant(constValueIndex);
+ }
+
/** Reads JVMS 4.6 method_infos. */
private List<ClassFile.MethodInfo> readMethods(ConstantPoolReader constantPool) {
int methodsCount = reader.u2();
@@ -415,6 +433,10 @@ public class ClassReader {
for (ImmutableList.Builder<AnnotationInfo> x : parameterAnnotationsBuilder) {
parameterAnnotations.add(x.build());
}
+ if ((accessFlags & (TurbineFlag.ACC_BRIDGE | TurbineFlag.ACC_SYNTHETIC)) != 0) {
+ // javac doesn't enter synthetic members for reasons 'lost to history', so we don't either
+ continue;
+ }
methods.add(
new ClassFile.MethodInfo(
accessFlags,
@@ -454,6 +476,8 @@ public class ClassReader {
String desc = constantPool.utf8(descriptorIndex);
int attributesCount = reader.u2();
Const.Value value = null;
+ ImmutableList.Builder<ClassFile.AnnotationInfo> annotations = ImmutableList.builder();
+ String signature = null;
for (int j = 0; j < attributesCount; j++) {
String attributeName = constantPool.utf8(reader.u2());
switch (attributeName) {
@@ -461,6 +485,13 @@ public class ClassReader {
reader.u4(); // length
value = constantPool.constant(reader.u2());
break;
+ case "RuntimeInvisibleAnnotations":
+ case "RuntimeVisibleAnnotations":
+ readAnnotations(annotations, constantPool);
+ break;
+ case "Signature":
+ signature = readSignature(constantPool);
+ break;
default:
reader.skip(reader.u4());
break;
@@ -471,10 +502,10 @@ public class ClassReader {
accessFlags,
name,
desc,
- /*signature*/ null,
+ signature,
value,
- ImmutableList.of(),
- ImmutableList.of()));
+ annotations.build(),
+ /* typeAnnotations= */ ImmutableList.of()));
}
return fields;
}
diff --git a/java/com/google/turbine/bytecode/ClassWriter.java b/java/com/google/turbine/bytecode/ClassWriter.java
index 4a89ec8..c3490ca 100644
--- a/java/com/google/turbine/bytecode/ClassWriter.java
+++ b/java/com/google/turbine/bytecode/ClassWriter.java
@@ -110,8 +110,6 @@ public class ClassWriter {
case UTF8:
output.writeUTF(((StringValue) value).value());
break;
- default:
- throw new AssertionError(e.kind());
}
}
}
diff --git a/java/com/google/turbine/bytecode/ConstantPool.java b/java/com/google/turbine/bytecode/ConstantPool.java
index b423cfc..673102c 100644
--- a/java/com/google/turbine/bytecode/ConstantPool.java
+++ b/java/com/google/turbine/bytecode/ConstantPool.java
@@ -66,9 +66,8 @@ public class ConstantPool {
// "In retrospect, making 8-byte constants take two constant pool entries
// was a poor choice." -- JVMS 4.4.5
return 2;
- default:
- throw new AssertionError(kind);
}
+ throw new AssertionError(kind);
}
/** A constant pool entry. */
diff --git a/java/com/google/turbine/bytecode/sig/SigWriter.java b/java/com/google/turbine/bytecode/sig/SigWriter.java
index dab4f47..3711186 100644
--- a/java/com/google/turbine/bytecode/sig/SigWriter.java
+++ b/java/com/google/turbine/bytecode/sig/SigWriter.java
@@ -113,8 +113,6 @@ public class SigWriter {
sb.append('+');
writeTySig(((UpperBoundTySig) sig).bound());
break;
- default:
- throw new AssertionError(sig.kind());
}
}
@@ -198,8 +196,6 @@ public class SigWriter {
case WILD_TY_SIG:
wildTyArgSig((WildTySig) p);
break;
- default:
- throw new AssertionError(p.kind());
}
}
diff --git a/java/com/google/turbine/deps/Dependencies.java b/java/com/google/turbine/deps/Dependencies.java
index f76efe7..92193e8 100644
--- a/java/com/google/turbine/deps/Dependencies.java
+++ b/java/com/google/turbine/deps/Dependencies.java
@@ -22,14 +22,22 @@ import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.turbine.binder.Binder.BindingResult;
import com.google.turbine.binder.ClassPath;
+import com.google.turbine.binder.bound.EnumConstantValue;
+import com.google.turbine.binder.bound.TurbineAnnotationValue;
+import com.google.turbine.binder.bound.TurbineClassValue;
import com.google.turbine.binder.bound.TypeBoundClass;
+import com.google.turbine.binder.bound.TypeBoundClass.FieldInfo;
+import com.google.turbine.binder.bound.TypeBoundClass.MethodInfo;
import com.google.turbine.binder.bytecode.BytecodeBoundClass;
import com.google.turbine.binder.env.CompoundEnv;
import com.google.turbine.binder.env.Env;
import com.google.turbine.binder.env.SimpleEnv;
import com.google.turbine.binder.sym.ClassSymbol;
import com.google.turbine.lower.Lower.Lowered;
+import com.google.turbine.model.Const;
import com.google.turbine.proto.DepsProto;
+import com.google.turbine.type.AnnoInfo;
+import com.google.turbine.type.Type;
import java.io.BufferedInputStream;
import java.io.IOError;
import java.io.IOException;
@@ -82,13 +90,59 @@ public class Dependencies {
Env<ClassSymbol, TypeBoundClass> env =
CompoundEnv.<ClassSymbol, TypeBoundClass>of(new SimpleEnv<>(bound.units()))
.append(bound.classPathEnv());
- Set<ClassSymbol> closure = new LinkedHashSet<>();
+ Set<ClassSymbol> closure = new LinkedHashSet<>(lowered.symbols());
for (ClassSymbol sym : lowered.symbols()) {
- addSuperTypes(closure, env, sym);
+ TypeBoundClass info = env.get(sym);
+ addAnnotations(closure, info.annotations());
+ for (MethodInfo method : info.methods()) {
+ addAnnotations(closure, method.annotations());
+ }
+ for (FieldInfo field : info.fields()) {
+ addAnnotations(closure, field.annotations());
+ }
+ addSuperTypes(closure, env, info);
}
return closure;
}
+ private static void addAnnotations(
+ Set<ClassSymbol> closure, ImmutableList<AnnoInfo> annotations) {
+ for (AnnoInfo annoInfo : annotations) {
+ addAnnotation(closure, annoInfo);
+ }
+ }
+
+ private static void addAnnotation(Set<ClassSymbol> closure, AnnoInfo annoInfo) {
+ closure.add(annoInfo.sym());
+ for (Const c : annoInfo.values().values()) {
+ addConst(closure, c);
+ }
+ }
+
+ private static void addConst(Set<ClassSymbol> closure, Const c) {
+ switch (c.kind()) {
+ case ARRAY:
+ for (Const e : ((Const.ArrayInitValue) c).elements()) {
+ addConst(closure, e);
+ }
+ break;
+ case CLASS_LITERAL:
+ Type t = ((TurbineClassValue) c).type();
+ if (t.tyKind() == Type.TyKind.CLASS_TY) {
+ closure.add(((Type.ClassTy) t).sym());
+ }
+ break;
+ case ENUM_CONSTANT:
+ closure.add(((EnumConstantValue) c).sym().owner());
+ break;
+ case ANNOTATION:
+ addAnnotation(closure, ((TurbineAnnotationValue) c).info());
+ break;
+ case PRIMITIVE:
+ // continue below
+ }
+ }
+
private static void addSuperTypes(
Set<ClassSymbol> closure, Env<ClassSymbol, TypeBoundClass> env, ClassSymbol sym) {
if (!closure.add(sym)) {
@@ -98,6 +152,11 @@ public class Dependencies {
if (info == null) {
return;
}
+ addSuperTypes(closure, env, info);
+ }
+
+ private static void addSuperTypes(
+ Set<ClassSymbol> closure, Env<ClassSymbol, TypeBoundClass> env, TypeBoundClass info) {
if (info.superclass() != null) {
addSuperTypes(closure, env, info.superclass());
}
@@ -109,11 +168,11 @@ public class Dependencies {
private static void addPackageInfos(Set<ClassSymbol> closure, BindingResult bound) {
Set<ClassSymbol> packages = new LinkedHashSet<>();
for (ClassSymbol sym : closure) {
- int idx = sym.binaryName().lastIndexOf('/');
- if (idx == -1) {
+ String packageName = sym.packageName();
+ if (packageName.isEmpty()) {
continue;
}
- packages.add(new ClassSymbol(sym.binaryName().substring(0, idx) + "/package-info"));
+ packages.add(new ClassSymbol(packageName + "/package-info"));
}
for (ClassSymbol pkg : packages) {
if (bound.classPathEnv().get(pkg) != null) {
@@ -134,6 +193,7 @@ public class Dependencies {
ImmutableList<String> depsArtifacts) {
if (directJars.isEmpty()) {
// the compilation doesn't support strict deps (e.g. proto libraries)
+ // TODO(cushon): make this a usage error
return transitiveClasspath;
}
Set<String> reduced = new HashSet<>(directJars);
@@ -153,8 +213,6 @@ public class Dependencies {
case INCOMPLETE:
case UNUSED:
break;
- default:
- throw new AssertionError(dep.getKind());
}
}
}
diff --git a/java/com/google/turbine/deps/Transitive.java b/java/com/google/turbine/deps/Transitive.java
index f9a29a1..8b0d44d 100644
--- a/java/com/google/turbine/deps/Transitive.java
+++ b/java/com/google/turbine/deps/Transitive.java
@@ -17,7 +17,6 @@
package com.google.turbine.deps;
import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableList.Builder;
import com.google.common.collect.ImmutableMap;
import com.google.turbine.binder.Binder.BindingResult;
import com.google.turbine.binder.ClassPath;
@@ -65,7 +64,7 @@ public class Transitive {
*/
public static ClassFile trimClass(ClassFile cf) {
// drop non-constant fields
- Builder<FieldInfo> fields = ImmutableList.builder();
+ ImmutableList.Builder<FieldInfo> fields = ImmutableList.builder();
for (FieldInfo f : cf.fields()) {
if (f.value() != null) {
fields.add(f);
@@ -75,7 +74,7 @@ public class Transitive {
// To do this for javac, we would have to scan all remaining signatures and preserve attributes
// for reachable inner classes, but turbine only needs the attributes for the immediate
// children or parent of the current class.
- Builder<InnerClass> innerClasses = ImmutableList.builder();
+ ImmutableList.Builder<InnerClass> innerClasses = ImmutableList.builder();
for (InnerClass i : cf.innerClasses()) {
if (i.innerClass().equals(cf.name()) || i.outerClass().equals(cf.name())) {
innerClasses.add(i);
diff --git a/java/com/google/turbine/diag/SourceFile.java b/java/com/google/turbine/diag/SourceFile.java
index cb4133b..3868252 100644
--- a/java/com/google/turbine/diag/SourceFile.java
+++ b/java/com/google/turbine/diag/SourceFile.java
@@ -16,12 +16,25 @@
package com.google.turbine.diag;
+import com.google.common.base.Supplier;
+import com.google.common.base.Suppliers;
+import java.util.Objects;
+
/** A source file. */
public class SourceFile {
private final String path;
private final String source;
+ private final Supplier<LineMap> lineMap =
+ Suppliers.memoize(
+ new Supplier<LineMap>() {
+ @Override
+ public LineMap get() {
+ return LineMap.create(source);
+ }
+ });
+
public SourceFile(String path, String source) {
this.path = path;
this.source = source;
@@ -36,4 +49,22 @@ public class SourceFile {
public String source() {
return source;
}
+
+ LineMap lineMap() {
+ return lineMap.get();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof SourceFile)) {
+ return false;
+ }
+ SourceFile that = (SourceFile) obj;
+ return Objects.equals(path, that.path) && source.equals(that.source);
+ }
+
+ @Override
+ public int hashCode() {
+ return path != null ? path.hashCode() : 0;
+ }
}
diff --git a/java/com/google/turbine/diag/TurbineDiagnostic.java b/java/com/google/turbine/diag/TurbineDiagnostic.java
index 0404a8e..ccbaa7f 100644
--- a/java/com/google/turbine/diag/TurbineDiagnostic.java
+++ b/java/com/google/turbine/diag/TurbineDiagnostic.java
@@ -16,7 +16,6 @@
package com.google.turbine.diag;
-import static com.google.common.base.MoreObjects.firstNonNull;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.collect.Iterables.getOnlyElement;
import static java.util.Objects.requireNonNull;
@@ -27,18 +26,29 @@ import com.google.common.collect.ImmutableList;
import com.google.turbine.binder.sym.ClassSymbol;
import com.google.turbine.diag.TurbineError.ErrorKind;
import java.util.Objects;
+import javax.tools.Diagnostic;
+import org.checkerframework.checker.nullness.qual.Nullable;
/** A compilation error. */
public class TurbineDiagnostic {
+ private final Diagnostic.Kind severity;
private final ErrorKind kind;
- private final String diagnostic;
private final ImmutableList<Object> args;
+ private final @Nullable SourceFile source;
+ private final int position;
- private TurbineDiagnostic(ErrorKind kind, String diagnostic, ImmutableList<Object> args) {
+ private TurbineDiagnostic(
+ Diagnostic.Kind severity,
+ ErrorKind kind,
+ ImmutableList<Object> args,
+ @Nullable SourceFile source,
+ int position) {
+ this.severity = requireNonNull(severity);
this.kind = requireNonNull(kind);
- this.diagnostic = requireNonNull(diagnostic);
this.args = requireNonNull(args);
+ this.source = source;
+ this.position = position;
}
/** The diagnostic kind. */
@@ -46,9 +56,28 @@ public class TurbineDiagnostic {
return kind;
}
+ /**
+ * The diagnostic severity (error, warning, ...). Turbine only produces errors, non-error
+ * diagnostics are only ever created by annotation processors.
+ */
+ public Diagnostic.Kind severity() {
+ return severity;
+ }
+
/** The diagnostic message. */
public String diagnostic() {
- return diagnostic;
+ StringBuilder sb = new StringBuilder(path());
+ if (line() != -1) {
+ sb.append(':').append(line());
+ }
+ sb.append(": error: ");
+ sb.append(message().trim()).append(System.lineSeparator());
+ if (line() != -1 && column() != -1) {
+ sb.append(CharMatcher.breakingWhitespace().trimTrailingFrom(source.lineMap().line(position)))
+ .append(System.lineSeparator());
+ sb.append(Strings.repeat(" ", column() - 1)).append('^');
+ }
+ return sb.toString();
}
/** The diagnostic arguments. */
@@ -57,20 +86,28 @@ public class TurbineDiagnostic {
}
private static TurbineDiagnostic create(
- ErrorKind kind, String diagnostic, ImmutableList<Object> args) {
+ Diagnostic.Kind severity,
+ ErrorKind kind,
+ ImmutableList<Object> args,
+ SourceFile source,
+ int position) {
switch (kind) {
case SYMBOL_NOT_FOUND:
{
checkArgument(
args.size() == 1 && getOnlyElement(args) instanceof ClassSymbol,
- "diagnostic (%s) has invalid argument args %s",
- diagnostic,
+ "diagnostic (%s) has invalid argument %s",
+ kind,
args);
break;
}
default: // fall out
}
- return new TurbineDiagnostic(kind, diagnostic, args);
+ return new TurbineDiagnostic(severity, kind, args, source, position);
+ }
+
+ public static TurbineDiagnostic format(Diagnostic.Kind severity, ErrorKind kind, String message) {
+ return create(severity, kind, ImmutableList.of(message), null, -1);
}
/**
@@ -81,10 +118,7 @@ public class TurbineDiagnostic {
* @param args format args
*/
public static TurbineDiagnostic format(SourceFile source, ErrorKind kind, Object... args) {
- String path = firstNonNull(source.path(), "<>");
- String message = kind.format(args);
- String diagnostic = path + ": error: " + message.trim() + System.lineSeparator();
- return create(kind, diagnostic, ImmutableList.copyOf(args));
+ return create(Diagnostic.Kind.ERROR, kind, ImmutableList.copyOf(args), source, -1);
}
/**
@@ -95,26 +129,13 @@ public class TurbineDiagnostic {
* @param args format args
*/
public static TurbineDiagnostic format(
- SourceFile source, int position, ErrorKind kind, Object... args) {
- String path = firstNonNull(source.path(), "<>");
- LineMap lineMap = LineMap.create(source.source());
- int lineNumber = lineMap.lineNumber(position);
- int column = lineMap.column(position);
- String message = kind.format(args);
-
- StringBuilder sb = new StringBuilder(path).append(":");
- sb.append(lineNumber).append(": error: ");
- sb.append(message.trim()).append(System.lineSeparator());
- sb.append(CharMatcher.breakingWhitespace().trimTrailingFrom(lineMap.line(position)))
- .append(System.lineSeparator());
- sb.append(Strings.repeat(" ", column)).append('^');
- String diagnostic = sb.toString();
- return create(kind, diagnostic, ImmutableList.copyOf(args));
+ Diagnostic.Kind severity, SourceFile source, int position, ErrorKind kind, Object... args) {
+ return create(severity, kind, ImmutableList.copyOf(args), source, position);
}
@Override
public int hashCode() {
- return Objects.hash(diagnostic, kind);
+ return Objects.hash(kind, source, position);
}
@Override
@@ -123,6 +144,26 @@ public class TurbineDiagnostic {
return false;
}
TurbineDiagnostic that = (TurbineDiagnostic) obj;
- return diagnostic.equals(that.diagnostic) && kind.equals(that.kind);
+ return severity.equals(that.severity)
+ && kind.equals(that.kind)
+ && args.equals(that.args)
+ && Objects.equals(source, that.source)
+ && position == that.position;
+ }
+
+ public String path() {
+ return source != null && source.path() != null ? source.path() : "<>";
+ }
+
+ public int line() {
+ return position != -1 ? source.lineMap().lineNumber(position) : -1;
+ }
+
+ public int column() {
+ return position != -1 ? source.lineMap().column(position) + 1 : -1;
+ }
+
+ public String message() {
+ return kind.format(args.toArray());
}
}
diff --git a/java/com/google/turbine/diag/TurbineError.java b/java/com/google/turbine/diag/TurbineError.java
index b8b2a54..39244b5 100644
--- a/java/com/google/turbine/diag/TurbineError.java
+++ b/java/com/google/turbine/diag/TurbineError.java
@@ -19,6 +19,7 @@ package com.google.turbine.diag;
import static java.util.stream.Collectors.joining;
import com.google.common.collect.ImmutableList;
+import javax.tools.Diagnostic;
/** A compilation error. */
public class TurbineError extends Error {
@@ -31,6 +32,7 @@ public class TurbineError extends Error {
UNTERMINATED_STRING("unterminated string literal"),
UNTERMINATED_CHARACTER_LITERAL("unterminated char literal"),
UNTERMINATED_EXPRESSION("unterminated expression, expected ';' not found"),
+ INVALID_UNICODE("illegal unicode escape"),
EMPTY_CHARACTER_LITERAL("empty char literal"),
EXPECTED_TOKEN("expected token %s"),
INVALID_LITERAL("invalid literal: %s"),
@@ -42,11 +44,14 @@ public class TurbineError extends Error {
INVALID_ANNOTATION_ARGUMENT("invalid annotation argument"),
CANNOT_RESOLVE("could not resolve %s"),
EXPRESSION_ERROR("could not evaluate constant expression"),
+ OPERAND_TYPE("bad operand type %s"),
CYCLIC_HIERARCHY("cycle in class hierarchy: %s"),
NOT_AN_ANNOTATION("%s is not an annotation"),
NONREPEATABLE_ANNOTATION("%s is not @Repeatable"),
DUPLICATE_DECLARATION("duplicate declaration of %s"),
- BAD_MODULE_INFO("unexpected declaration found in module-info");
+ BAD_MODULE_INFO("unexpected declaration found in module-info"),
+ UNCLOSED_COMMENT("unclosed comment"),
+ PROC("%s");
private final String message;
@@ -80,16 +85,21 @@ public class TurbineError extends Error {
public static TurbineError format(
SourceFile source, int position, ErrorKind kind, Object... args) {
return new TurbineError(
- ImmutableList.of(TurbineDiagnostic.format(source, position, kind, args)));
+ ImmutableList.of(
+ TurbineDiagnostic.format(Diagnostic.Kind.ERROR, source, position, kind, args)));
}
private final ImmutableList<TurbineDiagnostic> diagnostics;
public TurbineError(ImmutableList<TurbineDiagnostic> diagnostics) {
- super(diagnostics.stream().map(d -> d.diagnostic()).collect(joining("\n")));
this.diagnostics = diagnostics;
}
+ @Override
+ public String getMessage() {
+ return diagnostics.stream().map(d -> d.diagnostic()).collect(joining(System.lineSeparator()));
+ }
+
public ImmutableList<TurbineDiagnostic> diagnostics() {
return diagnostics;
}
diff --git a/java/com/google/turbine/diag/TurbineLog.java b/java/com/google/turbine/diag/TurbineLog.java
index fd8fc38..b336e25 100644
--- a/java/com/google/turbine/diag/TurbineLog.java
+++ b/java/com/google/turbine/diag/TurbineLog.java
@@ -18,8 +18,10 @@ 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;
/** A log that collects diagnostics. */
public class TurbineLog {
@@ -31,11 +33,51 @@ public class TurbineLog {
}
public void maybeThrow() {
- if (!errors.isEmpty()) {
+ if (anyErrors()) {
throw new TurbineError(ImmutableList.copyOf(errors));
}
}
+ private boolean anyErrors() {
+ for (TurbineDiagnostic error : errors) {
+ if (error.severity().equals(Diagnostic.Kind.ERROR)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Returns true if a non-deferrable error was raised during annotation processing, i.e. an error
+ * reported by an annotation processor.
+ *
+ * <p>Errors reported by turbine (e.g. missing symbols) are non-fatal, since they may be fixed by
+ * code generated in later processing rounds.
+ */
+ public boolean errorRaised() {
+ for (TurbineDiagnostic error : errors) {
+ if (error.kind().equals(ErrorKind.PROC) && error.severity().equals(Diagnostic.Kind.ERROR)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /** 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();
+ }
+ }
+ }
+
+ /** 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));
+ }
+
/** A log for a specific source file. */
public class TurbineLogWithSource {
@@ -45,12 +87,12 @@ public class TurbineLog {
this.source = source;
}
- public void error(ErrorKind kind, Object... args) {
- errors.add(TurbineDiagnostic.format(source, kind, args));
+ public void diagnostic(Diagnostic.Kind severity, int position, ErrorKind kind, Object... args) {
+ errors.add(TurbineDiagnostic.format(severity, source, position, kind, args));
}
public void error(int position, ErrorKind kind, Object... args) {
- errors.add(TurbineDiagnostic.format(source, position, kind, args));
+ diagnostic(Diagnostic.Kind.ERROR, position, kind, args);
}
}
}
diff --git a/java/com/google/turbine/lower/Lower.java b/java/com/google/turbine/lower/Lower.java
index 16447ab..0f7bb90 100644
--- a/java/com/google/turbine/lower/Lower.java
+++ b/java/com/google/turbine/lower/Lower.java
@@ -21,10 +21,8 @@ import static com.google.turbine.binder.DisambiguateTypeAnnotations.groupRepeate
import com.google.common.base.Function;
import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableList.Builder;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
-import com.google.turbine.binder.bound.AnnotationValue;
import com.google.turbine.binder.bound.EnumConstantValue;
import com.google.turbine.binder.bound.ModuleInfo.ExportInfo;
import com.google.turbine.binder.bound.ModuleInfo.OpenInfo;
@@ -33,6 +31,7 @@ import com.google.turbine.binder.bound.ModuleInfo.RequireInfo;
import com.google.turbine.binder.bound.ModuleInfo.UseInfo;
import com.google.turbine.binder.bound.SourceModuleInfo;
import com.google.turbine.binder.bound.SourceTypeBoundClass;
+import com.google.turbine.binder.bound.TurbineAnnotationValue;
import com.google.turbine.binder.bound.TurbineClassValue;
import com.google.turbine.binder.bound.TypeBoundClass;
import com.google.turbine.binder.bound.TypeBoundClass.FieldInfo;
@@ -264,10 +263,10 @@ public class Lower {
ImmutableList<AnnotationInfo> annotations = lowerAnnotations(info.annotations());
- ImmutableList<ClassFile.InnerClass> inners = collectInnerClasses(info.source(), sym, info);
-
ImmutableList<TypeAnnotationInfo> typeAnnotations = classTypeAnnotations(info);
+ ImmutableList<ClassFile.InnerClass> inners = collectInnerClasses(info.source(), sym, info);
+
ClassFile classfile =
new ClassFile(
access,
@@ -525,9 +524,8 @@ public class Lower {
return true;
case SOURCE:
return null;
- default:
- throw new AssertionError(retention);
}
+ throw new AssertionError(retention);
}
private ImmutableMap<String, ElementValue> annotationValues(ImmutableMap<String, Const> values) {
@@ -563,12 +561,12 @@ public class Lower {
}
case ANNOTATION:
{
- AnnotationValue annotationValue = (AnnotationValue) value;
+ TurbineAnnotationValue annotationValue = (TurbineAnnotationValue) value;
Boolean visible = isVisible(annotationValue.sym());
if (visible == null) {
visible = true;
}
- return new ElementValue.AnnotationValue(
+ return new ElementValue.ConstTurbineAnnotationValue(
new AnnotationInfo(
sig.objectType(annotationValue.sym()),
visible,
@@ -576,9 +574,8 @@ public class Lower {
}
case PRIMITIVE:
return new ElementValue.ConstValue((Const.Value) value);
- default:
- throw new AssertionError(value.kind());
}
+ throw new AssertionError(value.kind());
}
/** Lower type annotations in a class declaration's signature. */
@@ -656,7 +653,7 @@ public class Lower {
* or on bounds.
*/
private void typeParameterAnnotations(
- Builder<TypeAnnotationInfo> result,
+ ImmutableList.Builder<TypeAnnotationInfo> result,
Iterable<TyVarInfo> typeParameters,
TargetType targetType,
TargetType boundTargetType) {
@@ -675,7 +672,7 @@ public class Lower {
info));
}
int boundIndex = 0;
- for (Type i : p.bound().bounds()) {
+ for (Type i : p.upperBound().bounds()) {
if (boundIndex == 0 && isInterface(i, env)) {
// super class bound index is always 0; interface bounds start at 1
boundIndex++;
@@ -696,7 +693,10 @@ public class Lower {
}
private void lowerTypeAnnotations(
- Builder<TypeAnnotationInfo> result, Type type, TargetType targetType, Target target) {
+ ImmutableList.Builder<TypeAnnotationInfo> result,
+ Type type,
+ TargetType targetType,
+ Target target) {
new LowerTypeAnnotations(result, targetType, target)
.lowerTypeAnnotations(type, TypePath.root());
}
@@ -707,7 +707,7 @@ public class Lower {
private final Target target;
public LowerTypeAnnotations(
- Builder<TypeAnnotationInfo> result, TargetType targetType, Target target) {
+ ImmutableList.Builder<TypeAnnotationInfo> result, TargetType targetType, Target target) {
this.result = result;
this.targetType = targetType;
this.target = target;
@@ -764,8 +764,6 @@ public class Lower {
lowerTypeAnnotations(type.annotations(), path);
lowerTypeAnnotations(type.bound(), path.wild());
break;
- default:
- throw new AssertionError(type.boundKind());
}
}
diff --git a/java/com/google/turbine/lower/LowerSignature.java b/java/com/google/turbine/lower/LowerSignature.java
index fe9b912..13a7b9f 100644
--- a/java/com/google/turbine/lower/LowerSignature.java
+++ b/java/com/google/turbine/lower/LowerSignature.java
@@ -92,17 +92,8 @@ public class LowerSignature {
while (curr.targs().isEmpty() && it.hasNext()) {
curr = it.next();
}
- String pkg;
- String name;
- int idx = curr.sym().binaryName().lastIndexOf('/');
- if (idx == -1) {
- pkg = "";
- name = curr.sym().binaryName();
- } else {
- pkg = curr.sym().binaryName().substring(0, idx);
- name = curr.sym().binaryName().substring(idx + 1);
- }
- classes.add(new Sig.SimpleClassTySig(name, tyArgSigs(curr)));
+ String pkg = curr.sym().packageName();
+ classes.add(new Sig.SimpleClassTySig(curr.sym().simpleName(), tyArgSigs(curr)));
while (it.hasNext()) {
SimpleClassTy outer = curr;
curr = it.next();
@@ -128,9 +119,8 @@ public class LowerSignature {
return new UpperBoundTySig(signature(((Type.WildUpperBoundedTy) ty).bound()));
case LOWER:
return new LowerBoundTySig(signature(((Type.WildLowerBoundedTy) ty).bound()));
- default:
- throw new AssertionError(ty.boundKind());
}
+ throw new AssertionError(ty.boundKind());
}
/**
@@ -284,13 +274,13 @@ public class LowerSignature {
String identifier = sym.name();
Sig.TySig cbound = null;
ImmutableList.Builder<Sig.TySig> ibounds = ImmutableList.builder();
- if (info.bound().bounds().isEmpty()) {
+ if (info.upperBound().bounds().isEmpty()) {
cbound =
new ClassTySig(
"java/lang", ImmutableList.of(new SimpleClassTySig("Object", ImmutableList.of())));
} else {
boolean first = true;
- for (Type bound : info.bound().bounds()) {
+ for (Type bound : info.upperBound().bounds()) {
TySig sig = signature(bound);
if (first) {
if (!isInterface(bound, env)) {
diff --git a/java/com/google/turbine/main/Main.java b/java/com/google/turbine/main/Main.java
index 34421e1..1e60ae6 100644
--- a/java/com/google/turbine/main/Main.java
+++ b/java/com/google/turbine/main/Main.java
@@ -19,15 +19,21 @@ package com.google.turbine.main;
import static com.google.common.base.StandardSystemProperty.JAVA_SPECIFICATION_VERSION;
import static java.nio.charset.StandardCharsets.UTF_8;
+import com.google.auto.value.AutoValue;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
import com.google.common.hash.Hashing;
import com.google.common.io.MoreFiles;
import com.google.turbine.binder.Binder;
import com.google.turbine.binder.Binder.BindingResult;
+import com.google.turbine.binder.Binder.Statistics;
import com.google.turbine.binder.ClassPath;
import com.google.turbine.binder.ClassPathBinder;
import com.google.turbine.binder.CtSymClassBinder;
import com.google.turbine.binder.JimageClassBinder;
+import com.google.turbine.binder.Processing;
+import com.google.turbine.binder.bound.SourceTypeBoundClass;
+import com.google.turbine.binder.sym.ClassSymbol;
import com.google.turbine.deps.Dependencies;
import com.google.turbine.deps.Transitive;
import com.google.turbine.diag.SourceFile;
@@ -35,9 +41,12 @@ import com.google.turbine.diag.TurbineError;
import com.google.turbine.lower.Lower;
import com.google.turbine.lower.Lower.Lowered;
import com.google.turbine.options.TurbineOptions;
+import com.google.turbine.options.TurbineOptions.ReducedClasspathMode;
import com.google.turbine.options.TurbineOptionsParser;
import com.google.turbine.parse.Parser;
import com.google.turbine.proto.DepsProto;
+import com.google.turbine.proto.ManifestProto;
+import com.google.turbine.proto.ManifestProto.CompilationUnit;
import com.google.turbine.tree.Tree.CompUnit;
import com.google.turbine.zip.Zip;
import java.io.BufferedOutputStream;
@@ -75,7 +84,8 @@ public class Main {
public static void main(String[] args) throws IOException {
boolean ok;
try {
- ok = compile(args);
+ compile(args);
+ ok = true;
} catch (TurbineError | UsageException e) {
System.err.println(e.getMessage());
ok = false;
@@ -86,56 +96,182 @@ public class Main {
System.exit(ok ? 0 : 1);
}
- public static boolean compile(String[] args) throws IOException {
- TurbineOptions options = TurbineOptionsParser.parse(Arrays.asList(args));
- return compile(options);
+ /** The result of a turbine invocation. */
+ @AutoValue
+ public abstract static class Result {
+ /** Returns {@code true} if transitive classpath fallback occurred. */
+ public abstract boolean transitiveClasspathFallback();
+
+ /** The length of the transitive classpath. */
+ public abstract int transitiveClasspathLength();
+
+ /**
+ * The length of the reduced classpath, or {@link #transitiveClasspathLength} if classpath
+ * reduction is not supported.
+ */
+ public abstract int reducedClasspathLength();
+
+ public abstract Statistics processorStatistics();
+
+ static Result create(
+ boolean transitiveClasspathFallback,
+ int transitiveClasspathLength,
+ int reducedClasspathLength,
+ Statistics processorStatistics) {
+ return new AutoValue_Main_Result(
+ transitiveClasspathFallback,
+ transitiveClasspathLength,
+ reducedClasspathLength,
+ processorStatistics);
+ }
+ }
+
+ public static void compile(String[] args) throws IOException {
+ compile(TurbineOptionsParser.parse(Arrays.asList(args)));
}
- public static boolean compile(TurbineOptions options) throws IOException {
+ public static Result compile(TurbineOptions options) throws IOException {
usage(options);
ImmutableList<CompUnit> units = parseAll(options);
ClassPath bootclasspath = bootclasspath(options);
- Collection<String> reducedClasspath =
- Dependencies.reduceClasspath(
- options.classPath(), options.directJars(), options.depsArtifacts());
- ClassPath classpath = ClassPathBinder.bindClasspath(toPaths(reducedClasspath));
+ BindingResult bound;
+ ReducedClasspathMode reducedClasspathMode = options.reducedClasspathMode();
+ if (reducedClasspathMode == ReducedClasspathMode.JAVABUILDER_REDUCED
+ && options.directJars().isEmpty()) {
+ // the compilation doesn't support reduced classpaths
+ // TODO(cushon): make this a usage error, see TODO in Dependencies.reduceClasspath
+ reducedClasspathMode = ReducedClasspathMode.NONE;
+ }
+ boolean transitiveClasspathFallback = false;
+ ImmutableList<String> classPath = options.classPath();
+ int transitiveClasspathLength = classPath.size();
+ int reducedClasspathLength = classPath.size();
+ switch (reducedClasspathMode) {
+ case NONE:
+ bound = bind(options, units, bootclasspath, classPath);
+ break;
+ case BAZEL_FALLBACK:
+ reducedClasspathLength = options.reducedClasspathLength();
+ bound = bind(options, units, bootclasspath, classPath);
+ transitiveClasspathFallback = true;
+ break;
+ case JAVABUILDER_REDUCED:
+ Collection<String> reducedClasspath =
+ Dependencies.reduceClasspath(classPath, options.directJars(), options.depsArtifacts());
+ reducedClasspathLength = reducedClasspath.size();
+ try {
+ bound = bind(options, units, bootclasspath, reducedClasspath);
+ } catch (TurbineError e) {
+ bound = fallback(options, units, bootclasspath, classPath);
+ transitiveClasspathFallback = true;
+ }
+ break;
+ case BAZEL_REDUCED:
+ transitiveClasspathLength = options.fullClasspathLength();
+ try {
+ bound = bind(options, units, bootclasspath, classPath);
+ } catch (TurbineError e) {
+ writeJdepsForFallback(options);
+ return Result.create(
+ /* transitiveClasspathFallback= */ true,
+ /* transitiveClasspathLength= */ transitiveClasspathLength,
+ /* reducedClasspathLength= */ reducedClasspathLength,
+ Statistics.empty());
+ }
+ break;
+ default:
+ throw new AssertionError(reducedClasspathMode);
+ }
- BindingResult bound =
- Binder.bind(units, classpath, bootclasspath, /* moduleVersion=*/ Optional.empty());
+ if (options.outputDeps().isPresent()
+ || options.output().isPresent()
+ || options.outputManifest().isPresent()) {
+ // TODO(cushon): parallelize
+ Lowered lowered = Lower.lowerAll(bound.units(), bound.modules(), bound.classPathEnv());
- // TODO(cushon): parallelize
- Lowered lowered = Lower.lowerAll(bound.units(), bound.modules(), bound.classPathEnv());
+ if (options.outputDeps().isPresent()) {
+ DepsProto.Dependencies deps =
+ Dependencies.collectDeps(options.targetLabel(), bootclasspath, bound, lowered);
+ try (OutputStream os =
+ new BufferedOutputStream(
+ Files.newOutputStream(Paths.get(options.outputDeps().get())))) {
+ deps.writeTo(os);
+ }
+ }
+ if (options.output().isPresent()) {
+ Map<String, byte[]> transitive = Transitive.collectDeps(bootclasspath, bound);
+ writeOutput(options, bound.generatedClasses(), lowered.bytes(), transitive);
+ }
+ if (options.outputManifest().isPresent()) {
+ writeManifestProto(options, bound.units(), bound.generatedSources());
+ }
+ }
- Map<String, byte[]> transitive = Transitive.collectDeps(bootclasspath, bound);
+ writeSources(options, bound.generatedSources());
+ writeResources(options, bound.generatedClasses());
+ return Result.create(
+ /* transitiveClasspathFallback= */ transitiveClasspathFallback,
+ /* transitiveClasspathLength= */ transitiveClasspathLength,
+ /* reducedClasspathLength= */ reducedClasspathLength,
+ bound.statistics());
+ }
- if (options.outputDeps().isPresent()) {
- DepsProto.Dependencies deps =
- Dependencies.collectDeps(options.targetLabel(), bootclasspath, bound, lowered);
- try (OutputStream os =
- new BufferedOutputStream(Files.newOutputStream(Paths.get(options.outputDeps().get())))) {
- deps.writeTo(os);
- }
+ // don't inline this; we want it to show up in profiles
+ private static BindingResult fallback(
+ TurbineOptions options,
+ ImmutableList<CompUnit> units,
+ ClassPath bootclasspath,
+ ImmutableList<String> classPath)
+ throws IOException {
+ return bind(options, units, bootclasspath, classPath);
+ }
+
+ /**
+ * Writes a jdeps proto that indiciates to Blaze that the transitive classpath compilation failed,
+ * and it should fall back to the transitive classpath. Used only when {@link
+ * ReducedClasspathMode#BAZEL_REDUCED}.
+ */
+ public static void writeJdepsForFallback(TurbineOptions options) throws IOException {
+ try (OutputStream os =
+ new BufferedOutputStream(Files.newOutputStream(Paths.get(options.outputDeps().get())))) {
+ DepsProto.Dependencies.newBuilder()
+ .setRuleLabel(options.targetLabel().get())
+ .setRequiresReducedClasspathFallback(true)
+ .build()
+ .writeTo(os);
}
+ }
- writeOutput(options, lowered.bytes(), transitive);
- return true;
+ private static BindingResult bind(
+ TurbineOptions options,
+ ImmutableList<CompUnit> units,
+ ClassPath bootclasspath,
+ Collection<String> classpath)
+ throws IOException {
+ return Binder.bind(
+ units,
+ ClassPathBinder.bindClasspath(toPaths(classpath)),
+ Processing.initializeProcessors(
+ /* javacopts= */ options.javacOpts(),
+ /* processorPath= */ options.processorPath(),
+ /* processorNames= */ options.processors(),
+ /* builtinProcessors= */ options.builtinProcessors()),
+ bootclasspath,
+ /* moduleVersion=*/ Optional.empty());
}
private static void usage(TurbineOptions options) {
- if (!options.processors().isEmpty()) {
- throw new UsageException("--processors is not supported");
- }
- if (options.sources().isEmpty() && options.sourceJars().isEmpty()) {
- throw new UsageException("no sources were provided");
- }
if (options.help()) {
throw new UsageException();
}
- if (!options.output().isPresent()) {
- throw new UsageException("--output is required");
+ if (!options.output().isPresent()
+ && !options.gensrcOutput().isPresent()
+ && !options.resourceOutput().isPresent()) {
+ throw new UsageException(
+ "at least one of --output, --gensrc_output, or --resource_output is required");
}
}
@@ -188,9 +324,46 @@ public class Main {
return units.build();
}
- /** Write bytecode to the output jar. */
+ /** Writes source files generated by annotation processors. */
+ private static void writeSources(
+ TurbineOptions options, ImmutableMap<String, SourceFile> generatedSources)
+ throws IOException {
+ if (!options.gensrcOutput().isPresent()) {
+ return;
+ }
+ Path path = Paths.get(options.gensrcOutput().get());
+ try (OutputStream os = Files.newOutputStream(path);
+ BufferedOutputStream bos = new BufferedOutputStream(os, BUFFER_SIZE);
+ JarOutputStream jos = new JarOutputStream(bos)) {
+ for (SourceFile source : generatedSources.values()) {
+ addEntry(jos, source.path(), source.source().getBytes(UTF_8));
+ }
+ writeManifest(jos, manifest());
+ }
+ }
+
+ /** Writes resource files generated by annotation processors. */
+ private static void writeResources(
+ TurbineOptions options, ImmutableMap<String, byte[]> generatedResources) throws IOException {
+ if (!options.resourceOutput().isPresent()) {
+ return;
+ }
+ Path path = Paths.get(options.resourceOutput().get());
+ try (OutputStream os = Files.newOutputStream(path);
+ BufferedOutputStream bos = new BufferedOutputStream(os, BUFFER_SIZE);
+ JarOutputStream jos = new JarOutputStream(bos)) {
+ for (Map.Entry<String, byte[]> resource : generatedResources.entrySet()) {
+ addEntry(jos, resource.getKey(), resource.getValue());
+ }
+ }
+ }
+
+ /** Writes bytecode to the output jar. */
private static void writeOutput(
- TurbineOptions options, Map<String, byte[]> lowered, Map<String, byte[]> transitive)
+ TurbineOptions options,
+ Map<String, byte[]> generated,
+ Map<String, byte[]> lowered,
+ Map<String, byte[]> transitive)
throws IOException {
Path path = Paths.get(options.output().get());
try (OutputStream os = Files.newOutputStream(path);
@@ -199,17 +372,42 @@ public class Main {
for (Map.Entry<String, byte[]> entry : lowered.entrySet()) {
addEntry(jos, entry.getKey() + ".class", entry.getValue());
}
+ for (Map.Entry<String, byte[]> entry : generated.entrySet()) {
+ addEntry(jos, entry.getKey(), entry.getValue());
+ }
for (Map.Entry<String, byte[]> entry : transitive.entrySet()) {
addEntry(
jos, ClassPathBinder.TRANSITIVE_PREFIX + entry.getKey() + ".class", entry.getValue());
}
if (options.targetLabel().isPresent()) {
- addEntry(jos, MANIFEST_DIR, new byte[] {});
- addEntry(jos, MANIFEST_NAME, manifestContent(options));
+ writeManifest(jos, manifest(options));
}
}
}
+ private static void writeManifestProto(
+ TurbineOptions options,
+ ImmutableMap<ClassSymbol, SourceTypeBoundClass> units,
+ ImmutableMap<String, SourceFile> generatedSources)
+ throws IOException {
+ ManifestProto.Manifest.Builder manifest = ManifestProto.Manifest.newBuilder();
+ for (Map.Entry<ClassSymbol, SourceTypeBoundClass> e : units.entrySet()) {
+ manifest.addCompilationUnit(
+ CompilationUnit.newBuilder()
+ .setPath(e.getValue().source().path())
+ .setPkg(e.getKey().packageName())
+ .addTopLevel(e.getKey().simpleName())
+ .setGeneratedByAnnotationProcessor(
+ generatedSources.containsKey(e.getValue().source().path()))
+ .build());
+ }
+ try (OutputStream os =
+ new BufferedOutputStream(
+ Files.newOutputStream(Paths.get(options.outputManifest().get())))) {
+ manifest.build().writeTo(os);
+ }
+ }
+
/** Normalize timestamps. */
static final long DEFAULT_TIMESTAMP =
LocalDateTime.of(2010, 1, 1, 0, 0, 0)
@@ -228,7 +426,15 @@ public class Main {
jos.write(bytes);
}
- private static byte[] manifestContent(TurbineOptions turbineOptions) throws IOException {
+ private static void writeManifest(JarOutputStream jos, Manifest manifest) throws IOException {
+ addEntry(jos, MANIFEST_DIR, new byte[] {});
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ manifest.write(out);
+ addEntry(jos, MANIFEST_NAME, out.toByteArray());
+ }
+
+ /** Creates a default {@link Manifest}. */
+ private static Manifest manifest() {
Manifest manifest = new Manifest();
Attributes attributes = manifest.getMainAttributes();
attributes.put(Attributes.Name.MANIFEST_VERSION, "1.0");
@@ -236,15 +442,20 @@ public class Main {
if (attributes.getValue(createdBy) == null) {
attributes.put(createdBy, "bazel");
}
+ return manifest;
+ }
+
+ /** Creates a {@link Manifest} that includes the target label and injecting rule kind. */
+ private static Manifest manifest(TurbineOptions turbineOptions) {
+ Manifest manifest = manifest();
+ Attributes attributes = manifest.getMainAttributes();
if (turbineOptions.targetLabel().isPresent()) {
attributes.put(TARGET_LABEL, turbineOptions.targetLabel().get());
}
if (turbineOptions.injectingRuleKind().isPresent()) {
attributes.put(INJECTING_RULE_KIND, turbineOptions.injectingRuleKind().get());
}
- ByteArrayOutputStream out = new ByteArrayOutputStream();
- manifest.write(out);
- return out.toByteArray();
+ return manifest;
}
private static ImmutableList<Path> toPaths(Iterable<String> paths) {
diff --git a/java/com/google/turbine/model/Const.java b/java/com/google/turbine/model/Const.java
index 6e41bd2..ed4b072 100644
--- a/java/com/google/turbine/model/Const.java
+++ b/java/com/google/turbine/model/Const.java
@@ -18,6 +18,9 @@ package com.google.turbine.model;
import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableList;
+import com.google.common.escape.SourceCodeEscapers;
+import javax.lang.model.element.AnnotationValue;
+import javax.lang.model.element.AnnotationValueVisitor;
/**
* Compile-time constant expressions, including literals of primitive or String type, class
@@ -31,6 +34,9 @@ public abstract class Const {
@Override
public abstract boolean equals(Object obj);
+ @Override
+ public abstract String toString();
+
/** The constant kind. */
public abstract Kind kind();
@@ -51,7 +57,7 @@ public abstract class Const {
}
/** Subtypes of {@link Const} for primitive and String literals. */
- public abstract static class Value extends Const {
+ public abstract static class Value extends Const implements AnnotationValue {
public abstract TurbineConstantTypeKind constantTypeKind();
@Override
@@ -110,6 +116,11 @@ public abstract class Const {
}
@Override
+ public <R, P> R accept(AnnotationValueVisitor<R, P> v, P p) {
+ return v.visitBoolean(value, p);
+ }
+
+ @Override
public TurbineConstantTypeKind constantTypeKind() {
return TurbineConstantTypeKind.BOOLEAN;
}
@@ -119,6 +130,11 @@ public abstract class Const {
}
@Override
+ public Object getValue() {
+ return value;
+ }
+
+ @Override
public BooleanValue asBoolean() {
return this;
}
@@ -154,6 +170,11 @@ public abstract class Const {
}
@Override
+ public <R, P> R accept(AnnotationValueVisitor<R, P> v, P p) {
+ return v.visitInt(value, p);
+ }
+
+ @Override
public TurbineConstantTypeKind constantTypeKind() {
return TurbineConstantTypeKind.INT;
}
@@ -163,6 +184,11 @@ public abstract class Const {
}
@Override
+ public Object getValue() {
+ return value;
+ }
+
+ @Override
public IntValue asInteger() {
return this;
}
@@ -227,6 +253,11 @@ public abstract class Const {
}
@Override
+ public <R, P> R accept(AnnotationValueVisitor<R, P> v, P p) {
+ return v.visitLong(value, p);
+ }
+
+ @Override
public TurbineConstantTypeKind constantTypeKind() {
return TurbineConstantTypeKind.LONG;
}
@@ -236,6 +267,11 @@ public abstract class Const {
}
@Override
+ public Object getValue() {
+ return value;
+ }
+
+ @Override
public IntValue asInteger() {
return new IntValue((int) value);
}
@@ -296,7 +332,12 @@ public abstract class Const {
@Override
public String toString() {
- return "'" + value + "'";
+ return "'" + SourceCodeEscapers.javaCharEscaper().escape(String.valueOf(value)) + "'";
+ }
+
+ @Override
+ public <R, P> R accept(AnnotationValueVisitor<R, P> v, P p) {
+ return v.visitChar(value, p);
}
@Override
@@ -309,6 +350,11 @@ public abstract class Const {
}
@Override
+ public Object getValue() {
+ return value;
+ }
+
+ @Override
public IntValue asInteger() {
return new IntValue((int) value);
}
@@ -369,10 +415,18 @@ public abstract class Const {
@Override
public String toString() {
+ if (Float.isNaN(value)) {
+ return "0.0f/0.0f";
+ }
return value + "f";
}
@Override
+ public <R, P> R accept(AnnotationValueVisitor<R, P> v, P p) {
+ return v.visitFloat(value, p);
+ }
+
+ @Override
public TurbineConstantTypeKind constantTypeKind() {
return TurbineConstantTypeKind.FLOAT;
}
@@ -382,6 +436,11 @@ public abstract class Const {
}
@Override
+ public Object getValue() {
+ return value;
+ }
+
+ @Override
public IntValue asInteger() {
return new IntValue((int) value);
}
@@ -442,10 +501,24 @@ public abstract class Const {
@Override
public String toString() {
+ if (Double.isNaN(value)) {
+ return "0.0/0.0";
+ }
+ if (value == Double.POSITIVE_INFINITY) {
+ return "1.0/0.0";
+ }
+ if (value == Double.NEGATIVE_INFINITY) {
+ return "-1.0/0.0";
+ }
return String.valueOf(value);
}
@Override
+ public <R, P> R accept(AnnotationValueVisitor<R, P> v, P p) {
+ return v.visitDouble(value, p);
+ }
+
+ @Override
public TurbineConstantTypeKind constantTypeKind() {
return TurbineConstantTypeKind.DOUBLE;
}
@@ -455,6 +528,11 @@ public abstract class Const {
}
@Override
+ public Object getValue() {
+ return value;
+ }
+
+ @Override
public IntValue asInteger() {
return new IntValue((int) value);
}
@@ -515,7 +593,12 @@ public abstract class Const {
@Override
public String toString() {
- return String.format("\"%s\"", value);
+ return '"' + SourceCodeEscapers.javaCharEscaper().escape(value) + '"';
+ }
+
+ @Override
+ public <R, P> R accept(AnnotationValueVisitor<R, P> v, P p) {
+ return v.visitString(value, p);
}
@Override
@@ -528,6 +611,11 @@ public abstract class Const {
}
@Override
+ public Object getValue() {
+ return value;
+ }
+
+ @Override
public StringValue asString() {
return this;
}
@@ -557,6 +645,11 @@ public abstract class Const {
}
@Override
+ public <R, P> R accept(AnnotationValueVisitor<R, P> v, P p) {
+ return v.visitShort(value, p);
+ }
+
+ @Override
public TurbineConstantTypeKind constantTypeKind() {
return TurbineConstantTypeKind.SHORT;
}
@@ -566,6 +659,11 @@ public abstract class Const {
}
@Override
+ public Object getValue() {
+ return value;
+ }
+
+ @Override
public IntValue asInteger() {
return new IntValue((int) value);
}
@@ -635,6 +733,11 @@ public abstract class Const {
}
@Override
+ public Object getValue() {
+ return value;
+ }
+
+ @Override
public IntValue asInteger() {
return new IntValue((int) value);
}
@@ -686,7 +789,12 @@ public abstract class Const {
@Override
public String toString() {
- return String.valueOf(value);
+ return String.format("(byte)0x%02x", value);
+ }
+
+ @Override
+ public <R, P> R accept(AnnotationValueVisitor<R, P> v, P p) {
+ return v.visitByte(value, p);
}
}
diff --git a/java/com/google/turbine/options/TurbineOptions.java b/java/com/google/turbine/options/TurbineOptions.java
index 0ce0c13..4dcc408 100644
--- a/java/com/google/turbine/options/TurbineOptions.java
+++ b/java/com/google/turbine/options/TurbineOptions.java
@@ -16,104 +16,59 @@
package com.google.turbine.options;
-import static com.google.common.base.Preconditions.checkNotNull;
-
+import com.google.auto.value.AutoValue;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import java.util.Optional;
import org.checkerframework.checker.nullness.qual.Nullable;
/** Header compilation options. */
-public class TurbineOptions {
-
- private final Optional<String> output;
- private final ImmutableList<String> classPath;
- private final ImmutableSet<String> bootClassPath;
- private final Optional<String> release;
- private final Optional<String> system;
- private final ImmutableList<String> sources;
- private final ImmutableList<String> processorPath;
- private final ImmutableSet<String> processors;
- private final ImmutableList<String> sourceJars;
- private final Optional<String> outputDeps;
- private final ImmutableSet<String> directJars;
- private final Optional<String> targetLabel;
- private final Optional<String> injectingRuleKind;
- private final ImmutableList<String> depsArtifacts;
- private final boolean javacFallback;
- private final boolean help;
- private final ImmutableList<String> javacOpts;
- private final boolean shouldReduceClassPath;
-
- private TurbineOptions(
- @Nullable String output,
- ImmutableList<String> classPath,
- ImmutableSet<String> bootClassPath,
- @Nullable String release,
- @Nullable String system,
- ImmutableList<String> sources,
- ImmutableList<String> processorPath,
- ImmutableSet<String> processors,
- ImmutableList<String> sourceJars,
- @Nullable String outputDeps,
- ImmutableSet<String> directJars,
- @Nullable String targetLabel,
- @Nullable String injectingRuleKind,
- ImmutableList<String> depsArtifacts,
- boolean javacFallback,
- boolean help,
- ImmutableList<String> javacOpts,
- boolean shouldReduceClassPath) {
- this.output = Optional.ofNullable(output);
- this.classPath = checkNotNull(classPath, "classPath must not be null");
- this.bootClassPath = checkNotNull(bootClassPath, "bootClassPath must not be null");
- this.release = Optional.ofNullable(release);
- this.system = Optional.ofNullable(system);
- this.sources = checkNotNull(sources, "sources must not be null");
- this.processorPath = checkNotNull(processorPath, "processorPath must not be null");
- this.processors = checkNotNull(processors, "processors must not be null");
- this.sourceJars = checkNotNull(sourceJars, "sourceJars must not be null");
- this.outputDeps = Optional.ofNullable(outputDeps);
- this.directJars = checkNotNull(directJars, "directJars must not be null");
- this.targetLabel = Optional.ofNullable(targetLabel);
- this.injectingRuleKind = Optional.ofNullable(injectingRuleKind);
- this.depsArtifacts = checkNotNull(depsArtifacts, "depsArtifacts must not be null");
- this.javacFallback = javacFallback;
- this.help = help;
- this.javacOpts = checkNotNull(javacOpts, "javacOpts must not be null");
- this.shouldReduceClassPath = shouldReduceClassPath;
+@AutoValue
+public abstract class TurbineOptions {
+
+ /**
+ * This modes controls how a probablistic Java classpath reduction is used. For each mode except
+ * {@code NONE} a speculative compilation is performed against a subset of the original classpath.
+ * If it fails due to a missing symbol, it is retried with the original transitive classpath.
+ */
+ public enum ReducedClasspathMode {
+ /**
+ * Bazel performs classpath reduction, and invokes turbine passing only the reduced classpath.
+ * If the compilation fails and requires fallback, turbine finishes with exit code 0 but records
+ * that the reduced classpath compilation failed in the jdeps proto.
+ */
+ BAZEL_REDUCED,
+ /**
+ * Indicates that the reduced classpath compilation failed when Bazel previously invoked
+ * turbine, and that we are retrying with a transitive classpath.
+ */
+ BAZEL_FALLBACK,
+ /**
+ * Turbine implements reduced classpaths locally, with in-process fallback if the compilation
+ * fails.
+ */
+ JAVABUILDER_REDUCED,
+ /** Reduced classpaths are disabled, and a full transitive classpath is used. */
+ NONE
}
/** Paths to the Java source files to compile. */
- public ImmutableList<String> sources() {
- return sources;
- }
+ public abstract ImmutableList<String> sources();
/** Paths to classpath artifacts. */
- public ImmutableList<String> classPath() {
- return classPath;
- }
+ public abstract ImmutableList<String> classPath();
/** Paths to compilation bootclasspath artifacts. */
- public ImmutableSet<String> bootClassPath() {
- return bootClassPath;
- }
+ public abstract ImmutableSet<String> bootClassPath();
/** The target platform version. */
- public Optional<String> release() {
- return release;
- }
+ public abstract Optional<String> release();
/** The target platform's system modules. */
- public Optional<String> system() {
- return system;
- }
+ public abstract Optional<String> system();
/** The output jar. */
- @Nullable
- public Optional<String> output() {
- return output;
- }
+ public abstract Optional<String> output();
/**
* The output jar.
@@ -123,214 +78,187 @@ public class TurbineOptions {
@Deprecated
@Nullable
public String outputFile() {
- return output.orElse(null);
+ return output().orElse(null);
}
/** Paths to annotation processor artifacts. */
- public ImmutableList<String> processorPath() {
- return processorPath;
- }
+ public abstract ImmutableList<String> processorPath();
/** Annotation processor class names. */
- public ImmutableSet<String> processors() {
- return processors;
- }
+ public abstract ImmutableSet<String> processors();
+
+ /** Class names of annotation processor that are built in. */
+ public abstract ImmutableSet<String> builtinProcessors();
/** Source jars for compilation. */
- public ImmutableList<String> sourceJars() {
- return sourceJars;
- }
+ public abstract ImmutableList<String> sourceJars();
/** Output jdeps file. */
- public Optional<String> outputDeps() {
- return outputDeps;
- }
+ public abstract Optional<String> outputDeps();
+
+ /** Output manifest file. */
+ public abstract Optional<String> outputManifest();
/** The direct dependencies. */
- public ImmutableSet<String> directJars() {
- return directJars;
- }
+ public abstract ImmutableSet<String> directJars();
/** The label of the target being compiled. */
- public Optional<String> targetLabel() {
- return targetLabel;
- }
+ public abstract Optional<String> targetLabel();
/**
* If present, the name of the rule that injected an aspect that compiles this target.
*
* <p>Note that this rule will have a completely different label to {@link #targetLabel} above.
*/
- public Optional<String> injectingRuleKind() {
- return injectingRuleKind;
- }
+ public abstract Optional<String> injectingRuleKind();
/** The .jdeps artifacts for direct dependencies. */
- public ImmutableList<String> depsArtifacts() {
- return depsArtifacts;
- }
-
- /** Fall back to javac-turbine for error reporting. */
- public boolean javacFallback() {
- return javacFallback;
- }
+ public abstract ImmutableList<String> depsArtifacts();
/** Print usage information. */
- public boolean help() {
- return help;
- }
+ public abstract boolean help();
/** Additional Java compiler flags. */
- public ImmutableList<String> javacOpts() {
- return javacOpts;
- }
+ public abstract ImmutableList<String> javacOpts();
- /** Returns true if the reduced classpath optimization is enabled. */
- public boolean shouldReduceClassPath() {
- return shouldReduceClassPath;
- }
+ /** The reduced classpath optimization mode. */
+ public abstract ReducedClasspathMode reducedClasspathMode();
+
+ /** An optional path for profiling output. */
+ public abstract Optional<String> profile();
+
+ /** An optional path for generated source output. */
+ public abstract Optional<String> gensrcOutput();
+
+ /** An optional path for generated resource output. */
+ public abstract Optional<String> resourceOutput();
+
+ public abstract int fullClasspathLength();
+
+ public abstract int reducedClasspathLength();
public static Builder builder() {
- return new Builder();
+ return new AutoValue_TurbineOptions.Builder()
+ .setSources(ImmutableList.of())
+ .setClassPath(ImmutableList.of())
+ .setBootClassPath(ImmutableList.of())
+ .setProcessorPath(ImmutableList.of())
+ .setProcessors(ImmutableList.of())
+ .setBuiltinProcessors(ImmutableList.of())
+ .setSourceJars(ImmutableList.of())
+ .setDirectJars(ImmutableList.of())
+ .setDepsArtifacts(ImmutableList.of())
+ .addAllJavacOpts(ImmutableList.of())
+ .setReducedClasspathMode(ReducedClasspathMode.NONE)
+ .setHelp(false)
+ .setFullClasspathLength(0)
+ .setReducedClasspathLength(0);
}
/** A {@link Builder} for {@link TurbineOptions}. */
- public static class Builder {
-
- private String output;
- private final ImmutableList.Builder<String> classPath = ImmutableList.builder();
- private final ImmutableList.Builder<String> sources = ImmutableList.builder();
- private final ImmutableList.Builder<String> processorPath = ImmutableList.builder();
- private final ImmutableSet.Builder<String> processors = ImmutableSet.builder();
- private final ImmutableList.Builder<String> sourceJars = ImmutableList.builder();
- private final ImmutableSet.Builder<String> bootClassPath = ImmutableSet.builder();
- @Nullable private String release;
- @Nullable private String system;
- private String outputDeps;
- private final ImmutableSet.Builder<String> directJars = ImmutableSet.builder();
- @Nullable private String targetLabel;
- @Nullable private String injectingRuleKind;
- private final ImmutableList.Builder<String> depsArtifacts = ImmutableList.builder();
- private boolean javacFallback = true;
- private boolean help = false;
- private final ImmutableList.Builder<String> javacOpts = ImmutableList.builder();
- private boolean shouldReduceClassPath = true;
-
- public TurbineOptions build() {
- return new TurbineOptions(
- output,
- classPath.build(),
- bootClassPath.build(),
- release,
- system,
- sources.build(),
- processorPath.build(),
- processors.build(),
- sourceJars.build(),
- outputDeps,
- directJars.build(),
- targetLabel,
- injectingRuleKind,
- depsArtifacts.build(),
- javacFallback,
- help,
- javacOpts.build(),
- shouldReduceClassPath);
+ @AutoValue.Builder
+ 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 Builder setOutput(String output) {
- this.output = output;
- return this;
- }
+ public abstract Builder setClassPath(ImmutableList<String> classPath);
- public Builder addClassPathEntries(Iterable<String> classPath) {
- this.classPath.addAll(classPath);
- return this;
- }
+ public abstract Builder setBootClassPath(ImmutableList<String> bootClassPath);
- public Builder addBootClassPathEntries(Iterable<String> bootClassPath) {
- this.bootClassPath.addAll(bootClassPath);
- return this;
+ /** @deprecated use {@link #setBootClassPath(ImmutableList)} instead. */
+ @Deprecated
+ public Builder addBootClassPathEntries(Iterable<String> sources) {
+ return setBootClassPath(ImmutableList.copyOf(sources));
}
- public Builder setRelease(String release) {
- this.release = release;
- return this;
- }
+ public abstract Builder setRelease(String release);
- public Builder setSystem(String system) {
- this.system = system;
- return this;
- }
+ 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) {
- this.sources.addAll(sources);
- return this;
+ return setSources(ImmutableList.copyOf(sources));
}
+ /** @deprecated use {@link #setProcessorPath(ImmutableList)} instead. */
+ @Deprecated
public Builder addProcessorPathEntries(Iterable<String> processorPath) {
- this.processorPath.addAll(processorPath);
- return this;
+ 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) {
- this.processors.addAll(processors);
- return this;
+ return setProcessors(ImmutableList.copyOf(processors));
}
- // TODO(cushon): remove this when turbine dependency is updated
- public Builder setTempDir(String tempDir) {
- return this;
- }
+ public abstract Builder setProcessors(ImmutableList<String> processors);
- public Builder setSourceJars(Iterable<String> sourceJars) {
- this.sourceJars.addAll(sourceJars);
- return this;
+ /** @deprecated use {@link #setBuiltinProcessors(ImmutableList)} instead. */
+ @Deprecated
+ public Builder addBuiltinProcessors(Iterable<String> builtinProcessors) {
+ return setBuiltinProcessors(ImmutableList.copyOf(builtinProcessors));
}
- public Builder setOutputDeps(String outputDeps) {
- this.outputDeps = outputDeps;
- return this;
- }
+ public abstract Builder setBuiltinProcessors(ImmutableList<String> builtinProcessors);
- public Builder setTargetLabel(String targetLabel) {
- this.targetLabel = targetLabel;
- return this;
- }
+ public abstract Builder setSourceJars(ImmutableList<String> sourceJars);
- public Builder setInjectingRuleKind(String injectingRuleKind) {
- this.injectingRuleKind = injectingRuleKind;
- return this;
- }
+ public abstract Builder setOutputDeps(String outputDeps);
+
+ public abstract Builder setOutputManifest(String outputManifest);
+ public abstract Builder setTargetLabel(String targetLabel);
+
+ public abstract Builder setInjectingRuleKind(String injectingRuleKind);
+
+ /** @deprecated use {@link #setDepsArtifacts(ImmutableList)} instead. */
+ @Deprecated
public Builder addAllDepsArtifacts(Iterable<String> depsArtifacts) {
- this.depsArtifacts.addAll(depsArtifacts);
- return this;
+ return setDepsArtifacts(ImmutableList.copyOf(depsArtifacts));
}
- public Builder setJavacFallback(boolean javacFallback) {
- this.javacFallback = javacFallback;
- return this;
- }
+ public abstract Builder setDepsArtifacts(ImmutableList<String> depsArtifacts);
- public Builder setHelp(boolean help) {
- this.help = help;
- return this;
- }
+ public abstract Builder setHelp(boolean help);
+
+ abstract ImmutableList.Builder<String> javacOptsBuilder();
public Builder addAllJavacOpts(Iterable<String> javacOpts) {
- this.javacOpts.addAll(javacOpts);
+ javacOptsBuilder().addAll(javacOpts);
return this;
}
- public Builder setShouldReduceClassPath(boolean shouldReduceClassPath) {
- this.shouldReduceClassPath = shouldReduceClassPath;
- return this;
- }
+ public abstract Builder setReducedClasspathMode(ReducedClasspathMode reducedClasspathMode);
- public Builder addDirectJars(ImmutableList<String> jars) {
- this.directJars.addAll(jars);
- return this;
+ /** @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);
+
+ public abstract Builder setGensrcOutput(String gensrcOutput);
+
+ public abstract Builder setResourceOutput(String resourceOutput);
+
+ public abstract Builder setFullClasspathLength(int fullClasspathLength);
+
+ public abstract Builder setReducedClasspathLength(int reducedClasspathLength);
+
+ public abstract TurbineOptions build();
}
}
diff --git a/java/com/google/turbine/options/TurbineOptionsParser.java b/java/com/google/turbine/options/TurbineOptionsParser.java
index 55697ca..17d4bf6 100644
--- a/java/com/google/turbine/options/TurbineOptionsParser.java
+++ b/java/com/google/turbine/options/TurbineOptionsParser.java
@@ -22,6 +22,7 @@ import static java.nio.charset.StandardCharsets.UTF_8;
import com.google.common.base.CharMatcher;
import com.google.common.base.Splitter;
import com.google.common.collect.ImmutableList;
+import com.google.turbine.options.TurbineOptions.ReducedClasspathMode;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
@@ -69,16 +70,19 @@ public class TurbineOptionsParser {
readOne(argumentDeque);
break;
case "--processors":
- builder.addProcessors(readList(argumentDeque));
+ builder.setProcessors(readList(argumentDeque));
+ break;
+ case "--builtin_processors":
+ builder.setBuiltinProcessors(readList(argumentDeque));
break;
case "--processorpath":
- builder.addProcessorPathEntries(readList(argumentDeque));
+ builder.setProcessorPath(readList(argumentDeque));
break;
case "--classpath":
- builder.addClassPathEntries(readList(argumentDeque));
+ builder.setClassPath(readList(argumentDeque));
break;
case "--bootclasspath":
- builder.addBootClassPathEntries(readList(argumentDeque));
+ builder.setBootClassPath(readList(argumentDeque));
break;
case "--release":
builder.setRelease(readOne(argumentDeque));
@@ -94,16 +98,19 @@ public class TurbineOptionsParser {
break;
}
case "--sources":
- builder.addSources(readList(argumentDeque));
+ builder.setSources(readList(argumentDeque));
break;
case "--output_deps":
builder.setOutputDeps(readOne(argumentDeque));
break;
+ case "--output_manifest_proto":
+ builder.setOutputManifest(readOne(argumentDeque));
+ break;
case "--direct_dependencies":
- builder.addDirectJars(readList(argumentDeque));
+ builder.setDirectJars(readList(argumentDeque));
break;
case "--deps_artifacts":
- builder.addAllDepsArtifacts(readList(argumentDeque));
+ builder.setDepsArtifacts(readList(argumentDeque));
break;
case "--target_label":
builder.setTargetLabel(readOne(argumentDeque));
@@ -112,16 +119,32 @@ public class TurbineOptionsParser {
builder.setInjectingRuleKind(readOne(argumentDeque));
break;
case "--javac_fallback":
- builder.setJavacFallback(true);
- break;
case "--nojavac_fallback":
- builder.setJavacFallback(false);
+ // TODO(cushon): remove this case once blaze stops passing the flag
break;
case "--reduce_classpath":
- builder.setShouldReduceClassPath(true);
+ builder.setReducedClasspathMode(ReducedClasspathMode.JAVABUILDER_REDUCED);
break;
case "--noreduce_classpath":
- builder.setShouldReduceClassPath(false);
+ builder.setReducedClasspathMode(ReducedClasspathMode.NONE);
+ break;
+ case "--reduce_classpath_mode":
+ builder.setReducedClasspathMode(ReducedClasspathMode.valueOf(readOne(argumentDeque)));
+ break;
+ case "--full_classpath_length":
+ builder.setFullClasspathLength(Integer.parseInt(readOne(argumentDeque)));
+ break;
+ case "--reduced_classpath_length":
+ builder.setReducedClasspathLength(Integer.parseInt(readOne(argumentDeque)));
+ break;
+ case "--profile":
+ builder.setProfile(readOne(argumentDeque));
+ break;
+ case "--gensrc_output":
+ builder.setGensrcOutput(readOne(argumentDeque));
+ break;
+ case "--resource_output":
+ builder.setResourceOutput(readOne(argumentDeque));
break;
case "--help":
builder.setHelp(true);
diff --git a/java/com/google/turbine/parse/ConstExpressionParser.java b/java/com/google/turbine/parse/ConstExpressionParser.java
index b3666a4..e49d51c 100644
--- a/java/com/google/turbine/parse/ConstExpressionParser.java
+++ b/java/com/google/turbine/parse/ConstExpressionParser.java
@@ -96,7 +96,7 @@ public class ConstExpressionParser {
}
}
- private Tree.Expression primary(boolean negate) {
+ private Tree.@Nullable Expression primary(boolean negate) {
switch (token) {
case INT_LITERAL:
return finishLiteral(TurbineConstantTypeKind.INT, negate);
@@ -133,8 +133,9 @@ public class ConstExpressionParser {
case LPAREN:
return maybeCast();
case LBRACE:
+ int pos = position;
eat();
- return arrayInitializer();
+ return arrayInitializer(pos);
case IDENT:
return qualIdent();
case BYTE:
@@ -244,10 +245,10 @@ public class ConstExpressionParser {
position = lexer.position();
}
- private Tree.Expression arrayInitializer() {
+ private Tree.Expression arrayInitializer(int pos) {
if (token == Token.RBRACE) {
eat();
- return new Tree.ArrayInit(position, ImmutableList.<Tree.Expression>of());
+ return new Tree.ArrayInit(pos, ImmutableList.<Tree.Expression>of());
}
ImmutableList.Builder<Tree.Expression> exprs = ImmutableList.builder();
@@ -273,11 +274,12 @@ public class ConstExpressionParser {
return null;
}
}
- return new Tree.ArrayInit(position, exprs.build());
+ return new Tree.ArrayInit(pos, exprs.build());
}
/** Finish hex, decimal, octal, and binary integer literals (see JLS 3.10.1). */
private Tree.Expression finishLiteral(TurbineConstantTypeKind kind, boolean negate) {
+ int pos = position;
String text = ident().value();
Const.Value value;
switch (kind) {
@@ -354,7 +356,7 @@ public class ConstExpressionParser {
throw new AssertionError(kind);
}
eat();
- return new Tree.Literal(position, kind, value);
+ return new Tree.Literal(pos, kind, value);
}
static boolean isOctal(String text) {
@@ -561,12 +563,16 @@ public class ConstExpressionParser {
return new Tree.TypeCast(position, new Tree.PrimTy(position, ImmutableList.of(), ty), rhs);
}
- private Tree.AnnoExpr annotation() {
+ private Tree.@Nullable AnnoExpr annotation() {
if (token != Token.AT) {
throw new AssertionError();
}
eat();
- ImmutableList<Ident> name = ((Tree.ConstVarName) qualIdent()).name();
+ Tree.ConstVarName constVarName = (Tree.ConstVarName) qualIdent();
+ if (constVarName == null) {
+ return null;
+ }
+ ImmutableList<Ident> name = constVarName.name();
ImmutableList.Builder<Tree.Expression> args = ImmutableList.builder();
if (token == Token.LPAREN) {
eat();
diff --git a/java/com/google/turbine/parse/IteratorLexer.java b/java/com/google/turbine/parse/IteratorLexer.java
index 823f9bb..1b7a9d0 100644
--- a/java/com/google/turbine/parse/IteratorLexer.java
+++ b/java/com/google/turbine/parse/IteratorLexer.java
@@ -56,7 +56,11 @@ public class IteratorLexer implements Lexer {
@Override
public int position() {
- // TODO(cushon): test expression position EOF handling
- return curr != null ? curr.position : -1;
+ return curr.position;
+ }
+
+ @Override
+ public String javadoc() {
+ return null;
}
}
diff --git a/java/com/google/turbine/parse/Lexer.java b/java/com/google/turbine/parse/Lexer.java
index 2d8422a..992bef1 100644
--- a/java/com/google/turbine/parse/Lexer.java
+++ b/java/com/google/turbine/parse/Lexer.java
@@ -31,4 +31,7 @@ public interface Lexer {
/** Returns the source file for diagnostics. */
SourceFile source();
+
+ /** Returns a saved javadoc comment. */
+ String javadoc();
}
diff --git a/java/com/google/turbine/parse/Parser.java b/java/com/google/turbine/parse/Parser.java
index ff3cc3e..4a090b3 100644
--- a/java/com/google/turbine/parse/Parser.java
+++ b/java/com/google/turbine/parse/Parser.java
@@ -23,6 +23,7 @@ import static com.google.turbine.parse.Token.RPAREN;
import static com.google.turbine.parse.Token.SEMI;
import static com.google.turbine.tree.TurbineModifier.PROTECTED;
import static com.google.turbine.tree.TurbineModifier.PUBLIC;
+import static com.google.turbine.tree.TurbineModifier.VARARGS;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
@@ -149,13 +150,14 @@ public class Parser {
break;
case AT:
{
+ int pos = position;
next();
if (token == INTERFACE) {
decls.add(annotationDeclaration(access, annos.build()));
access = EnumSet.noneOf(TurbineModifier.class);
annos = ImmutableList.builder();
} else {
- annos.add(annotation());
+ annos.add(annotation(pos));
}
break;
}
@@ -213,6 +215,7 @@ public class Parser {
}
private TyDecl interfaceDeclaration(EnumSet<TurbineModifier> access, ImmutableList<Anno> annos) {
+ String javadoc = lexer.javadoc();
eat(Token.INTERFACE);
int pos = position;
Ident name = eatIdent();
@@ -241,10 +244,12 @@ public class Parser {
Optional.<ClassTy>empty(),
interfaces.build(),
members,
- TurbineTyKind.INTERFACE);
+ TurbineTyKind.INTERFACE,
+ javadoc);
}
private TyDecl annotationDeclaration(EnumSet<TurbineModifier> access, ImmutableList<Anno> annos) {
+ String javadoc = lexer.javadoc();
eat(Token.INTERFACE);
int pos = position;
Ident name = eatIdent();
@@ -260,10 +265,12 @@ public class Parser {
Optional.<ClassTy>empty(),
ImmutableList.<ClassTy>of(),
members,
- TurbineTyKind.ANNOTATION);
+ TurbineTyKind.ANNOTATION,
+ javadoc);
}
private TyDecl enumDeclaration(EnumSet<TurbineModifier> access, ImmutableList<Anno> annos) {
+ String javadoc = lexer.javadoc();
eat(Token.ENUM);
int pos = position;
Ident name = eatIdent();
@@ -287,7 +294,8 @@ public class Parser {
Optional.<ClassTy>empty(),
interfaces.build(),
members,
- TurbineTyKind.ENUM);
+ TurbineTyKind.ENUM,
+ javadoc);
}
private String moduleName() {
@@ -340,7 +348,7 @@ public class Parser {
return new ModDecl(pos, annos, open, moduleName, directives.build());
}
- private String flatname(char join, ImmutableList<Ident> idents) {
+ private static String flatname(char join, ImmutableList<Ident> idents) {
StringBuilder sb = new StringBuilder();
boolean first = true;
for (Ident ident : idents) {
@@ -466,7 +474,8 @@ public class Parser {
ImmutableList.<Type>of(),
ImmutableList.of()),
name,
- Optional.<Expression>empty()));
+ Optional.<Expression>empty(),
+ null));
annos = ImmutableList.builder();
break;
}
@@ -478,8 +487,9 @@ public class Parser {
annos = ImmutableList.builder();
break OUTER;
case AT:
+ int pos = position;
next();
- annos.add(annotation());
+ annos.add(annotation(pos));
break;
default:
throw error(token);
@@ -489,6 +499,7 @@ public class Parser {
}
private TyDecl classDeclaration(EnumSet<TurbineModifier> access, ImmutableList<Anno> annos) {
+ String javadoc = lexer.javadoc();
eat(Token.CLASS);
int pos = position;
Ident name = eatIdent();
@@ -520,7 +531,8 @@ public class Parser {
Optional.ofNullable(xtnds),
interfaces.build(),
members,
- TurbineTyKind.CLASS);
+ TurbineTyKind.CLASS,
+ javadoc);
}
private ImmutableList<Tree> classMembers() {
@@ -580,13 +592,14 @@ public class Parser {
case AT:
{
// TODO(cushon): de-dup with top-level parsing
+ int pos = position;
next();
if (token == INTERFACE) {
acc.add(annotationDeclaration(access, annos.build()));
access = EnumSet.noneOf(TurbineModifier.class);
annos = ImmutableList.builder();
} else {
- annos.add(annotation());
+ annos.add(annotation(pos));
}
break;
}
@@ -769,8 +782,9 @@ public class Parser {
}
ImmutableList.Builder<Anno> builder = ImmutableList.builder();
while (token == Token.AT) {
+ int pos = position;
next();
- builder.add(annotation());
+ builder.add(annotation(pos));
}
return builder.build();
}
@@ -807,6 +821,7 @@ public class Parser {
ImmutableList<Anno> annos,
Type baseTy,
Ident name) {
+ String javadoc = lexer.javadoc();
ImmutableList.Builder<Tree> result = ImmutableList.builder();
VariableInitializerParser initializerParser = new VariableInitializerParser(token, lexer);
List<List<SavedToken>> bits = initializerParser.parseInitializers();
@@ -831,7 +846,7 @@ public class Parser {
if (init != null && init.kind() == Tree.Kind.ARRAY_INIT) {
init = null;
}
- result.add(new VarDecl(pos, access, annos, ty, name, Optional.ofNullable(init)));
+ result.add(new VarDecl(pos, access, annos, ty, name, Optional.ofNullable(init), javadoc));
}
if (token != SEMI) {
throw TurbineError.format(lexer.source(), expressionStart, ErrorKind.UNTERMINATED_EXPRESSION);
@@ -847,6 +862,7 @@ public class Parser {
ImmutableList<TyParam> typaram,
Type result,
Ident name) {
+ String javadoc = lexer.javadoc();
eat(Token.LPAREN);
ImmutableList.Builder<VarDecl> formals = ImmutableList.builder();
formalParams(formals, access);
@@ -873,8 +889,9 @@ public class Parser {
Tree expr = cparser.expression();
token = cparser.token;
if (expr == null && token == Token.AT) {
+ int annoPos = position;
next();
- expr = annotation();
+ expr = annotation(annoPos);
}
if (expr == null) {
throw error(token);
@@ -898,7 +915,8 @@ public class Parser {
name,
formals.build(),
exceptions.build(),
- Optional.ofNullable(defaultValue));
+ Optional.ofNullable(defaultValue),
+ javadoc);
}
/**
@@ -928,6 +946,10 @@ public class Parser {
if (extra.isEmpty()) {
return type;
}
+ if (type == null) {
+ // trailing dims without a type, e.g. for a constructor declaration
+ throw error(token);
+ }
if (type.kind() == Kind.ARR_TY) {
ArrTy arrTy = (ArrTy) type;
return new ArrTy(arrTy.position(), arrTy.annos(), extraDims(arrTy.elem(), extra));
@@ -962,13 +984,29 @@ public class Parser {
private VarDecl formalParam() {
ImmutableList.Builder<Anno> annos = ImmutableList.builder();
EnumSet<TurbineModifier> access = modifiersAndAnnotations(annos);
- Type ty = referenceType(ImmutableList.of());
+ Type ty = referenceTypeWithoutDims(ImmutableList.of());
ImmutableList<Anno> typeAnnos = maybeAnnos();
- if (maybe(Token.ELLIPSIS)) {
- access.add(TurbineModifier.VARARGS);
- ty = new ArrTy(position, typeAnnos, ty);
- } else {
- ty = maybeDims(typeAnnos, ty);
+ OUTER:
+ while (true) {
+ switch (token) {
+ case LBRACK:
+ next();
+ eat(Token.RBRACK);
+ ty = new ArrTy(position, typeAnnos, ty);
+ typeAnnos = maybeAnnos();
+ break;
+ case ELLIPSIS:
+ next();
+ access.add(VARARGS);
+ ty = new ArrTy(position, typeAnnos, ty);
+ typeAnnos = ImmutableList.of();
+ break OUTER;
+ default:
+ break OUTER;
+ }
+ }
+ if (!typeAnnos.isEmpty()) {
+ throw error(token);
}
// the parameter name is `this` for receiver parameters, and a qualified this expression
// for inner classes
@@ -979,7 +1017,8 @@ public class Parser {
name = identOrThis();
}
ty = extraDims(ty);
- return new VarDecl(position, access, annos.build(), ty, name, Optional.<Expression>empty());
+ return new VarDecl(
+ position, access, annos.build(), ty, name, Optional.<Expression>empty(), null);
}
private Ident identOrThis() {
@@ -1041,13 +1080,14 @@ public class Parser {
OUTER:
while (true) {
ImmutableList<Anno> annotations = maybeAnnos();
+ int pos = position;
Ident name = eatIdent();
ImmutableList<Tree> bounds = ImmutableList.of();
if (token == Token.EXTENDS) {
next();
bounds = tybounds();
}
- acc.add(new TyParam(position, name, bounds, annotations));
+ acc.add(new TyParam(pos, name, bounds, annotations));
switch (token) {
case COMMA:
eat(Token.COMMA);
@@ -1166,49 +1206,42 @@ public class Parser {
return acc.build();
}
- private Type referenceType(ImmutableList<Anno> typeAnnos) {
- Type ty;
+ private Type referenceTypeWithoutDims(ImmutableList<Anno> typeAnnos) {
switch (token) {
case IDENT:
- ty = classty(null, typeAnnos);
- break;
+ return classty(null, typeAnnos);
case BOOLEAN:
next();
- ty = new PrimTy(position, typeAnnos, TurbineConstantTypeKind.BOOLEAN);
- break;
+ return new PrimTy(position, typeAnnos, TurbineConstantTypeKind.BOOLEAN);
case BYTE:
next();
- ty = new PrimTy(position, typeAnnos, TurbineConstantTypeKind.BYTE);
- break;
+ return new PrimTy(position, typeAnnos, TurbineConstantTypeKind.BYTE);
case SHORT:
next();
- ty = new PrimTy(position, typeAnnos, TurbineConstantTypeKind.SHORT);
- break;
+ return new PrimTy(position, typeAnnos, TurbineConstantTypeKind.SHORT);
case INT:
next();
- ty = new PrimTy(position, typeAnnos, TurbineConstantTypeKind.INT);
- break;
+ return new PrimTy(position, typeAnnos, TurbineConstantTypeKind.INT);
case LONG:
next();
- ty = new PrimTy(position, typeAnnos, TurbineConstantTypeKind.LONG);
- break;
+ return new PrimTy(position, typeAnnos, TurbineConstantTypeKind.LONG);
case CHAR:
next();
- ty = new PrimTy(position, typeAnnos, TurbineConstantTypeKind.CHAR);
- break;
+ return new PrimTy(position, typeAnnos, TurbineConstantTypeKind.CHAR);
case DOUBLE:
next();
- ty = new PrimTy(position, typeAnnos, TurbineConstantTypeKind.DOUBLE);
- break;
+ return new PrimTy(position, typeAnnos, TurbineConstantTypeKind.DOUBLE);
case FLOAT:
next();
- ty = new PrimTy(position, typeAnnos, TurbineConstantTypeKind.FLOAT);
- break;
+ return new PrimTy(position, typeAnnos, TurbineConstantTypeKind.FLOAT);
default:
throw error(token);
}
- ty = maybeDims(maybeAnnos(), ty);
- return ty;
+ }
+
+ private Type referenceType(ImmutableList<Anno> typeAnnos) {
+ Type ty = referenceTypeWithoutDims(typeAnnos);
+ return maybeDims(maybeAnnos(), ty);
}
private Type maybeDims(ImmutableList<Anno> typeAnnos, Type ty) {
@@ -1269,8 +1302,9 @@ public class Parser {
access.add(TurbineModifier.STRICTFP);
break;
case AT:
+ int pos = position;
next();
- annos.add(annotation());
+ annos.add(annotation(pos));
break;
default:
return access;
@@ -1318,8 +1352,7 @@ public class Parser {
return name.build();
}
- private Anno annotation() {
- int pos = position;
+ private Anno annotation(int pos) {
ImmutableList<Ident> name = qualIdent();
ImmutableList.Builder<Expression> args = ImmutableList.builder();
diff --git a/java/com/google/turbine/parse/StreamLexer.java b/java/com/google/turbine/parse/StreamLexer.java
index 74b0ce8..2e20c26 100644
--- a/java/com/google/turbine/parse/StreamLexer.java
+++ b/java/com/google/turbine/parse/StreamLexer.java
@@ -16,9 +16,9 @@
package com.google.turbine.parse;
+import static com.google.common.base.Verify.verify;
import static com.google.turbine.parse.UnicodeEscapePreprocessor.ASCII_SUB;
-import com.google.common.base.Verify;
import com.google.turbine.diag.SourceFile;
import com.google.turbine.diag.TurbineError;
import com.google.turbine.diag.TurbineError.ErrorKind;
@@ -40,6 +40,9 @@ public class StreamLexer implements Lexer {
/** The value of the current string or character literal token. */
private String value = null;
+ /** A saved javadoc comment. */
+ private String javadoc = null;
+
public StreamLexer(UnicodeEscapePreprocessor reader) {
this.reader = reader;
eat();
@@ -62,6 +65,17 @@ public class StreamLexer implements Lexer {
}
@Override
+ public String javadoc() {
+ String result = javadoc;
+ javadoc = null;
+ if (result == null) {
+ return null;
+ }
+ verify(result.endsWith("*/"), result);
+ return result.substring(0, result.length() - "*/".length());
+ }
+
+ @Override
public String stringValue() {
if (value != null) {
return value;
@@ -111,30 +125,51 @@ public class StreamLexer implements Lexer {
}
eat();
break;
+ default: // fall out
}
}
case '*':
+ eat();
boolean sawStar = false;
- while (true) {
+ boolean isJavadoc = false;
+ if (ch == '*') {
eat();
+ // handle empty non-javadoc comments: `/**/`
+ if (ch == '/') {
+ eat();
+ continue OUTER;
+ }
+ isJavadoc = true;
+ readFrom();
+ }
+ while (true) {
switch (ch) {
case '*':
+ eat();
sawStar = true;
break;
case '/':
+ eat();
if (sawStar) {
- eat();
+ if (isJavadoc) {
+ // Save the comment, excluding the leading `/**` and including
+ // the trailing `/*`. The comment is trimmed and normalized later.
+ javadoc = stringValue();
+ }
continue OUTER;
}
sawStar = false;
break;
case ASCII_SUB:
if (reader.done()) {
- return Token.EOF;
+ throw TurbineError.format(
+ reader.source(), position, ErrorKind.UNCLOSED_COMMENT);
}
eat();
+ sawStar = false;
break;
default:
+ eat();
sawStar = false;
break;
}
@@ -205,7 +240,9 @@ public class StreamLexer implements Lexer {
return identifier();
case ASCII_SUB:
- Verify.verify(reader.done());
+ if (!reader.done()) {
+ throw error(ErrorKind.UNEXPECTED_EOF);
+ }
return Token.EOF;
case '-':
@@ -512,6 +549,7 @@ public class StreamLexer implements Lexer {
eat();
signedInteger();
break;
+ default: // fall out
}
return floatTypeSuffix();
}
@@ -526,6 +564,7 @@ public class StreamLexer implements Lexer {
eat();
signedInteger();
break;
+ default: // fall out
}
return floatTypeSuffix();
}
@@ -989,7 +1028,7 @@ public class StreamLexer implements Lexer {
return makeIdent(stringValue());
}
- private Token makeIdent(String s) {
+ private static Token makeIdent(String s) {
switch (s) {
case "abstract":
return Token.ABSTRACT;
diff --git a/java/com/google/turbine/parse/UnicodeEscapePreprocessor.java b/java/com/google/turbine/parse/UnicodeEscapePreprocessor.java
index 58f129d..3f38561 100644
--- a/java/com/google/turbine/parse/UnicodeEscapePreprocessor.java
+++ b/java/com/google/turbine/parse/UnicodeEscapePreprocessor.java
@@ -16,7 +16,10 @@
package com.google.turbine.parse;
+import com.google.errorprone.annotations.CheckReturnValue;
import com.google.turbine.diag.SourceFile;
+import com.google.turbine.diag.TurbineError;
+import com.google.turbine.diag.TurbineError.ErrorKind;
/** Preprocesses Unicode escape characters in Java source code, as described in JLS §3.3. */
public class UnicodeEscapePreprocessor {
@@ -85,7 +88,7 @@ public class UnicodeEscapePreprocessor {
}
/** Consumes a hex digit. */
- private static int hexDigit(char d) {
+ private int hexDigit(char d) {
switch (d) {
case '0':
case '1':
@@ -113,9 +116,9 @@ public class UnicodeEscapePreprocessor {
case 'f':
return ((d - 'a') + 10);
case ASCII_SUB:
- throw new AssertionError("unexpected end of input");
+ throw error(ErrorKind.UNEXPECTED_EOF);
default:
- throw new AssertionError(String.format("unexpected hex digit: 0x%x", (int) d));
+ throw error(ErrorKind.INVALID_UNICODE);
}
}
@@ -134,4 +137,10 @@ public class UnicodeEscapePreprocessor {
public SourceFile source() {
return source;
}
+
+ @CheckReturnValue
+ private TurbineError error(ErrorKind kind, Object... args) {
+ throw TurbineError.format(
+ source(), Math.min(position(), source().source().length() - 1), kind, args);
+ }
}
diff --git a/java/com/google/turbine/parse/VariableInitializerParser.java b/java/com/google/turbine/parse/VariableInitializerParser.java
index a39e9e8..4ad9272 100644
--- a/java/com/google/turbine/parse/VariableInitializerParser.java
+++ b/java/com/google/turbine/parse/VariableInitializerParser.java
@@ -149,8 +149,6 @@ public class VariableInitializerParser {
case START:
case TYPE:
break OUTER;
- default:
- break;
}
save();
next();
@@ -163,8 +161,6 @@ public class VariableInitializerParser {
case TYPE:
commas.add(tokens.size());
break;
- default:
- break;
}
break;
case DOT:
@@ -209,14 +205,14 @@ public class VariableInitializerParser {
result.add(
ImmutableList.<SavedToken>builder()
.addAll(tokens.subList(start, idx - 1))
- .add(new SavedToken(Token.EOF, null, -1))
+ .add(new SavedToken(Token.EOF, null, tokens.get(idx - 1).position))
.build());
start = idx;
}
result.add(
ImmutableList.<SavedToken>builder()
.addAll(tokens.subList(start, tokens.size()))
- .add(new SavedToken(Token.EOF, null, -1))
+ .add(new SavedToken(Token.EOF, null, lexer.position()))
.build());
return result;
}
@@ -284,6 +280,9 @@ public class VariableInitializerParser {
int lastType = -1;
int lastComma = -1;
for (int i = 0; i < many; i++) {
+ if (ltIndices.isEmpty()) {
+ throw error(ErrorKind.UNEXPECTED_TOKEN, ">");
+ }
lastType = ltIndices.removeLast();
lastComma = commaIndices.removeLast();
}
diff --git a/java/com/google/turbine/processing/ClassHierarchy.java b/java/com/google/turbine/processing/ClassHierarchy.java
new file mode 100644
index 0000000..50c929c
--- /dev/null
+++ b/java/com/google/turbine/processing/ClassHierarchy.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright 2019 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.processing;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+import com.google.turbine.binder.bound.TypeBoundClass;
+import com.google.turbine.binder.env.CompoundEnv;
+import com.google.turbine.binder.env.Env;
+import com.google.turbine.binder.sym.ClassSymbol;
+import com.google.turbine.diag.TurbineError;
+import com.google.turbine.diag.TurbineError.ErrorKind;
+import com.google.turbine.type.Type;
+import com.google.turbine.type.Type.ClassTy;
+import com.google.turbine.type.Type.TyKind;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * A representation of the class hierarchy, with logic for performing search between subtypes and
+ * their supertypes.
+ */
+public class ClassHierarchy {
+
+ private final Map<ClassSymbol, HierarchyNode> cache = new HashMap<>();
+ private Env<ClassSymbol, ? extends TypeBoundClass> env;
+
+ ClassHierarchy(Env<ClassSymbol, ? extends TypeBoundClass> env) {
+ this.env = env;
+ }
+
+ public void round(CompoundEnv<ClassSymbol, TypeBoundClass> env) {
+ cache.clear();
+ this.env = env;
+ }
+
+ /** A linked list between two types in the hierarchy. */
+ private static class PathNode {
+
+ /** The class for this node. */
+ final ClassTy type;
+
+ /** The node corresponding to a direct super-type of this class, or {@code null}. */
+ final PathNode ancestor;
+
+ PathNode(ClassTy type, PathNode ancestor) {
+ this.type = type;
+ this.ancestor = ancestor;
+ }
+ }
+
+ /**
+ * A node in the type hierarchy, corresponding to a class symbol U. For each type V in the
+ * transitive supertype hierarchy of U, we save a mapping from the class symbol for V to the path
+ * from U to V in the type hierarchy.
+ */
+ private class HierarchyNode {
+
+ private final ClassSymbol sym;
+ private final Map<ClassSymbol, PathNode> ancestors = new LinkedHashMap<>();
+
+ HierarchyNode(ClassSymbol sym) {
+ this.sym = sym;
+ }
+
+ /** Adds a child (direct supertype) of this node. */
+ private void add(Type type) {
+ if (type.tyKind() != TyKind.CLASS_TY) {
+ // ignore any erroneous types that ended up in the hierarchy
+ return;
+ }
+ ClassTy classTy = (ClassTy) type;
+ HierarchyNode child = get(classTy.sym());
+ // add a new edge to the direct supertype
+ PathNode existing = ancestors.putIfAbsent(child.sym, new PathNode(classTy, null));
+ if (existing != null) {
+ // if this child has already been added don't re-process its ancestors
+ return;
+ }
+ // copy and extend edges for the transitive supertypes
+ for (Map.Entry<ClassSymbol, PathNode> n : child.ancestors.entrySet()) {
+ ancestors.putIfAbsent(n.getKey(), new PathNode(classTy, n.getValue()));
+ }
+ }
+
+ /** The supertype closure of this node. */
+ private Set<ClassSymbol> closure() {
+ return ancestors.keySet();
+ }
+ }
+
+ private HierarchyNode compute(ClassSymbol sym) {
+ HierarchyNode node = new HierarchyNode(sym);
+ TypeBoundClass info = env.get(sym);
+ if (info == null) {
+ throw TurbineError.format(/* source= */ null, ErrorKind.SYMBOL_NOT_FOUND, sym);
+ }
+ if (info.superClassType() != null) {
+ node.add(info.superClassType());
+ }
+ for (Type type : info.interfaceTypes()) {
+ node.add(type);
+ }
+ return node;
+ }
+
+ private HierarchyNode get(ClassSymbol sym) {
+ // dont use computeIfAbsent, to support re-entrant lookups
+ HierarchyNode result = cache.get(sym);
+ if (result != null) {
+ return result;
+ }
+ result = compute(sym);
+ cache.put(sym, result);
+ return result;
+ }
+
+ /**
+ * Returns a list of types on the path between the given type {@code t} and a transitive
+ * superclass {@code s}, or an empty list if no such path exists.
+ */
+ ImmutableList<ClassTy> search(Type t, ClassSymbol s) {
+ if (t.tyKind() != TyKind.CLASS_TY) {
+ return ImmutableList.of();
+ }
+ ClassTy classTy = (ClassTy) t;
+ if (classTy.sym().equals(s)) {
+ return ImmutableList.of(classTy);
+ }
+ HierarchyNode node = get(classTy.sym());
+ PathNode path = node.ancestors.get(s);
+ if (path == null) {
+ return ImmutableList.of();
+ }
+ ImmutableList.Builder<ClassTy> result = ImmutableList.builder();
+ result.add(classTy);
+ while (path != null) {
+ result.add(path.type);
+ path = path.ancestor;
+ }
+ return result.build().reverse();
+ }
+
+ /**
+ * Returns all classes in the transitive supertype hierarchy of the given class, including the
+ * class itself.
+ *
+ * <p>The iteration order of the results is undefined, and in particular no guarantees are made
+ * about the ordering of sub-types and super-types.
+ */
+ public Iterable<ClassSymbol> transitiveSupertypes(ClassSymbol s) {
+ return Iterables.concat(ImmutableList.of(s), get(s).closure());
+ }
+}
diff --git a/java/com/google/turbine/processing/ModelFactory.java b/java/com/google/turbine/processing/ModelFactory.java
new file mode 100644
index 0000000..9b782cd
--- /dev/null
+++ b/java/com/google/turbine/processing/ModelFactory.java
@@ -0,0 +1,391 @@
+/*
+ * Copyright 2019 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.processing;
+
+import static com.google.common.collect.Iterables.getOnlyElement;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.base.Splitter;
+import com.google.common.base.Supplier;
+import com.google.common.base.Verify;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.turbine.binder.bound.TypeBoundClass;
+import com.google.turbine.binder.bound.TypeBoundClass.FieldInfo;
+import com.google.turbine.binder.bound.TypeBoundClass.MethodInfo;
+import com.google.turbine.binder.bound.TypeBoundClass.ParamInfo;
+import com.google.turbine.binder.bound.TypeBoundClass.TyVarInfo;
+import com.google.turbine.binder.env.CompoundEnv;
+import com.google.turbine.binder.env.Env;
+import com.google.turbine.binder.lookup.LookupKey;
+import com.google.turbine.binder.lookup.LookupResult;
+import com.google.turbine.binder.lookup.TopLevelIndex;
+import com.google.turbine.binder.sym.ClassSymbol;
+import com.google.turbine.binder.sym.FieldSymbol;
+import com.google.turbine.binder.sym.MethodSymbol;
+import com.google.turbine.binder.sym.PackageSymbol;
+import com.google.turbine.binder.sym.ParamSymbol;
+import com.google.turbine.binder.sym.Symbol;
+import com.google.turbine.binder.sym.TyVarSymbol;
+import com.google.turbine.model.TurbineConstantTypeKind;
+import com.google.turbine.processing.TurbineElement.TurbineExecutableElement;
+import com.google.turbine.processing.TurbineElement.TurbineFieldElement;
+import com.google.turbine.processing.TurbineElement.TurbineNoTypeElement;
+import com.google.turbine.processing.TurbineElement.TurbinePackageElement;
+import com.google.turbine.processing.TurbineElement.TurbineParameterElement;
+import com.google.turbine.processing.TurbineElement.TurbineTypeElement;
+import com.google.turbine.processing.TurbineElement.TurbineTypeParameterElement;
+import com.google.turbine.processing.TurbineTypeMirror.TurbineArrayType;
+import com.google.turbine.processing.TurbineTypeMirror.TurbineDeclaredType;
+import com.google.turbine.processing.TurbineTypeMirror.TurbineErrorType;
+import com.google.turbine.processing.TurbineTypeMirror.TurbineExecutableType;
+import com.google.turbine.processing.TurbineTypeMirror.TurbineIntersectionType;
+import com.google.turbine.processing.TurbineTypeMirror.TurbineNoType;
+import com.google.turbine.processing.TurbineTypeMirror.TurbineNullType;
+import com.google.turbine.processing.TurbineTypeMirror.TurbinePackageType;
+import com.google.turbine.processing.TurbineTypeMirror.TurbinePrimitiveType;
+import com.google.turbine.processing.TurbineTypeMirror.TurbineTypeVariable;
+import com.google.turbine.processing.TurbineTypeMirror.TurbineVoidType;
+import com.google.turbine.processing.TurbineTypeMirror.TurbineWildcardType;
+import com.google.turbine.tree.Tree.Ident;
+import com.google.turbine.type.Type;
+import com.google.turbine.type.Type.ArrayTy;
+import com.google.turbine.type.Type.ClassTy;
+import com.google.turbine.type.Type.ErrorTy;
+import com.google.turbine.type.Type.IntersectionTy;
+import com.google.turbine.type.Type.MethodTy;
+import com.google.turbine.type.Type.PrimTy;
+import com.google.turbine.type.Type.TyVar;
+import com.google.turbine.type.Type.WildTy;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicInteger;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.NoType;
+import javax.lang.model.type.NullType;
+import javax.lang.model.type.TypeMirror;
+
+/**
+ * A factoy for turbine's implementations of {@link Element} and {@link TypeMirror}.
+ *
+ * <p>The model provided by those interfaces contains cycles between types and elements, e.g. {@link
+ * Element#asType} and {@link javax.lang.model.type.DeclaredType#asElement}. Turbine's internal
+ * model uses an immutable representation of classes and types which cannot represent cycles
+ * directly. Instead, the implementations in {@link TurbineElement} and {@link TurbineTypeMirror}
+ * maintain a reference to this class, and use it to lazily construct edges in the type and element
+ * graph.
+ */
+public class ModelFactory {
+
+ public Env<ClassSymbol, ? extends TypeBoundClass> env;
+
+ private final AtomicInteger round = new AtomicInteger(0);
+
+ public void round(CompoundEnv<ClassSymbol, TypeBoundClass> env, TopLevelIndex tli) {
+ this.env = env;
+ this.tli = tli;
+ round.getAndIncrement();
+ cha.round(env);
+ }
+
+ private final HashMap<Type, TurbineTypeMirror> typeCache = new HashMap<>();
+
+ private final Map<FieldSymbol, TurbineFieldElement> fieldCache = new HashMap<>();
+ private final Map<MethodSymbol, TurbineExecutableElement> methodCache = new HashMap<>();
+ private final Map<ClassSymbol, TurbineTypeElement> classCache = new HashMap<>();
+ private final Map<ParamSymbol, TurbineParameterElement> paramCache = new HashMap<>();
+ private final Map<TyVarSymbol, TurbineTypeParameterElement> tyParamCache = new HashMap<>();
+ private final Map<PackageSymbol, TurbinePackageElement> packageCache = new HashMap<>();
+
+ private final HashMap<CharSequence, ClassSymbol> inferSymbolCache = new HashMap<>();
+
+ private final ClassHierarchy cha;
+ private final ClassLoader processorLoader;
+
+ private TopLevelIndex tli;
+
+ public ModelFactory(
+ Env<ClassSymbol, ? extends TypeBoundClass> env,
+ ClassLoader processorLoader,
+ TopLevelIndex tli) {
+ this.env = requireNonNull(env);
+ this.cha = new ClassHierarchy(env);
+ this.processorLoader = requireNonNull(processorLoader);
+ this.tli = requireNonNull(tli);
+ }
+
+ TypeMirror asTypeMirror(Type type) {
+ return typeCache.computeIfAbsent(type, this::createTypeMirror);
+ }
+
+ /**
+ * Returns a supplier that memoizes the result of the input supplier.
+ *
+ * <p>It ensures that the results are invalidated after each annotation processing round, to
+ * support computations that depend on information in the current round and which might change in
+ * future, e.g. as additional types are generated.
+ */
+ <T> Supplier<T> memoize(Supplier<T> s) {
+ return new Supplier<T>() {
+ T v;
+ int initializedInRound = -1;
+
+ @Override
+ public T get() {
+ int r = round.get();
+ if (initializedInRound != r) {
+ v = s.get();
+ initializedInRound = r;
+ }
+ return v;
+ }
+ };
+ }
+
+ /** Creates a {@link TurbineTypeMirror} backed by a {@link Type}. */
+ private TurbineTypeMirror createTypeMirror(Type type) {
+ switch (type.tyKind()) {
+ case PRIM_TY:
+ if (((PrimTy) type).primkind() == TurbineConstantTypeKind.STRING) {
+ return new TurbineDeclaredType(this, ClassTy.STRING);
+ }
+ return new TurbinePrimitiveType(this, (PrimTy) type);
+ case CLASS_TY:
+ return new TurbineDeclaredType(this, (ClassTy) type);
+ case ARRAY_TY:
+ return new TurbineArrayType(this, (ArrayTy) type);
+ case VOID_TY:
+ return new TurbineVoidType(this);
+ case WILD_TY:
+ return new TurbineWildcardType(this, (WildTy) type);
+ case TY_VAR:
+ return new TurbineTypeVariable(this, (TyVar) type);
+ case INTERSECTION_TY:
+ IntersectionTy intersectionTy = (IntersectionTy) type;
+ switch (intersectionTy.bounds().size()) {
+ case 0:
+ return createTypeMirror(ClassTy.OBJECT);
+ case 1:
+ return createTypeMirror(getOnlyElement(intersectionTy.bounds()));
+ default:
+ return new TurbineIntersectionType(this, intersectionTy);
+ }
+ case NONE_TY:
+ return new TurbineNoType(this);
+ case METHOD_TY:
+ return new TurbineExecutableType(this, (MethodTy) type);
+ case ERROR_TY:
+ return new TurbineErrorType(this, (ErrorTy) type);
+ }
+ throw new AssertionError(type.tyKind());
+ }
+
+ /** Creates a list of {@link TurbineTypeMirror}s backed by the given {@link Type}s. */
+ ImmutableList<TypeMirror> asTypeMirrors(Iterable<? extends Type> types) {
+ ImmutableList.Builder<TypeMirror> result = ImmutableList.builder();
+ for (Type type : types) {
+ result.add(asTypeMirror(type));
+ }
+ return result.build();
+ }
+
+ NoType noType() {
+ return (NoType) asTypeMirror(Type.NONE);
+ }
+
+ NoType packageType(PackageSymbol symbol) {
+ return new TurbinePackageType(this, symbol);
+ }
+
+ public NullType nullType() {
+ return new TurbineNullType(this);
+ }
+
+ /** Creates an {@link Element} backed by the given {@link Symbol}. */
+ Element element(Symbol symbol) {
+ switch (symbol.symKind()) {
+ case CLASS:
+ return typeElement((ClassSymbol) symbol);
+ case TY_PARAM:
+ return typeParameterElement((TyVarSymbol) symbol);
+ case METHOD:
+ return executableElement((MethodSymbol) symbol);
+ case FIELD:
+ return fieldElement((FieldSymbol) symbol);
+ case PARAMETER:
+ return parameterElement((ParamSymbol) symbol);
+ case PACKAGE:
+ return packageElement((PackageSymbol) symbol);
+ case MODULE:
+ break;
+ }
+ throw new AssertionError(symbol.symKind());
+ }
+
+ Element noElement(String name) {
+ return new TurbineNoTypeElement(this, name);
+ }
+
+ TurbineFieldElement fieldElement(FieldSymbol symbol) {
+ return fieldCache.computeIfAbsent(symbol, k -> new TurbineFieldElement(this, symbol));
+ }
+
+ TurbineExecutableElement executableElement(MethodSymbol symbol) {
+ return methodCache.computeIfAbsent(symbol, k -> new TurbineExecutableElement(this, symbol));
+ }
+
+ public TurbineTypeElement typeElement(ClassSymbol symbol) {
+ Verify.verify(!symbol.simpleName().equals("package-info"), "%s", symbol);
+ return classCache.computeIfAbsent(symbol, k -> new TurbineTypeElement(this, symbol));
+ }
+
+ TurbinePackageElement packageElement(PackageSymbol symbol) {
+ return packageCache.computeIfAbsent(symbol, k -> new TurbinePackageElement(this, symbol));
+ }
+
+ VariableElement parameterElement(ParamSymbol sym) {
+ return paramCache.computeIfAbsent(sym, k -> new TurbineParameterElement(this, sym));
+ }
+
+ TurbineTypeParameterElement typeParameterElement(TyVarSymbol sym) {
+ return tyParamCache.computeIfAbsent(sym, k -> new TurbineTypeParameterElement(this, sym));
+ }
+
+ ImmutableSet<Element> elements(ImmutableSet<? extends Symbol> symbols) {
+ ImmutableSet.Builder<Element> result = ImmutableSet.builder();
+ for (Symbol symbol : symbols) {
+ result.add(element(symbol));
+ }
+ return result.build();
+ }
+
+ public ClassSymbol inferSymbol(CharSequence name) {
+ return inferSymbolCache.computeIfAbsent(name, key -> inferSymbolImpl(name));
+ }
+
+ private ClassSymbol inferSymbolImpl(CharSequence name) {
+ LookupResult lookup = tli.scope().lookup(new LookupKey(asIdents(name)));
+ if (lookup == null) {
+ return null;
+ }
+ ClassSymbol sym = (ClassSymbol) lookup.sym();
+ for (Ident bit : lookup.remaining()) {
+ sym = getSymbol(sym).children().get(bit.value());
+ if (sym == null) {
+ return null;
+ }
+ }
+ return sym;
+ }
+
+ private static ImmutableList<Ident> asIdents(CharSequence name) {
+ ImmutableList.Builder<Ident> result = ImmutableList.builder();
+ for (String bit : Splitter.on('.').split(name)) {
+ result.add(new Ident(-1, bit));
+ }
+ return result.build();
+ }
+
+ /**
+ * Returns the {@link TypeBoundClass} for the given {@link ClassSymbol} from the current
+ * environment.
+ */
+ TypeBoundClass getSymbol(ClassSymbol sym) {
+ return env.get(sym);
+ }
+
+ MethodInfo getMethodInfo(MethodSymbol method) {
+ TypeBoundClass info = getSymbol(method.owner());
+ for (MethodInfo m : info.methods()) {
+ if (m.sym().equals(method)) {
+ return m;
+ }
+ }
+ return null;
+ }
+
+ ParamInfo getParamInfo(ParamSymbol sym) {
+ MethodInfo info = getMethodInfo(sym.owner());
+ for (ParamInfo p : info.parameters()) {
+ if (p.sym().equals(sym)) {
+ return p;
+ }
+ }
+ return null;
+ }
+
+ FieldInfo getFieldInfo(FieldSymbol symbol) {
+ TypeBoundClass info = getSymbol(symbol.owner());
+ requireNonNull(info, symbol.owner().toString());
+ for (FieldInfo field : info.fields()) {
+ if (field.sym().equals(symbol)) {
+ return field;
+ }
+ }
+ throw new AssertionError(symbol);
+ }
+
+ TyVarInfo getTyVarInfo(TyVarSymbol tyVar) {
+ Symbol owner = tyVar.owner();
+ Verify.verifyNotNull(owner); // TODO(cushon): capture variables
+ ImmutableMap<TyVarSymbol, TyVarInfo> tyParams;
+ switch (owner.symKind()) {
+ case METHOD:
+ tyParams = getMethodInfo((MethodSymbol) owner).tyParams();
+ break;
+ case CLASS:
+ tyParams = getSymbol((ClassSymbol) owner).typeParameterTypes();
+ break;
+ default:
+ throw new AssertionError(owner.symKind());
+ }
+ return tyParams.get(tyVar);
+ }
+
+ static ClassSymbol enclosingClass(Symbol sym) {
+ switch (sym.symKind()) {
+ case CLASS:
+ return (ClassSymbol) sym;
+ case TY_PARAM:
+ return enclosingClass(((TyVarSymbol) sym).owner());
+ case METHOD:
+ return ((MethodSymbol) sym).owner();
+ case FIELD:
+ return ((FieldSymbol) sym).owner();
+ case PARAMETER:
+ return ((ParamSymbol) sym).owner().owner();
+ case PACKAGE:
+ case MODULE:
+ throw new IllegalArgumentException(sym.toString());
+ }
+ throw new AssertionError(sym.symKind());
+ }
+
+ ClassHierarchy cha() {
+ return cha;
+ }
+
+ ClassLoader processorLoader() {
+ return processorLoader;
+ }
+
+ TopLevelIndex tli() {
+ return tli;
+ }
+}
diff --git a/java/com/google/turbine/processing/TurbineAnnotationMirror.java b/java/com/google/turbine/processing/TurbineAnnotationMirror.java
new file mode 100644
index 0000000..5ea3de1
--- /dev/null
+++ b/java/com/google/turbine/processing/TurbineAnnotationMirror.java
@@ -0,0 +1,349 @@
+/*
+ * Copyright 2019 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.processing;
+
+import static com.google.common.base.Preconditions.checkState;
+import static com.google.common.collect.Iterables.getLast;
+
+import com.google.common.base.Joiner;
+import com.google.common.base.Supplier;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.turbine.binder.bound.EnumConstantValue;
+import com.google.turbine.binder.bound.TurbineAnnotationValue;
+import com.google.turbine.binder.bound.TurbineClassValue;
+import com.google.turbine.binder.bound.TypeBoundClass.MethodInfo;
+import com.google.turbine.model.Const;
+import com.google.turbine.model.Const.ArrayInitValue;
+import com.google.turbine.processing.TurbineElement.TurbineExecutableElement;
+import com.google.turbine.processing.TurbineElement.TurbineFieldElement;
+import com.google.turbine.type.AnnoInfo;
+import com.google.turbine.type.Type.ErrorTy;
+import com.google.turbine.type.Type.TyKind;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.AnnotationValue;
+import javax.lang.model.element.AnnotationValueVisitor;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.ErrorType;
+import javax.lang.model.type.TypeMirror;
+
+/**
+ * An implementation of {@link AnnotationMirror} and {@link AnnotationValue} backed by {@link
+ * AnnoInfo} and {@link Const.Value}.
+ */
+class TurbineAnnotationMirror implements TurbineAnnotationValueMirror, AnnotationMirror {
+
+ static TurbineAnnotationValueMirror annotationValue(ModelFactory factory, Const value) {
+ switch (value.kind()) {
+ case ARRAY:
+ return new TurbineArrayConstant(factory, (ArrayInitValue) value);
+ case PRIMITIVE:
+ return new TurbinePrimitiveConstant((Const.Value) value);
+ case CLASS_LITERAL:
+ return new TurbineClassConstant(factory, (TurbineClassValue) value);
+ case ENUM_CONSTANT:
+ return new TurbineEnumConstant(factory, (EnumConstantValue) value);
+ case ANNOTATION:
+ return new TurbineAnnotationMirror(factory, (TurbineAnnotationValue) value);
+ }
+ throw new AssertionError(value.kind());
+ }
+
+ static TurbineAnnotationMirror create(ModelFactory factory, AnnoInfo anno) {
+ return new TurbineAnnotationMirror(factory, new TurbineAnnotationValue(anno));
+ }
+
+ private final TurbineAnnotationValue value;
+ private final AnnoInfo anno;
+ private final Supplier<DeclaredType> type;
+ private final Supplier<ImmutableMap<String, MethodInfo>> elements;
+ private final Supplier<ImmutableMap<ExecutableElement, AnnotationValue>> elementValues;
+ private final Supplier<ImmutableMap<ExecutableElement, AnnotationValue>>
+ elementValuesWithDefaults;
+
+ private TurbineAnnotationMirror(ModelFactory factory, TurbineAnnotationValue value) {
+ this.value = value;
+ this.anno = value.info();
+ this.type =
+ factory.memoize(
+ new Supplier<DeclaredType>() {
+ @Override
+ public DeclaredType get() {
+ if (anno.sym() == null) {
+ return (ErrorType)
+ factory.asTypeMirror(ErrorTy.create(getLast(anno.tree().name()).value()));
+ }
+ return (DeclaredType) factory.typeElement(anno.sym()).asType();
+ }
+ });
+ this.elements =
+ factory.memoize(
+ new Supplier<ImmutableMap<String, MethodInfo>>() {
+ @Override
+ public ImmutableMap<String, MethodInfo> get() {
+ ImmutableMap.Builder<String, MethodInfo> result = ImmutableMap.builder();
+ for (MethodInfo m : factory.getSymbol(anno.sym()).methods()) {
+ checkState(m.parameters().isEmpty());
+ result.put(m.name(), m);
+ }
+ return result.build();
+ }
+ });
+ this.elementValues =
+ factory.memoize(
+ new Supplier<ImmutableMap<ExecutableElement, AnnotationValue>>() {
+ @Override
+ public ImmutableMap<ExecutableElement, AnnotationValue> get() {
+ ImmutableMap.Builder<ExecutableElement, AnnotationValue> result =
+ ImmutableMap.builder();
+ for (Map.Entry<String, Const> value : anno.values().entrySet()) {
+ result.put(
+ factory.executableElement(elements.get().get(value.getKey()).sym()),
+ annotationValue(factory, value.getValue()));
+ }
+ return result.build();
+ }
+ });
+ this.elementValuesWithDefaults =
+ factory.memoize(
+ new Supplier<ImmutableMap<ExecutableElement, AnnotationValue>>() {
+ @Override
+ public ImmutableMap<ExecutableElement, AnnotationValue> get() {
+ Map<ExecutableElement, AnnotationValue> result = new LinkedHashMap<>();
+ result.putAll(getElementValues());
+ for (MethodInfo method : elements.get().values()) {
+ if (method.defaultValue() == null) {
+ continue;
+ }
+ TurbineExecutableElement element = factory.executableElement(method.sym());
+ if (result.containsKey(element)) {
+ continue;
+ }
+ result.put(element, annotationValue(factory, method.defaultValue()));
+ }
+ return ImmutableMap.copyOf(result);
+ }
+ });
+ }
+
+ @Override
+ public int hashCode() {
+ return anno.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return obj instanceof TurbineAnnotationMirror
+ && anno.equals(((TurbineAnnotationMirror) obj).anno);
+ }
+
+ @Override
+ public String toString() {
+ return anno.toString();
+ }
+
+ @Override
+ public DeclaredType getAnnotationType() {
+ return type.get();
+ }
+
+ public Map<? extends ExecutableElement, ? extends AnnotationValue>
+ getElementValuesWithDefaults() {
+ return elementValuesWithDefaults.get();
+ }
+
+ @Override
+ public Map<? extends ExecutableElement, ? extends AnnotationValue> getElementValues() {
+ return elementValues.get();
+ }
+
+ @Override
+ public AnnotationMirror getValue() {
+ return this;
+ }
+
+ @Override
+ public <R, P> R accept(AnnotationValueVisitor<R, P> v, P p) {
+ return v.visitAnnotation(getValue(), p);
+ }
+
+ public AnnoInfo anno() {
+ return anno;
+ }
+
+ @Override
+ public Const value() {
+ return value;
+ }
+
+ private static class TurbineArrayConstant implements TurbineAnnotationValueMirror {
+
+ private final ArrayInitValue value;
+ private final Supplier<ImmutableList<AnnotationValue>> elements;
+
+ private TurbineArrayConstant(ModelFactory factory, ArrayInitValue value) {
+ this.value = value;
+ this.elements =
+ factory.memoize(
+ new Supplier<ImmutableList<AnnotationValue>>() {
+ @Override
+ public ImmutableList<AnnotationValue> get() {
+ ImmutableList.Builder<AnnotationValue> values = ImmutableList.builder();
+ for (Const element : value.elements()) {
+ values.add(annotationValue(factory, element));
+ }
+ return values.build();
+ }
+ });
+ }
+
+ @Override
+ public List<AnnotationValue> getValue() {
+ return elements.get();
+ }
+
+ @Override
+ public <R, P> R accept(AnnotationValueVisitor<R, P> v, P p) {
+ return v.visitArray(elements.get(), p);
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder("{");
+ Joiner.on(", ").appendTo(sb, elements.get());
+ sb.append("}");
+ return sb.toString();
+ }
+
+ @Override
+ public Const value() {
+ return value;
+ }
+ }
+
+ private static class TurbineClassConstant implements TurbineAnnotationValueMirror {
+
+ private final TurbineClassValue value;
+ private final TypeMirror typeMirror;
+
+ private TurbineClassConstant(ModelFactory factory, TurbineClassValue value) {
+ this.value = value;
+ this.typeMirror = factory.asTypeMirror(value.type());
+ }
+
+ @Override
+ public TypeMirror getValue() {
+ return typeMirror;
+ }
+
+ @Override
+ public <R, P> R accept(AnnotationValueVisitor<R, P> v, P p) {
+ if (value.type().tyKind() == TyKind.ERROR_TY) {
+ // represent unresolvable class literals as the string value "<error>" for compatibility
+ // with javac: https://bugs.openjdk.java.net/browse/JDK-8229535
+ return v.visitString("<error>", p);
+ } else {
+ return v.visitType(getValue(), p);
+ }
+ }
+
+ @Override
+ public String toString() {
+ return typeMirror + ".class";
+ }
+
+ @Override
+ public Const value() {
+ return value;
+ }
+ }
+
+ private static class TurbineEnumConstant implements TurbineAnnotationValueMirror {
+
+ private final EnumConstantValue value;
+ private final TurbineFieldElement fieldElement;
+
+ private TurbineEnumConstant(ModelFactory factory, EnumConstantValue value) {
+ this.value = value;
+ this.fieldElement = factory.fieldElement(value.sym());
+ }
+
+ @Override
+ public Object getValue() {
+ return fieldElement;
+ }
+
+ @Override
+ public <R, P> R accept(AnnotationValueVisitor<R, P> v, P p) {
+ return v.visitEnumConstant(fieldElement, p);
+ }
+
+ @Override
+ public String toString() {
+ return fieldElement.getEnclosingElement() + "." + fieldElement.getSimpleName();
+ }
+
+ @Override
+ public Const value() {
+ return value;
+ }
+ }
+
+ private static class TurbinePrimitiveConstant implements TurbineAnnotationValueMirror {
+
+ private final Const.Value value;
+
+ public TurbinePrimitiveConstant(Const.Value value) {
+ this.value = value;
+ }
+
+ @Override
+ public Object getValue() {
+ return value.getValue();
+ }
+
+ @Override
+ public <R, P> R accept(AnnotationValueVisitor<R, P> v, P p) {
+ return value.accept(v, p);
+ }
+
+ @Override
+ public String toString() {
+ return value.toString();
+ }
+
+ @Override
+ public int hashCode() {
+ return value.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return obj instanceof TurbinePrimitiveConstant
+ && value.equals(((TurbinePrimitiveConstant) obj).value);
+ }
+
+ @Override
+ public Const value() {
+ return value;
+ }
+ }
+}
diff --git a/java/com/google/turbine/processing/TurbineAnnotationProxy.java b/java/com/google/turbine/processing/TurbineAnnotationProxy.java
new file mode 100644
index 0000000..c39f310
--- /dev/null
+++ b/java/com/google/turbine/processing/TurbineAnnotationProxy.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright 2019 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.processing;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+import com.google.turbine.binder.bound.EnumConstantValue;
+import com.google.turbine.binder.bound.TurbineAnnotationValue;
+import com.google.turbine.binder.bound.TurbineClassValue;
+import com.google.turbine.binder.bound.TypeBoundClass;
+import com.google.turbine.model.Const;
+import com.google.turbine.model.Const.ArrayInitValue;
+import com.google.turbine.model.Const.Value;
+import com.google.turbine.type.AnnoInfo;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Array;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.util.ArrayList;
+import java.util.List;
+import javax.lang.model.type.MirroredTypeException;
+import javax.lang.model.type.MirroredTypesException;
+import javax.lang.model.type.TypeMirror;
+
+/** An {@link InvocationHandler} for reflectively accessing annotations. */
+class TurbineAnnotationProxy implements InvocationHandler {
+
+ static <A extends Annotation> A create(
+ ModelFactory factory, Class<A> annotationType, AnnoInfo anno) {
+ ClassLoader loader = annotationType.getClassLoader();
+ if (loader == null) {
+ // annotation was loaded from the system classloader, e.g. java.lang.annotation.*
+ loader = factory.processorLoader();
+ }
+ return annotationType.cast(
+ Proxy.newProxyInstance(
+ loader,
+ new Class<?>[] {annotationType},
+ new TurbineAnnotationProxy(factory, loader, annotationType, anno)));
+ }
+
+ private final ModelFactory factory;
+ private final ClassLoader loader;
+ private final Class<?> annotationType;
+ private final AnnoInfo anno;
+
+ TurbineAnnotationProxy(
+ ModelFactory factory, ClassLoader loader, Class<?> annotationType, AnnoInfo anno) {
+ this.factory = factory;
+ this.loader = loader;
+ this.annotationType = annotationType;
+ this.anno = anno;
+ }
+
+ @Override
+ public Object invoke(Object proxy, Method method, Object[] args) {
+ switch (method.getName()) {
+ case "hashCode":
+ checkArgument(args == null);
+ return anno.hashCode();
+ case "annotationType":
+ checkArgument(args == null);
+ return annotationType;
+ case "equals":
+ checkArgument(args.length == 1);
+ return proxyEquals(args[0]);
+ case "toString":
+ checkArgument(args == null);
+ return anno.toString();
+ default:
+ break;
+ }
+ Const value = anno.values().get(method.getName());
+ if (value != null) {
+ return constValue(method.getReturnType(), factory, loader, value);
+ }
+ for (TypeBoundClass.MethodInfo m : factory.getSymbol(anno.sym()).methods()) {
+ if (m.name().contentEquals(method.getName())) {
+ return constValue(method.getReturnType(), factory, loader, m.defaultValue());
+ }
+ }
+ throw new NoSuchMethodError(method.getName());
+ }
+
+ public boolean proxyEquals(Object other) {
+ if (!annotationType.isInstance(other)) {
+ return false;
+ }
+ if (!Proxy.isProxyClass(other.getClass())) {
+ return false;
+ }
+ InvocationHandler handler = Proxy.getInvocationHandler(other);
+ if (!(handler instanceof TurbineAnnotationProxy)) {
+ return false;
+ }
+ TurbineAnnotationProxy that = (TurbineAnnotationProxy) handler;
+ return anno.equals(that.anno);
+ }
+
+ static Object constValue(
+ Class<?> returnType, ModelFactory factory, ClassLoader loader, Const value) {
+ switch (value.kind()) {
+ case PRIMITIVE:
+ return ((Value) value).getValue();
+ case ARRAY:
+ return constArrayValue(returnType, factory, loader, (Const.ArrayInitValue) value);
+ case ENUM_CONSTANT:
+ return constEnumValue(loader, (EnumConstantValue) value);
+ case ANNOTATION:
+ return constAnnotationValue(factory, loader, (TurbineAnnotationValue) value);
+ case CLASS_LITERAL:
+ return constClassValue(factory, (TurbineClassValue) value);
+ }
+ throw new AssertionError(value.kind());
+ }
+
+ private static Object constArrayValue(
+ Class<?> returnType, ModelFactory factory, ClassLoader loader, ArrayInitValue value) {
+ if (returnType.getComponentType().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());
+ int idx = 0;
+ for (Const element : value.elements()) {
+ Object v = constValue(returnType, factory, loader, element);
+ Array.set(result, idx++, v);
+ }
+ return result;
+ }
+
+ @SuppressWarnings("unchecked") // Enum.class
+ private static Object constEnumValue(ClassLoader loader, EnumConstantValue value) {
+ Class<?> clazz;
+ try {
+ clazz = loader.loadClass(value.sym().owner().toString());
+ } catch (ClassNotFoundException e) {
+ throw new LinkageError(e.getMessage(), e);
+ }
+ return Enum.valueOf(clazz.asSubclass(Enum.class), value.sym().name());
+ }
+
+ private static Object constAnnotationValue(
+ ModelFactory factory, ClassLoader loader, TurbineAnnotationValue value) {
+ try {
+ String name = value.sym().binaryName().replace('/', '.');
+ Class<? extends Annotation> clazz =
+ Class.forName(name, false, loader).asSubclass(Annotation.class);
+ return create(factory, clazz, value.info());
+ } catch (ClassNotFoundException e) {
+ throw new LinkageError(e.getMessage(), e);
+ }
+ }
+
+ private static Object constClassValue(ModelFactory factory, TurbineClassValue value) {
+ throw new MirroredTypeException(factory.asTypeMirror(value.type()));
+ }
+}
diff --git a/java/com/google/turbine/processing/TurbineAnnotationValueMirror.java b/java/com/google/turbine/processing/TurbineAnnotationValueMirror.java
new file mode 100644
index 0000000..a196750
--- /dev/null
+++ b/java/com/google/turbine/processing/TurbineAnnotationValueMirror.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2019 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.processing;
+
+import com.google.turbine.model.Const;
+import javax.lang.model.element.AnnotationValue;
+
+/** An {@link AnnotationValue} backed by a {@link Const}. */
+public interface TurbineAnnotationValueMirror extends AnnotationValue {
+ Const value();
+}
diff --git a/java/com/google/turbine/processing/TurbineElement.java b/java/com/google/turbine/processing/TurbineElement.java
new file mode 100644
index 0000000..c22a442
--- /dev/null
+++ b/java/com/google/turbine/processing/TurbineElement.java
@@ -0,0 +1,1298 @@
+/*
+ * Copyright 2019 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.processing;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.base.Joiner;
+import com.google.common.base.Splitter;
+import com.google.common.base.Supplier;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Sets;
+import com.google.turbine.binder.bound.AnnotationMetadata;
+import com.google.turbine.binder.bound.SourceTypeBoundClass;
+import com.google.turbine.binder.bound.TurbineAnnotationValue;
+import com.google.turbine.binder.bound.TypeBoundClass;
+import com.google.turbine.binder.bound.TypeBoundClass.FieldInfo;
+import com.google.turbine.binder.bound.TypeBoundClass.MethodInfo;
+import com.google.turbine.binder.bound.TypeBoundClass.ParamInfo;
+import com.google.turbine.binder.bound.TypeBoundClass.TyVarInfo;
+import com.google.turbine.binder.lookup.PackageScope;
+import com.google.turbine.binder.sym.ClassSymbol;
+import com.google.turbine.binder.sym.FieldSymbol;
+import com.google.turbine.binder.sym.MethodSymbol;
+import com.google.turbine.binder.sym.PackageSymbol;
+import com.google.turbine.binder.sym.ParamSymbol;
+import com.google.turbine.binder.sym.Symbol;
+import com.google.turbine.binder.sym.TyVarSymbol;
+import com.google.turbine.diag.TurbineError;
+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.MethDecl;
+import com.google.turbine.tree.Tree.TyDecl;
+import com.google.turbine.tree.Tree.VarDecl;
+import com.google.turbine.type.AnnoInfo;
+import com.google.turbine.type.Type;
+import com.google.turbine.type.Type.ClassTy;
+import com.google.turbine.type.Type.ClassTy.SimpleClassTy;
+import com.google.turbine.type.Type.ErrorTy;
+import java.lang.annotation.Annotation;
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Deque;
+import java.util.EnumSet;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.AnnotationValue;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.ElementVisitor;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.Modifier;
+import javax.lang.model.element.Name;
+import javax.lang.model.element.NestingKind;
+import javax.lang.model.element.PackageElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.TypeParameterElement;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.TypeMirror;
+import org.checkerframework.checker.nullness.qual.Nullable;
+
+/** An {@link Element} implementation backed by a {@link Symbol}. */
+public abstract class TurbineElement implements Element {
+
+ public abstract Symbol sym();
+
+ public abstract String javadoc();
+
+ @Override
+ public abstract int hashCode();
+
+ @Override
+ public abstract boolean equals(Object obj);
+
+ protected final ModelFactory factory;
+ private final Supplier<ImmutableList<AnnotationMirror>> annotationMirrors;
+
+ protected <T> Supplier<T> memoize(Supplier<T> supplier) {
+ return factory.memoize(supplier);
+ }
+
+ protected TurbineElement(ModelFactory factory) {
+ this.factory = requireNonNull(factory);
+ this.annotationMirrors =
+ factory.memoize(
+ new Supplier<ImmutableList<AnnotationMirror>>() {
+ @Override
+ public ImmutableList<AnnotationMirror> get() {
+ ImmutableList.Builder<AnnotationMirror> result = ImmutableList.builder();
+ for (AnnoInfo anno : annos()) {
+ result.add(TurbineAnnotationMirror.create(factory, anno));
+ }
+ return result.build();
+ }
+ });
+ }
+
+ static AnnoInfo getAnnotation(Iterable<AnnoInfo> annos, ClassSymbol sym) {
+ for (AnnoInfo anno : annos) {
+ if (Objects.equals(anno.sym(), sym)) {
+ return anno;
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public <A extends Annotation> A getAnnotation(Class<A> annotationType) {
+ ClassSymbol sym = new ClassSymbol(annotationType.getName().replace('.', '/'));
+ TypeBoundClass info = factory.getSymbol(sym);
+ if (info == null) {
+ return null;
+ }
+ AnnoInfo anno = getAnnotation(annos(), sym);
+ if (anno == null) {
+ return null;
+ }
+ return TurbineAnnotationProxy.create(factory, annotationType, anno);
+ }
+
+ @Override
+ public final <A extends Annotation> A[] getAnnotationsByType(Class<A> annotationType) {
+ ClassSymbol sym = new ClassSymbol(annotationType.getName().replace('.', '/'));
+ TypeBoundClass info = factory.getSymbol(sym);
+ if (info == null) {
+ return null;
+ }
+ AnnotationMetadata metadata = info.annotationMetadata();
+ if (metadata == null) {
+ return null;
+ }
+ List<A> result = new ArrayList<>();
+ for (AnnoInfo anno : annos()) {
+ if (anno.sym().equals(sym)) {
+ result.add(TurbineAnnotationProxy.create(factory, annotationType, anno));
+ continue;
+ }
+ if (anno.sym().equals(metadata.repeatable())) {
+ ArrayInitValue arrayValue = (ArrayInitValue) anno.values().get("value");
+ for (Const element : arrayValue.elements()) {
+ result.add(
+ TurbineAnnotationProxy.create(
+ factory, annotationType, ((TurbineAnnotationValue) element).info()));
+ }
+ }
+ }
+ return Iterables.toArray(result, annotationType);
+ }
+
+ @Override
+ public final List<? extends AnnotationMirror> getAnnotationMirrors() {
+ return annotationMirrors.get();
+ }
+
+ List<? extends AnnotationMirror> getAllAnnotationMirrors() {
+ return getAnnotationMirrors();
+ }
+
+ protected abstract ImmutableList<AnnoInfo> annos();
+
+ /** A {@link TypeElement} implementation backed by a {@link ClassSymbol}. */
+ static class TurbineTypeElement extends TurbineElement implements TypeElement {
+
+ @Override
+ public int hashCode() {
+ return sym.hashCode();
+ }
+
+ private final ClassSymbol sym;
+ private final Supplier<TypeBoundClass> info;
+
+ TurbineTypeElement(ModelFactory factory, ClassSymbol sym) {
+ super(factory);
+ this.sym = requireNonNull(sym);
+ this.info =
+ memoize(
+ new Supplier<TypeBoundClass>() {
+ @Override
+ public TypeBoundClass get() {
+ return factory.getSymbol(sym);
+ }
+ });
+ }
+
+ @Nullable
+ TypeBoundClass info() {
+ return info.get();
+ }
+
+ TypeBoundClass infoNonNull() {
+ TypeBoundClass info = info();
+ if (info == null) {
+ throw TurbineError.format(/* source= */ null, ErrorKind.SYMBOL_NOT_FOUND, sym);
+ }
+ return info;
+ }
+
+ @Override
+ public NestingKind getNestingKind() {
+ TypeBoundClass info = info();
+ return (info != null && info.owner() != null) ? NestingKind.MEMBER : NestingKind.TOP_LEVEL;
+ }
+
+ private final Supplier<TurbineName> qualifiedName =
+ memoize(
+ new Supplier<TurbineName>() {
+ @Override
+ public TurbineName get() {
+ TypeBoundClass info = info();
+ if (info == null || info.owner() == null) {
+ return new TurbineName(sym.toString());
+ }
+ ClassSymbol sym = sym();
+ Deque<String> flat = new ArrayDeque<>();
+ while (info.owner() != null) {
+ flat.addFirst(sym.binaryName().substring(info.owner().binaryName().length() + 1));
+ sym = info.owner();
+ info = factory.getSymbol(sym);
+ }
+ flat.addFirst(sym.toString());
+ return new TurbineName(Joiner.on('.').join(flat));
+ }
+ });
+
+ @Override
+ public Name getQualifiedName() {
+ return qualifiedName.get();
+ }
+
+ private final Supplier<TypeMirror> superclass =
+ memoize(
+ new Supplier<TypeMirror>() {
+ @Override
+ public TypeMirror get() {
+ TypeBoundClass info = infoNonNull();
+ switch (info.kind()) {
+ case CLASS:
+ case ENUM:
+ if (info.superclass() != null) {
+ return factory.asTypeMirror(info.superClassType());
+ }
+ if (info instanceof SourceTypeBoundClass) {
+ // support simple name 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()));
+ }
+ }
+ return factory.noType();
+ case INTERFACE:
+ case ANNOTATION:
+ return factory.noType();
+ }
+ throw new AssertionError(info.kind());
+ }
+ });
+
+ @Override
+ public TypeMirror getSuperclass() {
+ return superclass.get();
+ }
+
+ @Override
+ public String toString() {
+ return getQualifiedName().toString();
+ }
+
+ private final Supplier<List<TypeMirror>> interfaces =
+ memoize(
+ new Supplier<List<TypeMirror>>() {
+ @Override
+ public List<TypeMirror> get() {
+ return factory.asTypeMirrors(infoNonNull().interfaceTypes());
+ }
+ });
+
+ @Override
+ public List<? extends TypeMirror> getInterfaces() {
+ return interfaces.get();
+ }
+
+ private final Supplier<ImmutableList<TypeParameterElement>> typeParameters =
+ memoize(
+ new Supplier<ImmutableList<TypeParameterElement>>() {
+ @Override
+ public ImmutableList<TypeParameterElement> get() {
+ ImmutableList.Builder<TypeParameterElement> result = ImmutableList.builder();
+ for (TyVarSymbol p : infoNonNull().typeParameters().values()) {
+ result.add(factory.typeParameterElement(p));
+ }
+ return result.build();
+ }
+ });
+
+ @Override
+ public List<? extends TypeParameterElement> getTypeParameters() {
+ return typeParameters.get();
+ }
+
+ private final Supplier<TypeMirror> type =
+ memoize(
+ new Supplier<TypeMirror>() {
+ @Override
+ public TypeMirror get() {
+ return factory.asTypeMirror(asGenericType(sym));
+ }
+
+ ClassTy asGenericType(ClassSymbol symbol) {
+ TypeBoundClass info = info();
+ if (info == null) {
+ return ClassTy.asNonParametricClassTy(symbol);
+ }
+ Deque<Type.ClassTy.SimpleClassTy> simples = new ArrayDeque<>();
+ simples.addFirst(simple(symbol, info));
+ while (info.owner() != null && (info.access() & TurbineFlag.ACC_STATIC) == 0) {
+ symbol = info.owner();
+ info = factory.getSymbol(symbol);
+ simples.addFirst(simple(symbol, info));
+ }
+ return ClassTy.create(ImmutableList.copyOf(simples));
+ }
+
+ private SimpleClassTy simple(ClassSymbol sym, TypeBoundClass info) {
+ ImmutableList.Builder<Type> args = ImmutableList.builder();
+ for (TyVarSymbol t : info.typeParameters().values()) {
+ args.add(Type.TyVar.create(t, ImmutableList.of()));
+ }
+ return SimpleClassTy.create(sym, args.build(), ImmutableList.of());
+ }
+ });
+
+ @Override
+ public TypeMirror asType() {
+ return type.get();
+ }
+
+ @Override
+ public ElementKind getKind() {
+ TypeBoundClass info = infoNonNull();
+ switch (info.kind()) {
+ case CLASS:
+ return ElementKind.CLASS;
+ case INTERFACE:
+ return ElementKind.INTERFACE;
+ case ENUM:
+ return ElementKind.ENUM;
+ case ANNOTATION:
+ return ElementKind.ANNOTATION_TYPE;
+ }
+ throw new AssertionError(info.kind());
+ }
+
+ @Override
+ public Set<Modifier> getModifiers() {
+ return asModifierSet(ModifierOwner.TYPE, infoNonNull().access() & ~TurbineFlag.ACC_SUPER);
+ }
+
+ private final Supplier<TurbineName> simpleName =
+ memoize(
+ new Supplier<TurbineName>() {
+ @Override
+ public TurbineName get() {
+ TypeBoundClass info = info();
+ if (info == null || info.owner() == null) {
+ return new TurbineName(sym.simpleName());
+ }
+ return new TurbineName(
+ sym.binaryName().substring(info.owner().binaryName().length() + 1));
+ }
+ });
+
+ @Override
+ public Name getSimpleName() {
+ return simpleName.get();
+ }
+
+ private final Supplier<Element> enclosing =
+ memoize(
+ new Supplier<Element>() {
+ @Override
+ public Element get() {
+ return getNestingKind().equals(NestingKind.TOP_LEVEL)
+ ? factory.packageElement(sym.owner())
+ : factory.typeElement(info().owner());
+ }
+ });
+
+ @Override
+ public Element getEnclosingElement() {
+ return enclosing.get();
+ }
+
+ private final Supplier<ImmutableList<Element>> enclosed =
+ memoize(
+ new Supplier<ImmutableList<Element>>() {
+ @Override
+ public ImmutableList<Element> get() {
+ TypeBoundClass info = infoNonNull();
+ ImmutableList.Builder<Element> result = ImmutableList.builder();
+ for (FieldInfo field : info.fields()) {
+ result.add(factory.fieldElement(field.sym()));
+ }
+ for (MethodInfo method : info.methods()) {
+ result.add(factory.executableElement(method.sym()));
+ }
+ for (ClassSymbol child : info.children().values()) {
+ result.add(factory.typeElement(child));
+ }
+ return result.build();
+ }
+ });
+
+ @Override
+ public List<? extends Element> getEnclosedElements() {
+ return enclosed.get();
+ }
+
+ @Override
+ public <R, P> R accept(ElementVisitor<R, P> v, P p) {
+ return v.visitType(this, p);
+ }
+
+ @Override
+ public ClassSymbol sym() {
+ return sym;
+ }
+
+ @Override
+ public String javadoc() {
+ TypeBoundClass info = info();
+ if (!(info instanceof SourceTypeBoundClass)) {
+ return null;
+ }
+ return ((SourceTypeBoundClass) info).decl().javadoc();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return obj instanceof TurbineTypeElement && sym.equals(((TurbineTypeElement) obj).sym);
+ }
+
+ @Override
+ protected ImmutableList<AnnoInfo> annos() {
+ return infoNonNull().annotations();
+ }
+
+ @Override
+ public final <A extends Annotation> A getAnnotation(Class<A> annotationType) {
+ ClassSymbol sym = new ClassSymbol(annotationType.getName().replace('.', '/'));
+ AnnoInfo anno = getAnnotation(annos(), sym);
+ if (anno != null) {
+ return TurbineAnnotationProxy.create(factory, annotationType, anno);
+ }
+ if (!isAnnotationInherited(sym)) {
+ return null;
+ }
+ ClassSymbol superclass = infoNonNull().superclass();
+ while (superclass != null) {
+ TypeBoundClass info = factory.getSymbol(superclass);
+ if (info == null) {
+ break;
+ }
+ anno = getAnnotation(info.annotations(), sym);
+ if (anno != null) {
+ return TurbineAnnotationProxy.create(factory, annotationType, anno);
+ }
+ superclass = info.superclass();
+ }
+ return null;
+ }
+
+ @Override
+ List<? extends AnnotationMirror> getAllAnnotationMirrors() {
+ Map<ClassSymbol, AnnotationMirror> result = new LinkedHashMap<>();
+ for (AnnoInfo anno : annos()) {
+ result.put(anno.sym(), TurbineAnnotationMirror.create(factory, anno));
+ }
+ ClassSymbol superclass = infoNonNull().superclass();
+ while (superclass != null) {
+ TypeBoundClass i = factory.getSymbol(superclass);
+ if (i == null) {
+ break;
+ }
+ for (AnnoInfo anno : i.annotations()) {
+ addAnnotationFromSuper(result, anno);
+ }
+ superclass = i.superclass();
+ }
+ return ImmutableList.copyOf(result.values());
+ }
+
+ private void addAnnotationFromSuper(Map<ClassSymbol, AnnotationMirror> result, AnnoInfo anno) {
+ if (!isAnnotationInherited(anno.sym())) {
+ return;
+ }
+ if (result.containsKey(anno.sym())) {
+ // if the same inherited annotation is present on multiple supertypes, only return one
+ return;
+ }
+ result.put(anno.sym(), TurbineAnnotationMirror.create(factory, anno));
+ }
+
+ private boolean isAnnotationInherited(ClassSymbol sym) {
+ TypeBoundClass annoInfo = factory.getSymbol(sym);
+ if (annoInfo == null) {
+ return false;
+ }
+ for (AnnoInfo anno : annoInfo.annotations()) {
+ if (anno.sym().equals(ClassSymbol.INHERITED)) {
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+
+ /** A {@link TypeParameterElement} implementation backed by a {@link TyVarSymbol}. */
+ static class TurbineTypeParameterElement extends TurbineElement implements TypeParameterElement {
+
+ @Override
+ public int hashCode() {
+ return sym.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return obj instanceof TurbineTypeParameterElement
+ && sym.equals(((TurbineTypeParameterElement) obj).sym);
+ }
+
+ private final TyVarSymbol sym;
+
+ public TurbineTypeParameterElement(ModelFactory factory, TyVarSymbol sym) {
+ super(factory);
+ this.sym = sym;
+ }
+
+ private final Supplier<TyVarInfo> info =
+ memoize(
+ new Supplier<TyVarInfo>() {
+ @Override
+ public TyVarInfo get() {
+ return factory.getTyVarInfo(sym);
+ }
+ });
+
+ @Nullable
+ private TyVarInfo info() {
+ return info.get();
+ }
+
+ @Override
+ public String toString() {
+ return sym.name();
+ }
+
+ @Override
+ public Element getGenericElement() {
+ return factory.element(sym.owner());
+ }
+
+ @Override
+ public List<? extends TypeMirror> getBounds() {
+ ImmutableList<Type> bounds = info().upperBound().bounds();
+ return factory.asTypeMirrors(bounds.isEmpty() ? ImmutableList.of(ClassTy.OBJECT) : bounds);
+ }
+
+ @Override
+ public TypeMirror asType() {
+ return factory.asTypeMirror(Type.TyVar.create(sym, info().annotations()));
+ }
+
+ @Override
+ public ElementKind getKind() {
+ return ElementKind.TYPE_PARAMETER;
+ }
+
+ @Override
+ public Set<Modifier> getModifiers() {
+ return ImmutableSet.of();
+ }
+
+ @Override
+ public Name getSimpleName() {
+ return new TurbineName(sym.name());
+ }
+
+ @Override
+ public Element getEnclosingElement() {
+ return getGenericElement();
+ }
+
+ @Override
+ public List<? extends Element> getEnclosedElements() {
+ return ImmutableList.of();
+ }
+
+ @Override
+ public <R, P> R accept(ElementVisitor<R, P> v, P p) {
+ return v.visitTypeParameter(this, p);
+ }
+
+ @Override
+ public TyVarSymbol sym() {
+ return sym;
+ }
+
+ @Override
+ public String javadoc() {
+ return null;
+ }
+
+ @Override
+ protected ImmutableList<AnnoInfo> annos() {
+ return info().annotations();
+ }
+ }
+
+ /** An {@link ExecutableElement} implementation backed by a {@link MethodSymbol}. */
+ static class TurbineExecutableElement extends TurbineElement implements ExecutableElement {
+
+ private final MethodSymbol sym;
+
+ private final Supplier<MethodInfo> info =
+ memoize(
+ new Supplier<MethodInfo>() {
+ @Override
+ public MethodInfo get() {
+ return factory.getMethodInfo(sym);
+ }
+ });
+
+ @Nullable
+ MethodInfo info() {
+ return info.get();
+ }
+
+ TurbineExecutableElement(ModelFactory factory, MethodSymbol sym) {
+ super(factory);
+ this.sym = sym;
+ }
+
+ @Override
+ public MethodSymbol sym() {
+ return sym;
+ }
+
+ @Override
+ public String javadoc() {
+ MethDecl decl = info().decl();
+ return decl != null ? decl.javadoc() : null;
+ }
+
+ @Override
+ public int hashCode() {
+ return sym.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return obj instanceof TurbineExecutableElement
+ && sym.equals(((TurbineExecutableElement) obj).sym);
+ }
+
+ @Override
+ public List<? extends TypeParameterElement> getTypeParameters() {
+ ImmutableList.Builder<TurbineTypeParameterElement> result = ImmutableList.builder();
+ for (Map.Entry<TyVarSymbol, TyVarInfo> p : info().tyParams().entrySet()) {
+ result.add(factory.typeParameterElement(p.getKey()));
+ }
+ return result.build();
+ }
+
+ @Override
+ public TypeMirror getReturnType() {
+ return factory.asTypeMirror(info().returnType());
+ }
+
+ private final Supplier<ImmutableList<VariableElement>> parameters =
+ memoize(
+ new Supplier<ImmutableList<VariableElement>>() {
+ @Override
+ public ImmutableList<VariableElement> get() {
+ ImmutableList.Builder<VariableElement> result = ImmutableList.builder();
+ for (ParamInfo param : info().parameters()) {
+ if (param.synthetic()) {
+ // ExecutableElement#getParameters doesn't expect synthetic or mandated
+ // parameters
+ continue;
+ }
+ result.add(factory.parameterElement(param.sym()));
+ }
+ return result.build();
+ }
+ });
+
+ @Override
+ public List<? extends VariableElement> getParameters() {
+ return parameters.get();
+ }
+
+ @Override
+ public String toString() {
+ MethodInfo info = info();
+ StringBuilder sb = new StringBuilder();
+ if (!info.tyParams().isEmpty()) {
+ sb.append('<');
+ Joiner.on(',').appendTo(sb, info.tyParams().keySet());
+ sb.append('>');
+ }
+ if (getKind() == ElementKind.CONSTRUCTOR) {
+ sb.append(info.sym().owner().simpleName());
+ } else {
+ sb.append(info.sym().name());
+ }
+ sb.append('(');
+ boolean first = true;
+ for (ParamInfo p : info.parameters()) {
+ if (!first) {
+ sb.append(',');
+ }
+ sb.append(p.type());
+ first = false;
+ }
+ sb.append(')');
+ return sb.toString();
+ }
+
+ @Override
+ public TypeMirror getReceiverType() {
+ return info().receiver() != null
+ ? factory.asTypeMirror(info().receiver().type())
+ : factory.noType();
+ }
+
+ @Override
+ public boolean isVarArgs() {
+ return (info().access() & TurbineFlag.ACC_VARARGS) == TurbineFlag.ACC_VARARGS;
+ }
+
+ @Override
+ public boolean isDefault() {
+ return (info().access() & TurbineFlag.ACC_DEFAULT) == TurbineFlag.ACC_DEFAULT;
+ }
+
+ @Override
+ public List<? extends TypeMirror> getThrownTypes() {
+ return factory.asTypeMirrors(info().exceptions());
+ }
+
+ @Override
+ public AnnotationValue getDefaultValue() {
+ return info().defaultValue() != null
+ ? TurbineAnnotationMirror.annotationValue(factory, info().defaultValue())
+ : null;
+ }
+
+ @Override
+ public TypeMirror asType() {
+ return factory.asTypeMirror(info().asType());
+ }
+
+ @Override
+ public ElementKind getKind() {
+ return info().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);
+ }
+
+ @Override
+ public Name getSimpleName() {
+ return new TurbineName(info().sym().name());
+ }
+
+ @Override
+ public Element getEnclosingElement() {
+ return factory.typeElement(info().sym().owner());
+ }
+
+ @Override
+ public List<? extends Element> getEnclosedElements() {
+ return ImmutableList.of();
+ }
+
+ @Override
+ public <R, P> R accept(ElementVisitor<R, P> v, P p) {
+ return v.visitExecutable(this, p);
+ }
+
+ @Override
+ protected ImmutableList<AnnoInfo> annos() {
+ return info().annotations();
+ }
+ }
+
+ /** An {@link VariableElement} implementation backed by a {@link FieldSymbol}. */
+ static class TurbineFieldElement extends TurbineElement implements VariableElement {
+
+ @Override
+ public String toString() {
+ return sym.name();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return obj instanceof TurbineFieldElement && sym.equals(((TurbineFieldElement) obj).sym);
+ }
+
+ @Override
+ public int hashCode() {
+ return sym.hashCode();
+ }
+
+ private final FieldSymbol sym;
+
+ @Override
+ public FieldSymbol sym() {
+ return sym;
+ }
+
+ @Override
+ public String javadoc() {
+ VarDecl decl = info().decl();
+ return decl != null ? decl.javadoc() : null;
+ }
+
+ private final Supplier<FieldInfo> info =
+ memoize(
+ new Supplier<FieldInfo>() {
+ @Override
+ public FieldInfo get() {
+ return factory.getFieldInfo(sym);
+ }
+ });
+
+ @Nullable
+ FieldInfo info() {
+ return info.get();
+ }
+
+ TurbineFieldElement(ModelFactory factory, FieldSymbol sym) {
+ super(factory);
+ this.sym = sym;
+ }
+
+ @Override
+ public Object getConstantValue() {
+ if (info().value() == null) {
+ return null;
+ }
+ return info().value().getValue();
+ }
+
+ @Override
+ public TypeMirror asType() {
+ return factory.asTypeMirror(info().type());
+ }
+
+ @Override
+ public ElementKind getKind() {
+ return ((info().access() & TurbineFlag.ACC_ENUM) == TurbineFlag.ACC_ENUM)
+ ? ElementKind.ENUM_CONSTANT
+ : ElementKind.FIELD;
+ }
+
+ @Override
+ public Set<Modifier> getModifiers() {
+ return asModifierSet(ModifierOwner.FIELD, info().access());
+ }
+
+ @Override
+ public Name getSimpleName() {
+ return new TurbineName(sym.name());
+ }
+
+ @Override
+ public Element getEnclosingElement() {
+ return factory.typeElement(sym.owner());
+ }
+
+ @Override
+ public List<? extends Element> getEnclosedElements() {
+ return ImmutableList.of();
+ }
+
+ @Override
+ public <R, P> R accept(ElementVisitor<R, P> v, P p) {
+ return v.visitVariable(this, p);
+ }
+
+ @Override
+ protected ImmutableList<AnnoInfo> annos() {
+ return info().annotations();
+ }
+ }
+
+ private enum ModifierOwner {
+ TYPE,
+ PARAMETER,
+ FIELD,
+ METHOD
+ }
+
+ private static ImmutableSet<Modifier> asModifierSet(ModifierOwner modifierOwner, int access) {
+ EnumSet<Modifier> modifiers = EnumSet.noneOf(Modifier.class);
+ if ((access & TurbineFlag.ACC_PUBLIC) == TurbineFlag.ACC_PUBLIC) {
+ modifiers.add(Modifier.PUBLIC);
+ }
+ if ((access & TurbineFlag.ACC_PROTECTED) == TurbineFlag.ACC_PROTECTED) {
+ modifiers.add(Modifier.PROTECTED);
+ }
+ if ((access & TurbineFlag.ACC_PRIVATE) == TurbineFlag.ACC_PRIVATE) {
+ modifiers.add(Modifier.PRIVATE);
+ }
+ if ((access & TurbineFlag.ACC_ABSTRACT) == TurbineFlag.ACC_ABSTRACT) {
+ modifiers.add(Modifier.ABSTRACT);
+ }
+ if ((access & TurbineFlag.ACC_FINAL) == TurbineFlag.ACC_FINAL) {
+ modifiers.add(Modifier.FINAL);
+ }
+ if ((access & TurbineFlag.ACC_DEFAULT) == TurbineFlag.ACC_DEFAULT) {
+ modifiers.add(Modifier.DEFAULT);
+ }
+ if ((access & TurbineFlag.ACC_STATIC) == TurbineFlag.ACC_STATIC) {
+ modifiers.add(Modifier.STATIC);
+ }
+ if ((access & TurbineFlag.ACC_TRANSIENT) == TurbineFlag.ACC_TRANSIENT) {
+ switch (modifierOwner) {
+ case METHOD:
+ case PARAMETER:
+ // varargs and transient use the same bits
+ break;
+ default:
+ modifiers.add(Modifier.TRANSIENT);
+ }
+ }
+ if ((access & TurbineFlag.ACC_VOLATILE) == TurbineFlag.ACC_VOLATILE) {
+ modifiers.add(Modifier.VOLATILE);
+ }
+ if ((access & TurbineFlag.ACC_SYNCHRONIZED) == TurbineFlag.ACC_SYNCHRONIZED) {
+ modifiers.add(Modifier.SYNCHRONIZED);
+ }
+ if ((access & TurbineFlag.ACC_NATIVE) == TurbineFlag.ACC_NATIVE) {
+ modifiers.add(Modifier.NATIVE);
+ }
+ if ((access & TurbineFlag.ACC_STRICT) == TurbineFlag.ACC_STRICT) {
+ modifiers.add(Modifier.STRICTFP);
+ }
+
+ return Sets.immutableEnumSet(modifiers);
+ }
+
+ /** A {@link PackageElement} implementation backed by a {@link PackageSymbol}. */
+ static class TurbinePackageElement extends TurbineElement implements PackageElement {
+
+ private final PackageSymbol sym;
+
+ public TurbinePackageElement(ModelFactory factory, PackageSymbol sym) {
+ super(factory);
+ this.sym = sym;
+ }
+
+ @Override
+ public Name getQualifiedName() {
+ return new TurbineName(sym.toString());
+ }
+
+ @Override
+ public boolean isUnnamed() {
+ return sym.binaryName().isEmpty();
+ }
+
+ @Override
+ public TypeMirror asType() {
+ return factory.packageType(sym);
+ }
+
+ @Override
+ public ElementKind getKind() {
+ return ElementKind.PACKAGE;
+ }
+
+ @Override
+ public Set<Modifier> getModifiers() {
+ return ImmutableSet.of();
+ }
+
+ @Override
+ public Name getSimpleName() {
+ return new TurbineName(sym.binaryName().substring(sym.binaryName().lastIndexOf('/') + 1));
+ }
+
+ @Override
+ public Element getEnclosingElement() {
+ // a package is not enclosed by another element
+ return null;
+ }
+
+ @Override
+ public List<TurbineTypeElement> getEnclosedElements() {
+ ImmutableSet.Builder<TurbineTypeElement> result = ImmutableSet.builder();
+ PackageScope scope = factory.tli().lookupPackage(Splitter.on('/').split(sym.binaryName()));
+ for (ClassSymbol key : scope.classes()) {
+ if (key.binaryName().contains("$") && factory.getSymbol(key).owner() != null) {
+ // Skip member classes: only top-level classes are enclosed by the package.
+ // The initial check for '$' is an optimization.
+ continue;
+ }
+ if (key.simpleName().equals("package-info")) {
+ continue;
+ }
+ result.add(factory.typeElement(key));
+ }
+ return result.build().asList();
+ }
+
+ @Override
+ public <R, P> R accept(ElementVisitor<R, P> v, P p) {
+ return v.visitPackage(this, p);
+ }
+
+ @Override
+ public PackageSymbol sym() {
+ return sym;
+ }
+
+ @Override
+ public String javadoc() {
+ return null;
+ }
+
+ @Override
+ public int hashCode() {
+ return sym.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return obj instanceof TurbinePackageElement && sym.equals(((TurbinePackageElement) obj).sym);
+ }
+
+ private final Supplier<ImmutableList<AnnoInfo>> annos =
+ memoize(
+ new Supplier<ImmutableList<AnnoInfo>>() {
+ @Override
+ public ImmutableList<AnnoInfo> get() {
+ TypeBoundClass info =
+ factory.getSymbol(new ClassSymbol(sym.binaryName() + "/package-info"));
+ return info != null ? info.annotations() : ImmutableList.of();
+ }
+ });
+
+ @Override
+ protected ImmutableList<AnnoInfo> annos() {
+ return annos.get();
+ }
+
+ @Override
+ public String toString() {
+ return sym.toString();
+ }
+ }
+
+ /** A {@link VariableElement} implementation backed by a {@link ParamSymbol}. */
+ static class TurbineParameterElement extends TurbineElement implements VariableElement {
+
+ @Override
+ public ParamSymbol sym() {
+ return sym;
+ }
+
+ @Override
+ public String javadoc() {
+ return null;
+ }
+
+ @Override
+ public int hashCode() {
+ return sym.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return obj instanceof TurbineParameterElement
+ && sym.equals(((TurbineParameterElement) obj).sym);
+ }
+
+ private final ParamSymbol sym;
+
+ private final Supplier<ParamInfo> info =
+ memoize(
+ new Supplier<ParamInfo>() {
+ @Override
+ public ParamInfo get() {
+ return factory.getParamInfo(sym);
+ }
+ });
+
+ @Nullable
+ ParamInfo info() {
+ return info.get();
+ }
+
+ public TurbineParameterElement(ModelFactory factory, ParamSymbol sym) {
+ super(factory);
+ this.sym = sym;
+ }
+
+ @Override
+ public Object getConstantValue() {
+ return null;
+ }
+
+ private final Supplier<TypeMirror> type =
+ memoize(
+ new Supplier<TypeMirror>() {
+ @Override
+ public TypeMirror get() {
+ return factory.asTypeMirror(info().type());
+ }
+ });
+
+ @Override
+ public TypeMirror asType() {
+ return type.get();
+ }
+
+ @Override
+ public ElementKind getKind() {
+ return ElementKind.PARAMETER;
+ }
+
+ @Override
+ public Set<Modifier> getModifiers() {
+ return asModifierSet(ModifierOwner.PARAMETER, info().access());
+ }
+
+ @Override
+ public Name getSimpleName() {
+ return new TurbineName(sym.name());
+ }
+
+ @Override
+ public Element getEnclosingElement() {
+ return factory.executableElement(sym.owner());
+ }
+
+ @Override
+ public List<? extends Element> getEnclosedElements() {
+ return ImmutableList.of();
+ }
+
+ @Override
+ public <R, P> R accept(ElementVisitor<R, P> v, P p) {
+ return v.visitVariable(this, p);
+ }
+
+ @Override
+ public String toString() {
+ return String.valueOf(sym.name());
+ }
+
+ @Override
+ protected ImmutableList<AnnoInfo> annos() {
+ return info().annotations();
+ }
+ }
+
+ static class TurbineNoTypeElement implements TypeElement {
+
+ private final ModelFactory factory;
+ private final String name;
+
+ public TurbineNoTypeElement(ModelFactory factory, String name) {
+ this.factory = factory;
+ this.name = requireNonNull(name);
+ }
+
+ @Override
+ public TypeMirror asType() {
+ return factory.noType();
+ }
+
+ @Override
+ public ElementKind getKind() {
+ return ElementKind.CLASS;
+ }
+
+ @Override
+ public Set<Modifier> getModifiers() {
+ return ImmutableSet.of();
+ }
+
+ @Override
+ public Name getSimpleName() {
+ return new TurbineName(name.substring(name.lastIndexOf('.') + 1));
+ }
+
+ @Override
+ public TypeMirror getSuperclass() {
+ return factory.noType();
+ }
+
+ @Override
+ public List<? extends TypeMirror> getInterfaces() {
+ return ImmutableList.of();
+ }
+
+ @Override
+ public List<? extends TypeParameterElement> getTypeParameters() {
+ return ImmutableList.of();
+ }
+
+ @Override
+ public Element getEnclosingElement() {
+ int idx = name.lastIndexOf('.');
+ String packageName;
+ if (idx == -1) {
+ packageName = "";
+ } else {
+ packageName = name.substring(0, idx).replace('.', '/');
+ }
+ return factory.packageElement(new PackageSymbol(packageName));
+ }
+
+ @Override
+ public List<? extends Element> getEnclosedElements() {
+ return ImmutableList.of();
+ }
+
+ @Override
+ public NestingKind getNestingKind() {
+ return NestingKind.TOP_LEVEL;
+ }
+
+ @Override
+ public Name getQualifiedName() {
+ return new TurbineName(name);
+ }
+
+ @Override
+ public List<? extends AnnotationMirror> getAnnotationMirrors() {
+ return ImmutableList.of();
+ }
+
+ @Override
+ public <A extends Annotation> A getAnnotation(Class<A> aClass) {
+ return null;
+ }
+
+ @Override
+ public <A extends Annotation> A[] getAnnotationsByType(Class<A> aClass) {
+ return null;
+ }
+
+ @Override
+ public <R, P> R accept(ElementVisitor<R, P> elementVisitor, P p) {
+ return elementVisitor.visitType(this, p);
+ }
+
+ @Override
+ public String toString() {
+ return getSimpleName().toString();
+ }
+ }
+}
diff --git a/java/com/google/turbine/processing/TurbineElements.java b/java/com/google/turbine/processing/TurbineElements.java
new file mode 100644
index 0000000..9da210e
--- /dev/null
+++ b/java/com/google/turbine/processing/TurbineElements.java
@@ -0,0 +1,360 @@
+/*
+ * Copyright 2019 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.processing;
+
+import static com.google.common.base.Preconditions.checkState;
+
+import com.google.common.base.CharMatcher;
+import com.google.common.base.Joiner;
+import com.google.common.base.Splitter;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Multimap;
+import com.google.common.collect.MultimapBuilder;
+import com.google.turbine.binder.sym.ClassSymbol;
+import com.google.turbine.binder.sym.FieldSymbol;
+import com.google.turbine.binder.sym.PackageSymbol;
+import com.google.turbine.binder.sym.Symbol;
+import com.google.turbine.model.Const;
+import com.google.turbine.model.TurbineVisibility;
+import com.google.turbine.processing.TurbineElement.TurbineExecutableElement;
+import com.google.turbine.processing.TurbineElement.TurbineFieldElement;
+import com.google.turbine.processing.TurbineElement.TurbineTypeElement;
+import com.google.turbine.processing.TurbineTypeMirror.TurbineExecutableType;
+import com.google.turbine.type.AnnoInfo;
+import java.io.PrintWriter;
+import java.io.Writer;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.AnnotationValue;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.Name;
+import javax.lang.model.element.PackageElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.util.Elements;
+
+/** An implementation of {@link Elements} backed by turbine's {@link Element}. */
+public class TurbineElements implements Elements {
+
+ private final ModelFactory factory;
+ private final TurbineTypes types;
+
+ public TurbineElements(ModelFactory factory, TurbineTypes types) {
+ this.factory = factory;
+ this.types = types;
+ }
+
+ private static Symbol asSymbol(Element element) {
+ if (!(element instanceof TurbineElement)) {
+ throw new IllegalArgumentException(element.toString());
+ }
+ return ((TurbineElement) element).sym();
+ }
+
+ @Override
+ public PackageElement getPackageElement(CharSequence name) {
+ ImmutableList<String> packageName = ImmutableList.copyOf(Splitter.on('.').split(name));
+ if (factory.tli().lookupPackage(packageName) == null) {
+ return null;
+ }
+ return factory.packageElement(new PackageSymbol(Joiner.on('/').join(packageName)));
+ }
+
+ @Override
+ public TypeElement getTypeElement(CharSequence name) {
+ ClassSymbol sym = factory.inferSymbol(name);
+ if (sym == null) {
+ return null;
+ }
+ if (factory.getSymbol(sym) == null) {
+ return null;
+ }
+ return factory.typeElement(sym);
+ }
+
+ @Override
+ public Map<? extends ExecutableElement, ? extends AnnotationValue> getElementValuesWithDefaults(
+ AnnotationMirror a) {
+ return ((TurbineAnnotationMirror) a).getElementValuesWithDefaults();
+ }
+
+ @Override
+ public String getDocComment(Element e) {
+ if (!(e instanceof TurbineElement)) {
+ throw new IllegalArgumentException(e.toString());
+ }
+ String comment = ((TurbineElement) e).javadoc();
+ if (comment == null) {
+ return null;
+ }
+ StringBuilder sb = new StringBuilder();
+ boolean first = true;
+ for (String line : Splitter.on('\n').split(comment)) {
+ int start = 0;
+ if (!first) {
+ sb.append('\n');
+ while (start < line.length() && CharMatcher.whitespace().matches(line.charAt(start))) {
+ start++;
+ }
+ while (start < line.length() && line.charAt(start) == '*') {
+ start++;
+ }
+ }
+ sb.append(line, start, line.length());
+ first = false;
+ }
+ return sb.toString();
+ }
+
+ @Override
+ public boolean isDeprecated(Element element) {
+ if (!(element instanceof TurbineElement)) {
+ throw new IllegalArgumentException(element.toString());
+ }
+ for (AnnoInfo a : ((TurbineTypeElement) element).annos()) {
+ if (a.sym().equals(ClassSymbol.DEPRECATED)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public Name getBinaryName(TypeElement element) {
+ if (!(element instanceof TurbineTypeElement)) {
+ throw new IllegalArgumentException(element.toString());
+ }
+ return getName(((TurbineTypeElement) element).sym().binaryName().replace('/', '.'));
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @throws IllegalArgumentException for module elements
+ */
+ @Override
+ public PackageElement getPackageOf(Element element) {
+ Symbol sym = asSymbol(element);
+ return factory.packageElement(packageSymbol(sym));
+ }
+
+ private static PackageSymbol packageSymbol(Symbol sym) {
+ if (sym.symKind().equals(Symbol.Kind.PACKAGE)) {
+ return (PackageSymbol) sym;
+ }
+ return ModelFactory.enclosingClass(sym).owner();
+ }
+
+ @Override
+ public List<? extends Element> getAllMembers(TypeElement type) {
+ ClassSymbol s = (ClassSymbol) asSymbol(type);
+ PackageSymbol from = packageSymbol(s);
+
+ // keep track of processed methods grouped by their names, to handle overrides more efficiently
+ Multimap<String, TurbineExecutableElement> methods =
+ MultimapBuilder.linkedHashKeys().linkedHashSetValues().build();
+
+ // collect all members of each transitive supertype of the input
+ ImmutableList.Builder<Element> results = ImmutableList.builder();
+ for (ClassSymbol superType : factory.cha().transitiveSupertypes(s)) {
+ // Most of JSR-269 is implemented on top of turbine's model, instead of the Element and
+ // TypeMirror wrappers. We don't do that here because we need most of the Elements returned
+ // by getEnclosedElements anyways, and the work below benefits from some of the caching done
+ // by TurbineElement.
+ for (Element el : factory.typeElement(superType).getEnclosedElements()) {
+ Symbol sym = asSymbol(el);
+ switch (sym.symKind()) {
+ case METHOD:
+ TurbineExecutableElement m = (TurbineExecutableElement) el;
+ if (shouldAdd(s, from, methods, m)) {
+ methods.put(m.info().name(), m);
+ results.add(el);
+ }
+ break;
+ case FIELD:
+ if (shouldAdd(s, from, (TurbineFieldElement) el)) {
+ results.add(el);
+ }
+ break;
+ default:
+ results.add(el);
+ }
+ }
+ }
+ return results.build();
+ }
+
+ private boolean shouldAdd(
+ ClassSymbol s,
+ PackageSymbol from,
+ Multimap<String, TurbineExecutableElement> methods,
+ TurbineExecutableElement m) {
+ if (m.sym().owner().equals(s)) {
+ // always include methods (and constructors) declared in the given type
+ return true;
+ }
+ if (m.getKind() == ElementKind.CONSTRUCTOR) {
+ // skip constructors from super-types, because the spec says so
+ return false;
+ }
+ if (!isVisible(from, packageSymbol(m.sym()), TurbineVisibility.fromAccess(m.info().access()))) {
+ // skip invisible methods in supers
+ return false;
+ }
+ // otherwise check if we've seen methods that override, or are overridden by, the
+ // current method
+ Set<TurbineExecutableElement> overrides = new HashSet<>();
+ Set<TurbineExecutableElement> overridden = new HashSet<>();
+ String name = m.info().name();
+ for (TurbineExecutableElement other : methods.get(name)) {
+ if (overrides(m, other, (TypeElement) m.getEnclosingElement())) {
+ overrides.add(other);
+ continue;
+ }
+ if (overrides(other, m, (TypeElement) other.getEnclosingElement())) {
+ overridden.add(other);
+ continue;
+ }
+ }
+ if (!overridden.isEmpty()) {
+ // We've already processed method(s) that override this one; nothing to do here.
+ // If that's true, and we've *also* processed a methods that this one overrides,
+ // something has gone terribly wrong: since overriding is transitive the results
+ // contain a pair of methods that override each other.
+ checkState(overrides.isEmpty());
+ return false;
+ }
+ // Add this method, and remove any methods we've already processed that it overrides.
+ for (TurbineExecutableElement override : overrides) {
+ methods.remove(name, override);
+ }
+ return true;
+ }
+
+ private static boolean shouldAdd(ClassSymbol s, PackageSymbol from, TurbineFieldElement f) {
+ FieldSymbol sym = f.sym();
+ if (sym.owner().equals(s)) {
+ // always include fields declared in the given type
+ return true;
+ }
+ if (!isVisible(from, packageSymbol(sym), TurbineVisibility.fromAccess(f.info().access()))) {
+ // skip invisible fields in supers
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Returns true if an element with the given {@code visibility} and located in package {@from} is
+ * visible to elements in package {@code to}.
+ */
+ private static boolean isVisible(
+ PackageSymbol from, PackageSymbol to, TurbineVisibility visibility) {
+ switch (visibility) {
+ case PUBLIC:
+ case PROTECTED:
+ break;
+ case PACKAGE:
+ return from.equals(to);
+ case PRIVATE:
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public List<? extends AnnotationMirror> getAllAnnotationMirrors(Element element) {
+ return ((TurbineElement) element).getAllAnnotationMirrors();
+ }
+
+ @Override
+ public boolean hides(Element hider, Element hidden) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean overrides(
+ ExecutableElement overrider, ExecutableElement overridden, TypeElement type) {
+ if (!overrider.getSimpleName().contentEquals(overridden.getSimpleName())) {
+ return false;
+ }
+ TypeMirror a = overrider.asType();
+ TypeMirror b = types.asMemberOf((DeclaredType) type.asType(), overridden);
+ if (b == null) {
+ return false;
+ }
+ if (!types.isSubsignature((TurbineExecutableType) a, (TurbineExecutableType) b)) {
+ return false;
+ }
+ return isVisible(
+ packageSymbol(asSymbol(overrider)),
+ packageSymbol(asSymbol(overridden)),
+ TurbineVisibility.fromAccess(((TurbineExecutableElement) overridden).info().access()));
+ }
+
+ @Override
+ public String getConstantExpression(Object value) {
+ if (value instanceof Byte) {
+ return new Const.ByteValue((Byte) value).toString();
+ }
+ if (value instanceof Long) {
+ return new Const.LongValue((Long) value).toString();
+ }
+ if (value instanceof Float) {
+ return new Const.FloatValue((Float) value).toString();
+ }
+ if (value instanceof Double) {
+ return new Const.DoubleValue((Double) value).toString();
+ }
+ if (value instanceof Short) {
+ // Special-case short for consistency with javac, see:
+ // https://bugs.openjdk.java.net/browse/JDK-8227617
+ return String.format("(short)%d", (Short) value);
+ }
+ if (value instanceof String) {
+ return new Const.StringValue((String) value).toString();
+ }
+ if (value instanceof Character) {
+ return new Const.CharValue((Character) value).toString();
+ }
+ return String.valueOf(value);
+ }
+
+ @Override
+ public void printElements(Writer w, Element... elements) {
+ PrintWriter pw = new PrintWriter(w, true);
+ for (Element element : elements) {
+ pw.println(element.toString());
+ }
+ }
+
+ @Override
+ public Name getName(CharSequence cs) {
+ return new TurbineName(cs.toString());
+ }
+
+ @Override
+ public boolean isFunctionalInterface(TypeElement type) {
+ throw new UnsupportedOperationException();
+ }
+}
diff --git a/java/com/google/turbine/processing/TurbineFiler.java b/java/com/google/turbine/processing/TurbineFiler.java
new file mode 100644
index 0000000..186eb7f
--- /dev/null
+++ b/java/com/google/turbine/processing/TurbineFiler.java
@@ -0,0 +1,435 @@
+/*
+ * Copyright 2019 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.processing;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+import com.google.common.base.Function;
+import com.google.common.base.Supplier;
+import com.google.common.base.Suppliers;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.io.ByteStreams;
+import com.google.turbine.diag.SourceFile;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Reader;
+import java.io.StringReader;
+import java.io.Writer;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import javax.annotation.processing.Filer;
+import javax.annotation.processing.FilerException;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.Modifier;
+import javax.lang.model.element.NestingKind;
+import javax.tools.FileObject;
+import javax.tools.JavaFileManager.Location;
+import javax.tools.JavaFileObject;
+import javax.tools.JavaFileObject.Kind;
+import javax.tools.StandardLocation;
+
+/** Turbine's implementation of {@link Filer}. */
+public class TurbineFiler implements Filer {
+
+ /**
+ * Existing paths of file objects that cannot be regenerated, including the original compilation
+ * inputs and source or class files generated during any annotation processing round.
+ */
+ private final Set<String> seen;
+
+ /**
+ * File objects generated during the current processing round. Each entry has a unique path, which
+ * is enforced by {@link #seen}.
+ */
+ private final List<TurbineJavaFileObject> files = new ArrayList<>();
+
+ /** Loads resources from the classpath. */
+ private final Function<String, Supplier<byte[]>> classPath;
+
+ /** The {@link ClassLoader} for the annotation processor path, for loading resources. */
+ private final ClassLoader loader;
+
+ private final Map<String, SourceFile> generatedSources = new LinkedHashMap<>();
+ private final Map<String, byte[]> generatedClasses = new LinkedHashMap<>();
+
+ /** Generated source file objects from all rounds. */
+ public ImmutableMap<String, SourceFile> generatedSources() {
+ return ImmutableMap.copyOf(generatedSources);
+ }
+
+ /** Generated class file objects from all rounds. */
+ public ImmutableMap<String, byte[]> generatedClasses() {
+ return ImmutableMap.copyOf(generatedClasses);
+ }
+
+ public TurbineFiler(
+ Set<String> seen, Function<String, Supplier<byte[]>> classPath, ClassLoader loader) {
+ this.seen = seen;
+ this.classPath = classPath;
+ this.loader = loader;
+ }
+
+ /**
+ * Called when the current annotation processing round is complete, and returns the sources
+ * generated in that round.
+ */
+ public Collection<SourceFile> finishRound() {
+ Map<String, SourceFile> roundSources = new LinkedHashMap<>();
+ for (TurbineJavaFileObject e : files) {
+ String path = e.getName();
+ switch (e.getKind()) {
+ case SOURCE:
+ roundSources.put(path, new SourceFile(path, e.contents()));
+ break;
+ case CLASS:
+ generatedClasses.put(path, e.bytes());
+ break;
+ case OTHER:
+ switch (e.location()) {
+ case CLASS_OUTPUT:
+ generatedClasses.put(path, e.bytes());
+ break;
+ case SOURCE_OUTPUT:
+ this.generatedSources.put(path, new SourceFile(path, e.contents()));
+ break;
+ default:
+ throw new AssertionError(e.location());
+ }
+ break;
+ case HTML:
+ throw new UnsupportedOperationException(String.valueOf(e.getKind()));
+ }
+ }
+ files.clear();
+ this.generatedSources.putAll(roundSources);
+ return roundSources.values();
+ }
+
+ @Override
+ public JavaFileObject createSourceFile(CharSequence n, Element... originatingElements)
+ throws IOException {
+ String name = n.toString();
+ checkArgument(!name.contains("/"), "invalid type name: %s", name);
+ return create(StandardLocation.SOURCE_OUTPUT, Kind.SOURCE, name.replace('.', '/') + ".java");
+ }
+
+ @Override
+ public JavaFileObject createClassFile(CharSequence n, Element... originatingElements)
+ throws IOException {
+ String name = n.toString();
+ checkArgument(!name.contains("/"), "invalid type name: %s", name);
+ return create(StandardLocation.CLASS_OUTPUT, Kind.CLASS, name.replace('.', '/') + ".class");
+ }
+
+ @Override
+ public FileObject createResource(
+ Location location, CharSequence p, CharSequence r, Element... originatingElements)
+ throws IOException {
+ checkArgument(location instanceof StandardLocation, "%s", location);
+ String pkg = p.toString();
+ String relativeName = r.toString();
+ checkArgument(!pkg.contains("/"), "invalid package: %s", pkg);
+ String path = packageRelativePath(pkg, relativeName);
+ return create((StandardLocation) location, Kind.OTHER, path);
+ }
+
+ private JavaFileObject create(StandardLocation location, Kind kind, String path)
+ throws FilerException {
+ checkArgument(location.isOutputLocation());
+ if (!seen.add(path)) {
+ throw new FilerException("already created " + path);
+ }
+ TurbineJavaFileObject result = new TurbineJavaFileObject(location, kind, path);
+ files.add(result);
+ return result;
+ }
+
+ @Override
+ public FileObject getResource(Location location, CharSequence p, CharSequence r)
+ throws IOException {
+ String pkg = p.toString();
+ String relativeName = r.toString();
+ checkArgument(!pkg.contains("/"), "invalid package: %s", pkg);
+ checkArgument(location instanceof StandardLocation, "unsupported location %s", location);
+ StandardLocation standardLocation = (StandardLocation) location;
+ String path = packageRelativePath(pkg, relativeName);
+ switch (standardLocation) {
+ case CLASS_OUTPUT:
+ byte[] generated = generatedClasses.get(path);
+ if (generated == null) {
+ throw new FileNotFoundException(path);
+ }
+ return new BytesFileObject(path, Suppliers.ofInstance(generated));
+ case SOURCE_OUTPUT:
+ SourceFile source = generatedSources.get(path);
+ if (source == null) {
+ throw new FileNotFoundException(path);
+ }
+ return new SourceFileObject(path, source.source());
+ case ANNOTATION_PROCESSOR_PATH:
+ if (loader.getResource(path) == null) {
+ throw new FileNotFoundException(path);
+ }
+ return new ResourceFileObject(loader, path);
+ case CLASS_PATH:
+ Supplier<byte[]> bytes = classPath.apply(path);
+ if (bytes == null) {
+ throw new FileNotFoundException(path);
+ }
+ return new BytesFileObject(path, bytes);
+ default:
+ throw new IllegalArgumentException(standardLocation.getName());
+ }
+ }
+
+ private static String packageRelativePath(String pkg, String relativeName) {
+ if (pkg.isEmpty()) {
+ return relativeName;
+ }
+ return pkg.replace('.', '/') + '/' + relativeName;
+ }
+
+ private abstract static class ReadOnlyFileObject implements FileObject {
+
+ protected final String path;
+
+ public ReadOnlyFileObject(String path) {
+ this.path = path;
+ }
+
+ @Override
+ public final String getName() {
+ return path;
+ }
+
+ @Override
+ public URI toUri() {
+ return URI.create("file://" + path);
+ }
+
+ @Override
+ public final OutputStream openOutputStream() {
+ throw new IllegalStateException();
+ }
+
+ @Override
+ public final Writer openWriter() {
+ throw new IllegalStateException();
+ }
+
+ @Override
+ public final long getLastModified() {
+ return 0;
+ }
+
+ @Override
+ public final boolean delete() {
+ throw new IllegalStateException();
+ }
+ }
+
+ private abstract static class WriteOnlyFileObject implements FileObject {
+
+ @Override
+ public final InputStream openInputStream() {
+ throw new IllegalStateException();
+ }
+
+ @Override
+ public final Reader openReader(boolean ignoreEncodingErrors) {
+ throw new IllegalStateException();
+ }
+
+ @Override
+ public final CharSequence getCharContent(boolean ignoreEncodingErrors) {
+ throw new IllegalStateException();
+ }
+ }
+
+ private static class TurbineJavaFileObject extends WriteOnlyFileObject implements JavaFileObject {
+
+ private final StandardLocation location;
+ private final Kind kind;
+ private final CharSequence name;
+ private final ByteArrayOutputStream baos = new ByteArrayOutputStream();
+
+ public TurbineJavaFileObject(StandardLocation location, Kind kind, CharSequence name) {
+ this.location = location;
+ this.kind = kind;
+ this.name = name;
+ }
+
+ @Override
+ public Kind getKind() {
+ return kind;
+ }
+
+ @Override
+ public boolean isNameCompatible(String simpleName, Kind kind) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public NestingKind getNestingKind() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Modifier getAccessLevel() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public URI toUri() {
+ return URI.create("file://" + name + kind.extension);
+ }
+
+ @Override
+ public String getName() {
+ return name.toString();
+ }
+
+ @Override
+ public OutputStream openOutputStream() {
+ return baos;
+ }
+
+ @Override
+ public Writer openWriter() {
+ return new OutputStreamWriter(openOutputStream(), UTF_8);
+ }
+
+ @Override
+ public long getLastModified() {
+ return 0;
+ }
+
+ @Override
+ public boolean delete() {
+ throw new IllegalStateException();
+ }
+
+ public byte[] bytes() {
+ return baos.toByteArray();
+ }
+
+ public String contents() {
+ return new String(baos.toByteArray(), UTF_8);
+ }
+
+ public StandardLocation location() {
+ return location;
+ }
+ }
+
+ private static class ResourceFileObject extends ReadOnlyFileObject {
+
+ private final ClassLoader loader;
+
+ public ResourceFileObject(ClassLoader loader, String path) {
+ super(path);
+ this.loader = loader;
+ }
+
+ @Override
+ public URI toUri() {
+ try {
+ return loader.getResource(path).toURI();
+ } catch (URISyntaxException e) {
+ throw new AssertionError(e);
+ }
+ }
+
+ @Override
+ public InputStream openInputStream() {
+ return loader.getResourceAsStream(path);
+ }
+
+ @Override
+ public Reader openReader(boolean ignoreEncodingErrors) throws IOException {
+ return new InputStreamReader(openInputStream(), UTF_8);
+ }
+
+ @Override
+ public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
+ return new String(ByteStreams.toByteArray(openInputStream()), UTF_8);
+ }
+ }
+
+ private static class BytesFileObject extends ReadOnlyFileObject {
+
+ private final Supplier<byte[]> bytes;
+
+ public BytesFileObject(String path, Supplier<byte[]> bytes) {
+ super(path);
+ this.bytes = bytes;
+ }
+
+ @Override
+ public InputStream openInputStream() {
+ return new ByteArrayInputStream(bytes.get());
+ }
+
+ @Override
+ public Reader openReader(boolean ignoreEncodingErrors) {
+ return new InputStreamReader(openInputStream(), UTF_8);
+ }
+
+ @Override
+ public CharSequence getCharContent(boolean ignoreEncodingErrors) {
+ return new String(bytes.get(), UTF_8);
+ }
+ }
+
+ private static class SourceFileObject extends ReadOnlyFileObject {
+
+ private final String source;
+
+ public SourceFileObject(String path, String source) {
+ super(path);
+ this.source = source;
+ }
+
+ @Override
+ public InputStream openInputStream() {
+ return new ByteArrayInputStream(source.getBytes(UTF_8));
+ }
+
+ @Override
+ public Reader openReader(boolean ignoreEncodingErrors) {
+ return new StringReader(source);
+ }
+
+ @Override
+ public CharSequence getCharContent(boolean ignoreEncodingErrors) {
+ return source;
+ }
+ }
+}
diff --git a/java/com/google/turbine/processing/TurbineMessager.java b/java/com/google/turbine/processing/TurbineMessager.java
new file mode 100644
index 0000000..9c333b2
--- /dev/null
+++ b/java/com/google/turbine/processing/TurbineMessager.java
@@ -0,0 +1,252 @@
+/*
+ * Copyright 2019 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.processing;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.turbine.binder.bound.SourceTypeBoundClass;
+import com.google.turbine.binder.bound.TurbineAnnotationValue;
+import com.google.turbine.binder.bound.TypeBoundClass;
+import com.google.turbine.binder.bound.TypeBoundClass.MethodInfo;
+import com.google.turbine.binder.bound.TypeBoundClass.TyVarInfo;
+import com.google.turbine.binder.sym.ClassSymbol;
+import com.google.turbine.binder.sym.FieldSymbol;
+import com.google.turbine.binder.sym.MethodSymbol;
+import com.google.turbine.binder.sym.ParamSymbol;
+import com.google.turbine.binder.sym.Symbol;
+import com.google.turbine.binder.sym.TyVarSymbol;
+import com.google.turbine.diag.SourceFile;
+import com.google.turbine.diag.TurbineError;
+import com.google.turbine.diag.TurbineLog;
+import com.google.turbine.model.Const;
+import com.google.turbine.processing.TurbineElement.TurbineNoTypeElement;
+import com.google.turbine.tree.Tree;
+import com.google.turbine.type.AnnoInfo;
+import java.util.Iterator;
+import javax.annotation.processing.Messager;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.AnnotationValue;
+import javax.lang.model.element.Element;
+import javax.tools.Diagnostic;
+import org.checkerframework.checker.nullness.qual.Nullable;
+
+/** Turbine's implementation of {@link Messager}. */
+public class TurbineMessager implements Messager {
+ private final ModelFactory factory;
+ private final TurbineLog log;
+
+ public TurbineMessager(ModelFactory factory, TurbineLog log) {
+ this.factory = factory;
+ this.log = log;
+ }
+
+ @Override
+ public void printMessage(Diagnostic.Kind kind, CharSequence msg) {
+ // TODO(cushon): null-check `msg` after fixing affected processors
+ log.diagnostic(kind, String.valueOf(msg));
+ }
+
+ @Override
+ public void printMessage(Diagnostic.Kind kind, CharSequence msg, Element e) {
+ if (e == null || e instanceof TurbineNoTypeElement) {
+ printMessage(kind, msg);
+ return;
+ }
+ Symbol sym = ((TurbineElement) e).sym();
+ SourceFile source = getSource(sym);
+ int position = getPosition(sym);
+ log.withSource(source).diagnostic(kind, position, TurbineError.ErrorKind.PROC, msg);
+ }
+
+ @Override
+ public void printMessage(Diagnostic.Kind kind, CharSequence msg, Element e, AnnotationMirror a) {
+ if (a == null || e == null || e instanceof TurbineNoTypeElement) {
+ printMessage(kind, msg, e);
+ return;
+ }
+ SourceFile source = getSource(((TurbineElement) e).sym());
+ int position = ((TurbineAnnotationMirror) a).anno().tree().position();
+ log.withSource(source).diagnostic(kind, position, TurbineError.ErrorKind.PROC, msg);
+ }
+
+ @Override
+ public void printMessage(
+ Diagnostic.Kind kind, CharSequence msg, Element e, AnnotationMirror a, AnnotationValue v) {
+ if (a == null || e == null || e instanceof TurbineNoTypeElement || v == null) {
+ printMessage(kind, msg, e, a);
+ return;
+ }
+ SourceFile source = getSource(((TurbineElement) e).sym());
+ AnnoInfo anno = ((TurbineAnnotationMirror) a).anno();
+ int position = locateInAnnotation(((TurbineAnnotationValueMirror) v).value(), anno);
+ if (position == -1) {
+ position = anno.tree().position();
+ }
+ log.withSource(source).diagnostic(kind, position, TurbineError.ErrorKind.PROC, msg);
+ }
+
+ /**
+ * Returns the {@link SourceFile} that contains the declaration of the given {@link Symbol}, or
+ * {@code null} if the symbol was not compiled from source.
+ */
+ @Nullable
+ private SourceFile getSource(Symbol sym) {
+ ClassSymbol encl = ModelFactory.enclosingClass(sym);
+ TypeBoundClass info = factory.getSymbol(encl);
+ if (!(info instanceof SourceTypeBoundClass)) {
+ return null;
+ }
+ return ((SourceTypeBoundClass) info).source();
+ }
+
+ /**
+ * Returns the position of the given {@link Symbol}'s declaration, or {@code null} if it was not
+ * compiled from source.
+ */
+ private int getPosition(Symbol sym) {
+ switch (sym.symKind()) {
+ case CLASS:
+ return classPosition((ClassSymbol) sym);
+ case TY_PARAM:
+ return tyParamPosition((TyVarSymbol) sym);
+ case METHOD:
+ return methodPosition((MethodSymbol) sym);
+ case FIELD:
+ return fieldPosition((FieldSymbol) sym);
+ case PARAMETER:
+ return paramPosition((ParamSymbol) sym);
+ case MODULE:
+ case PACKAGE:
+ break;
+ }
+ throw new AssertionError(sym.symKind());
+ }
+
+ private int fieldPosition(FieldSymbol sym) {
+ Tree.VarDecl decl = factory.getFieldInfo(sym).decl();
+ return decl != null ? decl.position() : -1;
+ }
+
+ private int paramPosition(ParamSymbol sym) {
+ MethodInfo minfo = factory.getMethodInfo(sym.owner());
+ if (minfo.decl() == null) {
+ return -1;
+ }
+ int idx = minfo.parameters().indexOf(factory.getParamInfo(sym));
+ return minfo.decl().params().get(idx).position();
+ }
+
+ private int methodPosition(MethodSymbol sym) {
+ MethodInfo methodInfo = factory.getMethodInfo(sym);
+ Tree.MethDecl decl = methodInfo.decl();
+ if (decl != null) {
+ return decl.position();
+ }
+ // use the enclosing class position for synthetic methods
+ int position = classPosition(sym.owner());
+ if (position == -1) {
+ return -1;
+ }
+ // TODO(b/139079081): change diagnostic position of declarations instead of the -= 6 fixup
+ position -= 6; // adjust to start of `class ` for parity with javac
+ return position;
+ }
+
+ private int classPosition(ClassSymbol owner) {
+ TypeBoundClass symbol = factory.getSymbol(owner);
+ if (!(symbol instanceof SourceTypeBoundClass)) {
+ return -1;
+ }
+ return ((SourceTypeBoundClass) symbol).decl().position();
+ }
+
+ private int tyParamPosition(TyVarSymbol sym) {
+ TyVarSymbol tyVarSymbol = sym;
+ Symbol owner = tyVarSymbol.owner();
+ ImmutableMap<TyVarSymbol, TyVarInfo> tyVars;
+ ImmutableList<Tree.TyParam> trees;
+ switch (owner.symKind()) {
+ case CLASS:
+ TypeBoundClass cinfo = factory.getSymbol((ClassSymbol) owner);
+ if (!(cinfo instanceof SourceTypeBoundClass)) {
+ return -1;
+ }
+ tyVars = cinfo.typeParameterTypes();
+ trees = ((SourceTypeBoundClass) cinfo).decl().typarams();
+ break;
+ case METHOD:
+ MethodInfo minfo = factory.getMethodInfo((MethodSymbol) owner);
+ if (minfo.decl() == null) {
+ return -1;
+ }
+ tyVars = minfo.tyParams();
+ trees = minfo.decl().typarams();
+ break;
+ default:
+ throw new AssertionError(owner.symKind());
+ }
+ return trees.get(tyVars.keySet().asList().indexOf(tyVarSymbol)).position();
+ }
+
+ /** Returns the position of the given annotation value {@code v} in the given annotation. */
+ private static int locateInAnnotation(Const v, AnnoInfo anno) {
+ return locate(v, anno.values().values().asList(), anno.tree().args());
+ }
+
+ /**
+ * Returns the position of the given annotation value {@code toFind} within the given constant
+ * {@code v} (which may be a compound value, i.e. a nested annotation or array value), and given
+ * the corresponding expression tree.
+ */
+ private static int locate(Const toFind, Const v, Tree.Expression t) {
+ // the element name can be omitted for `value`, e.g. in `@A({1, 2, 3})`
+ t = t.kind().equals(Tree.Kind.ASSIGN) ? ((Tree.Assign) t).expr() : t;
+ if (toFind.equals(v)) {
+ return t.position();
+ }
+ switch (v.kind()) {
+ case ARRAY:
+ ImmutableList<Tree.Expression> elements =
+ t.kind().equals(Tree.Kind.ARRAY_INIT)
+ ? ((Tree.ArrayInit) t).exprs()
+ : ImmutableList.of(t);
+ return locate(toFind, ((Const.ArrayInitValue) v).elements(), elements);
+ case ANNOTATION:
+ return locateInAnnotation(toFind, ((TurbineAnnotationValue) v).info());
+ default:
+ return -1;
+ }
+ }
+
+ /**
+ * Returns the position of the given annotation value {@code toFind}, given a list of annotation
+ * values corresponding to the element values of a (possibly nested) annotation or an array value,
+ * and the corresponding expression trees.
+ */
+ private static int locate(
+ Const toFind, ImmutableList<Const> vx, ImmutableList<Tree.Expression> tx) {
+ Iterator<Const> vi = vx.iterator();
+ Iterator<Tree.Expression> ti = tx.iterator();
+ while (vi.hasNext()) {
+ int result = locate(toFind, vi.next(), ti.next());
+ if (result != -1) {
+ return result;
+ }
+ }
+ return -1;
+ }
+}
diff --git a/java/com/google/turbine/processing/TurbineName.java b/java/com/google/turbine/processing/TurbineName.java
new file mode 100644
index 0000000..584b1b1
--- /dev/null
+++ b/java/com/google/turbine/processing/TurbineName.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2019 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.processing;
+
+import static java.util.Objects.requireNonNull;
+
+import javax.lang.model.element.Name;
+
+/** An implementation of {@link Name} backed by a {@link CharSequence}. */
+public class TurbineName implements Name {
+
+ private final String name;
+
+ public TurbineName(String name) {
+ requireNonNull(name);
+ this.name = name;
+ }
+
+ @Override
+ public boolean contentEquals(CharSequence cs) {
+ return name.contentEquals(cs);
+ }
+
+ @Override
+ public int length() {
+ return name.length();
+ }
+
+ @Override
+ public char charAt(int index) {
+ return name.charAt(index);
+ }
+
+ @Override
+ public CharSequence subSequence(int start, int end) {
+ return name.subSequence(start, end);
+ }
+
+ @Override
+ public String toString() {
+ return name;
+ }
+
+ @Override
+ public int hashCode() {
+ return name.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return obj instanceof TurbineName && contentEquals(((TurbineName) obj).name);
+ }
+}
diff --git a/java/com/google/turbine/processing/TurbineProcessingEnvironment.java b/java/com/google/turbine/processing/TurbineProcessingEnvironment.java
new file mode 100644
index 0000000..726d075
--- /dev/null
+++ b/java/com/google/turbine/processing/TurbineProcessingEnvironment.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2019 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.processing;
+
+import java.util.Locale;
+import java.util.Map;
+import javax.annotation.processing.Filer;
+import javax.annotation.processing.Messager;
+import javax.annotation.processing.ProcessingEnvironment;
+import javax.lang.model.SourceVersion;
+import javax.lang.model.util.Elements;
+import javax.lang.model.util.Types;
+import org.checkerframework.checker.nullness.qual.Nullable;
+
+/** Turbine's {@link ProcessingEnvironment). */
+public class TurbineProcessingEnvironment implements ProcessingEnvironment {
+
+ private final Filer filer;
+ private final Types types;
+ private final Map<String, String> processorOptions;
+ private final Elements elements;
+ private final Map<String, byte[]> statistics;
+ private final SourceVersion sourceVersion;
+ private final Messager messager;
+ private final ClassLoader processorLoader;
+
+ public TurbineProcessingEnvironment(
+ Filer filer,
+ Types types,
+ Elements elements,
+ Messager messager,
+ Map<String, String> processorOptions,
+ SourceVersion sourceVersion,
+ @Nullable ClassLoader processorLoader,
+ Map<String, byte[]> statistics) {
+ this.filer = filer;
+ this.types = types;
+ this.processorOptions = processorOptions;
+ this.sourceVersion = sourceVersion;
+ this.elements = elements;
+ this.statistics = statistics;
+ this.messager = messager;
+ this.processorLoader = processorLoader;
+ }
+
+ @Override
+ public Map<String, String> getOptions() {
+ return processorOptions;
+ }
+
+ @Override
+ public Messager getMessager() {
+ return messager;
+ }
+
+ @Override
+ public Filer getFiler() {
+ return filer;
+ }
+
+ @Override
+ public Elements getElementUtils() {
+ return elements;
+ }
+
+ @Override
+ public Types getTypeUtils() {
+ return types;
+ }
+
+ @Override
+ public SourceVersion getSourceVersion() {
+ return sourceVersion;
+ }
+
+ @Override
+ public Locale getLocale() {
+ return Locale.ENGLISH;
+ }
+
+ public ClassLoader processorLoader() {
+ return processorLoader;
+ }
+
+ public void addStatistics(String key, byte[] extension) {
+ byte[] existing = statistics.put(key, extension);
+ if (existing != null) {
+ throw new IllegalStateException("duplicate statistics reported for " + key);
+ }
+ }
+}
diff --git a/java/com/google/turbine/processing/TurbineRoundEnvironment.java b/java/com/google/turbine/processing/TurbineRoundEnvironment.java
new file mode 100644
index 0000000..d63a4e8
--- /dev/null
+++ b/java/com/google/turbine/processing/TurbineRoundEnvironment.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2019 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.processing;
+
+import com.google.common.base.Supplier;
+import com.google.common.base.Suppliers;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.ImmutableSetMultimap;
+import com.google.turbine.binder.sym.ClassSymbol;
+import com.google.turbine.binder.sym.Symbol;
+import com.google.turbine.processing.TurbineElement.TurbineTypeElement;
+import java.lang.annotation.Annotation;
+import java.util.Set;
+import javax.annotation.processing.RoundEnvironment;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.TypeElement;
+
+/** A {@link RoundEnvironment}. */
+public class TurbineRoundEnvironment implements RoundEnvironment {
+
+ private final ModelFactory factory;
+ private final ImmutableSet<ClassSymbol> syms;
+ private final boolean processingOver;
+ private final boolean errorRaised;
+ private final ImmutableSetMultimap<ClassSymbol, Symbol> allAnnotations;
+
+ // the round environment doesn't outlive the round, so don't worry about resetting this cache
+ private final Supplier<ImmutableSet<TurbineTypeElement>> rootElements =
+ Suppliers.memoize(
+ new Supplier<ImmutableSet<TurbineTypeElement>>() {
+ @Override
+ public ImmutableSet<TurbineTypeElement> get() {
+ ImmutableSet.Builder<TurbineTypeElement> result = ImmutableSet.builder();
+ for (ClassSymbol sym : syms) {
+ if (sym.simpleName().contains("$") && factory.getSymbol(sym).owner() != null) {
+ continue;
+ }
+ if (sym.simpleName().equals("package-info")) {
+ continue;
+ }
+ result.add(factory.typeElement(sym));
+ }
+ return result.build();
+ }
+ });
+
+ public TurbineRoundEnvironment(
+ ModelFactory factory,
+ ImmutableSet<ClassSymbol> syms,
+ boolean processingOver,
+ boolean errorRaised,
+ ImmutableSetMultimap<ClassSymbol, Symbol> allAnnotations) {
+ this.factory = factory;
+ this.syms = syms;
+ this.processingOver = processingOver;
+ this.errorRaised = errorRaised;
+ this.allAnnotations = allAnnotations;
+ }
+
+ @Override
+ public boolean processingOver() {
+ return processingOver;
+ }
+
+ @Override
+ public boolean errorRaised() {
+ return errorRaised;
+ }
+
+ @Override
+ public ImmutableSet<TurbineTypeElement> getRootElements() {
+ return rootElements.get();
+ }
+
+ @Override
+ public Set<? extends Element> getElementsAnnotatedWith(TypeElement a) {
+ return factory.elements(allAnnotations.get(((TurbineTypeElement) a).sym()));
+ }
+
+ @Override
+ public Set<? extends Element> getElementsAnnotatedWith(Class<? extends Annotation> a) {
+ return getElementsAnnotatedWith(
+ factory.typeElement(new ClassSymbol(a.getName().replace('.', '/'))));
+ }
+}
diff --git a/java/com/google/turbine/processing/TurbineTypeMirror.java b/java/com/google/turbine/processing/TurbineTypeMirror.java
new file mode 100644
index 0000000..e94672c
--- /dev/null
+++ b/java/com/google/turbine/processing/TurbineTypeMirror.java
@@ -0,0 +1,770 @@
+/*
+ * Copyright 2019 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.processing;
+
+import static com.google.common.collect.Iterables.getLast;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.base.Ascii;
+import com.google.common.base.Joiner;
+import com.google.common.base.Supplier;
+import com.google.common.collect.ImmutableList;
+import com.google.turbine.binder.bound.TypeBoundClass;
+import com.google.turbine.binder.bound.TypeBoundClass.TyVarInfo;
+import com.google.turbine.binder.sym.PackageSymbol;
+import com.google.turbine.binder.sym.TyVarSymbol;
+import com.google.turbine.model.TurbineConstantTypeKind;
+import com.google.turbine.model.TurbineFlag;
+import com.google.turbine.model.TurbineTyKind;
+import com.google.turbine.type.AnnoInfo;
+import com.google.turbine.type.Type;
+import com.google.turbine.type.Type.ArrayTy;
+import com.google.turbine.type.Type.ClassTy;
+import com.google.turbine.type.Type.ErrorTy;
+import com.google.turbine.type.Type.IntersectionTy;
+import com.google.turbine.type.Type.MethodTy;
+import com.google.turbine.type.Type.PrimTy;
+import com.google.turbine.type.Type.TyVar;
+import com.google.turbine.type.Type.WildTy;
+import com.google.turbine.type.Type.WildTy.BoundKind;
+import java.lang.annotation.Annotation;
+import java.util.List;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.Element;
+import javax.lang.model.type.ArrayType;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.ErrorType;
+import javax.lang.model.type.ExecutableType;
+import javax.lang.model.type.IntersectionType;
+import javax.lang.model.type.NoType;
+import javax.lang.model.type.NullType;
+import javax.lang.model.type.PrimitiveType;
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.type.TypeVariable;
+import javax.lang.model.type.TypeVisitor;
+import javax.lang.model.type.WildcardType;
+
+/** A {@link TypeMirror} implementation backed by a {@link Type}. */
+public abstract class TurbineTypeMirror implements TypeMirror {
+
+ protected final ModelFactory factory;
+
+ protected TurbineTypeMirror(ModelFactory factory) {
+ this.factory = requireNonNull(factory);
+ }
+
+ protected abstract ImmutableList<AnnoInfo> annos();
+
+ @Override
+ public final List<? extends AnnotationMirror> getAnnotationMirrors() {
+ ImmutableList.Builder<AnnotationMirror> result = ImmutableList.builder();
+ for (AnnoInfo anno : annos()) {
+ result.add(TurbineAnnotationMirror.create(factory, anno));
+ }
+ return result.build();
+ }
+
+ @Override
+ public final <A extends Annotation> A getAnnotation(Class<A> annotationType) {
+ throw new AssertionError();
+ }
+
+ @Override
+ public final <A extends Annotation> A[] getAnnotationsByType(Class<A> annotationType) {
+ throw new AssertionError();
+ }
+
+ public abstract Type asTurbineType();
+
+ @Override
+ public String toString() {
+ return asTurbineType().toString();
+ }
+
+ /** A {@link PrimitiveType} implementation backed by a {@link PrimTy}. */
+ static class TurbinePrimitiveType extends TurbineTypeMirror implements PrimitiveType {
+
+ @Override
+ public String toString() {
+ return Ascii.toLowerCase(type.primkind().toString());
+ }
+
+ @Override
+ public Type asTurbineType() {
+ return type;
+ }
+
+ public final PrimTy type;
+
+ TurbinePrimitiveType(ModelFactory factory, PrimTy type) {
+ super(factory);
+ if (type.primkind() == TurbineConstantTypeKind.STRING) {
+ throw new AssertionError(type);
+ }
+ this.type = type;
+ }
+
+ @Override
+ public TypeKind getKind() {
+ switch (type.primkind()) {
+ case CHAR:
+ return TypeKind.CHAR;
+ case SHORT:
+ return TypeKind.SHORT;
+ case INT:
+ return TypeKind.INT;
+ case LONG:
+ return TypeKind.LONG;
+ case FLOAT:
+ return TypeKind.FLOAT;
+ case DOUBLE:
+ return TypeKind.DOUBLE;
+ case BOOLEAN:
+ return TypeKind.BOOLEAN;
+ case BYTE:
+ return TypeKind.BYTE;
+ case NULL:
+ return TypeKind.NULL;
+ case STRING:
+ }
+ throw new AssertionError(type.primkind());
+ }
+
+ @Override
+ public <R, P> R accept(TypeVisitor<R, P> v, P p) {
+ return v.visitPrimitive(this, p);
+ }
+
+ @Override
+ protected ImmutableList<AnnoInfo> annos() {
+ return type.annos();
+ }
+ }
+
+ /** A {@link DeclaredType} implementation backed by a {@link ClassTy}. */
+ static class TurbineDeclaredType extends TurbineTypeMirror implements DeclaredType {
+
+ @Override
+ public int hashCode() {
+ return type.sym().hashCode();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return obj instanceof TurbineDeclaredType && type.equals(((TurbineDeclaredType) obj).type);
+ }
+
+ @Override
+ public ClassTy asTurbineType() {
+ return type;
+ }
+
+ private final ClassTy type;
+
+ TurbineDeclaredType(ModelFactory factory, ClassTy type) {
+ super(factory);
+ this.type = type;
+ }
+
+ @Override
+ public String toString() {
+ return type.toString();
+ }
+
+ final Supplier<Element> element =
+ factory.memoize(
+ new Supplier<Element>() {
+ @Override
+ public Element get() {
+ return factory.typeElement(type.sym());
+ }
+ });
+
+ @Override
+ public Element asElement() {
+ return element.get();
+ }
+
+ final Supplier<TypeMirror> enclosing =
+ factory.memoize(
+ new Supplier<TypeMirror>() {
+ @Override
+ public TypeMirror get() {
+ TypeBoundClass info = factory.getSymbol(type.sym());
+ if (info != null
+ && info.owner() != null
+ && ((info.access() & TurbineFlag.ACC_STATIC) == 0)
+ && info.kind() == TurbineTyKind.CLASS) {
+ if (type.classes().size() > 1) {
+ return factory.asTypeMirror(
+ ClassTy.create(type.classes().subList(0, type.classes().size() - 1)));
+ }
+ return factory.asTypeMirror(ClassTy.asNonParametricClassTy(info.owner()));
+ }
+ return factory.noType();
+ }
+ });
+
+ @Override
+ public TypeMirror getEnclosingType() {
+ return enclosing.get();
+ }
+
+ final Supplier<ImmutableList<TypeMirror>> typeArguments =
+ factory.memoize(
+ new Supplier<ImmutableList<TypeMirror>>() {
+ @Override
+ public ImmutableList<TypeMirror> get() {
+ return factory.asTypeMirrors(getLast(type.classes()).targs());
+ }
+ });
+
+ @Override
+ public List<? extends TypeMirror> getTypeArguments() {
+ return typeArguments.get();
+ }
+
+ @Override
+ public TypeKind getKind() {
+ return TypeKind.DECLARED;
+ }
+
+ @Override
+ public <R, P> R accept(TypeVisitor<R, P> v, P p) {
+ return v.visitDeclared(this, p);
+ }
+
+ public ClassTy type() {
+ return type;
+ }
+
+ @Override
+ protected ImmutableList<AnnoInfo> annos() {
+ return getLast(type.classes()).annos();
+ }
+ }
+
+ /** An {@link ArrayType} implementation backed by a {@link ArrayTy}. */
+ static class TurbineArrayType extends TurbineTypeMirror implements ArrayType {
+
+ @Override
+ public Type asTurbineType() {
+ return type;
+ }
+
+ private final ArrayTy type;
+
+ TurbineArrayType(ModelFactory factory, ArrayTy type) {
+ super(factory);
+ this.type = type;
+ }
+
+ @Override
+ public TypeMirror getComponentType() {
+ return factory.asTypeMirror(type.elementType());
+ }
+
+ @Override
+ public TypeKind getKind() {
+ return TypeKind.ARRAY;
+ }
+
+ @Override
+ public <R, P> R accept(TypeVisitor<R, P> v, P p) {
+ return v.visitArray(this, p);
+ }
+
+ @Override
+ protected ImmutableList<AnnoInfo> annos() {
+ return type.annos();
+ }
+ }
+
+ /** An {@link ErrorType} implementation backed by a {@link ErrorTy}. */
+ static class TurbineErrorType extends TurbineTypeMirror implements ErrorType {
+
+ private final ErrorTy type;
+
+ public TurbineErrorType(ModelFactory factory, ErrorTy type) {
+ super(factory);
+ this.type = type;
+ }
+
+ @Override
+ public TypeKind getKind() {
+ return TypeKind.ERROR;
+ }
+
+ @Override
+ public <R, P> R accept(TypeVisitor<R, P> v, P p) {
+ return v.visitError(this, p);
+ }
+
+ @Override
+ protected ImmutableList<AnnoInfo> annos() {
+ return ImmutableList.of();
+ }
+
+ @Override
+ public Type asTurbineType() {
+ return type;
+ }
+
+ @Override
+ public Element asElement() {
+ return factory.noElement(type.name());
+ }
+
+ @Override
+ public TypeMirror getEnclosingType() {
+ return factory.noType();
+ }
+
+ @Override
+ public List<? extends TypeMirror> getTypeArguments() {
+ return ImmutableList.of();
+ }
+
+ @Override
+ public String toString() {
+ return type.toString();
+ }
+ }
+
+ /** A 'package type' implementation backed by a {@link PackageSymbol}. */
+ static class TurbinePackageType extends TurbineTypeMirror implements NoType {
+
+ @Override
+ public Type asTurbineType() {
+ throw new UnsupportedOperationException();
+ }
+
+ final PackageSymbol symbol;
+
+ TurbinePackageType(ModelFactory factory, PackageSymbol symbol) {
+ super(factory);
+ this.symbol = symbol;
+ }
+
+ @Override
+ public TypeKind getKind() {
+ return TypeKind.PACKAGE;
+ }
+
+ @Override
+ public <R, P> R accept(TypeVisitor<R, P> v, P p) {
+ return v.visitNoType(this, p);
+ }
+
+ @Override
+ public String toString() {
+ return symbol.toString();
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ return other instanceof TurbinePackageType
+ && symbol.equals(((TurbinePackageType) other).symbol);
+ }
+
+ @Override
+ public int hashCode() {
+ return symbol.hashCode();
+ }
+
+ @Override
+ protected ImmutableList<AnnoInfo> annos() {
+ return ImmutableList.of();
+ }
+ }
+
+ /** The absence of a type, {@see javax.lang.model.util.Types#getNoType}. */
+ static class TurbineNoType extends TurbineTypeMirror implements NoType {
+
+ @Override
+ public Type asTurbineType() {
+ return Type.NONE;
+ }
+
+ TurbineNoType(ModelFactory factory) {
+ super(factory);
+ }
+
+ @Override
+ public TypeKind getKind() {
+ return TypeKind.NONE;
+ }
+
+ @Override
+ public <R, P> R accept(TypeVisitor<R, P> v, P p) {
+ return v.visitNoType(this, p);
+ }
+
+ @Override
+ public String toString() {
+ return "none";
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ return other instanceof TurbineNoType;
+ }
+
+ @Override
+ public int hashCode() {
+ return getKind().hashCode();
+ }
+
+ @Override
+ protected ImmutableList<AnnoInfo> annos() {
+ return ImmutableList.of();
+ }
+ }
+
+ /** A void type, {@see javax.lang.model.util.Types#getNoType}. */
+ static class TurbineVoidType extends TurbineTypeMirror implements NoType {
+
+ @Override
+ public Type asTurbineType() {
+ return Type.VOID;
+ }
+
+ TurbineVoidType(ModelFactory factory) {
+ super(factory);
+ }
+
+ @Override
+ public TypeKind getKind() {
+ return TypeKind.VOID;
+ }
+
+ @Override
+ public <R, P> R accept(TypeVisitor<R, P> v, P p) {
+ return v.visitNoType(this, p);
+ }
+
+ @Override
+ protected ImmutableList<AnnoInfo> annos() {
+ return ImmutableList.of();
+ }
+ }
+
+ /** A {@link TypeVariable} implementation backed by a {@link TyVar}. */
+ static class TurbineTypeVariable extends TurbineTypeMirror implements TypeVariable {
+
+ @Override
+ public int hashCode() {
+ return type.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return obj instanceof TurbineTypeVariable && type.equals(((TurbineTypeVariable) obj).type);
+ }
+
+ @Override
+ public Type asTurbineType() {
+ return type;
+ }
+
+ private final TyVar type;
+
+ private final Supplier<TyVarInfo> info =
+ factory.memoize(
+ new Supplier<TyVarInfo>() {
+ @Override
+ public TyVarInfo get() {
+ return factory.getTyVarInfo(type.sym());
+ }
+ });
+
+ private TyVarInfo info() {
+ return info.get();
+ }
+
+ TurbineTypeVariable(ModelFactory factory, Type.TyVar type) {
+ super(factory);
+ this.type = type;
+ }
+
+ @Override
+ public TypeKind getKind() {
+ return TypeKind.TYPEVAR;
+ }
+
+ @Override
+ public <R, P> R accept(TypeVisitor<R, P> v, P p) {
+ return v.visitTypeVariable(this, p);
+ }
+
+ @Override
+ public Element asElement() {
+ return factory.typeParameterElement(type.sym());
+ }
+
+ @Override
+ public TypeMirror getUpperBound() {
+ return factory.asTypeMirror(info().upperBound());
+ }
+
+ @Override
+ public TypeMirror getLowerBound() {
+ return info().lowerBound() != null
+ ? factory.asTypeMirror(info().lowerBound())
+ : factory.noType();
+ }
+
+ @Override
+ public String toString() {
+ return type.toString();
+ }
+
+ @Override
+ protected ImmutableList<AnnoInfo> annos() {
+ return type.annos();
+ }
+ }
+
+ /** A {@link WildcardType} implementation backed by a {@link WildTy}. */
+ static class TurbineWildcardType extends TurbineTypeMirror implements WildcardType {
+
+ @Override
+ public Type asTurbineType() {
+ return type;
+ }
+
+ private final WildTy type;
+
+ public TurbineWildcardType(ModelFactory factory, WildTy type) {
+ super(factory);
+ this.type = type;
+ }
+
+ @Override
+ public TypeKind getKind() {
+ return TypeKind.WILDCARD;
+ }
+
+ @Override
+ public <R, P> R accept(TypeVisitor<R, P> v, P p) {
+ return v.visitWildcard(this, p);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return obj instanceof TurbineWildcardType && type.equals(((TurbineWildcardType) obj).type);
+ }
+
+ @Override
+ public int hashCode() {
+ return type.hashCode();
+ }
+
+ @Override
+ public TypeMirror getExtendsBound() {
+ return type.boundKind() == BoundKind.UPPER ? factory.asTypeMirror(type.bound()) : null;
+ }
+
+ @Override
+ public TypeMirror getSuperBound() {
+ return type.boundKind() == BoundKind.LOWER ? factory.asTypeMirror(type.bound()) : null;
+ }
+
+ @Override
+ protected ImmutableList<AnnoInfo> annos() {
+ return type.annotations();
+ }
+ }
+
+ /** A {@link IntersectionType} implementation backed by a {@link IntersectionTy}. */
+ static class TurbineIntersectionType extends TurbineTypeMirror implements IntersectionType {
+
+ @Override
+ public Type asTurbineType() {
+ return type;
+ }
+
+ private final IntersectionTy type;
+
+ TurbineIntersectionType(ModelFactory factory, IntersectionTy type) {
+ super(factory);
+ this.type = type;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return obj instanceof TurbineIntersectionType
+ && type.equals(((TurbineIntersectionType) obj).type);
+ }
+
+ @Override
+ public int hashCode() {
+ return type.hashCode();
+ }
+
+ @Override
+ public TypeKind getKind() {
+ return TypeKind.INTERSECTION;
+ }
+
+ @Override
+ public <R, P> R accept(TypeVisitor<R, P> v, P p) {
+ return v.visitIntersection(this, p);
+ }
+
+ final Supplier<ImmutableList<TypeMirror>> bounds =
+ factory.memoize(
+ new Supplier<ImmutableList<TypeMirror>>() {
+ @Override
+ public ImmutableList<TypeMirror> get() {
+ return factory.asTypeMirrors(TurbineTypes.getBounds(factory, type));
+ }
+ });
+
+ @Override
+ public List<? extends TypeMirror> getBounds() {
+ return bounds.get();
+ }
+
+ @Override
+ public String toString() {
+ return Joiner.on('&').join(getBounds());
+ }
+
+ @Override
+ protected ImmutableList<AnnoInfo> annos() {
+ return ImmutableList.of();
+ }
+ }
+
+ /** A {@link NullType} implementation. */
+ public static class TurbineNullType extends TurbineTypeMirror implements NullType {
+
+ @Override
+ public Type asTurbineType() {
+ return Type.PrimTy.create(TurbineConstantTypeKind.NULL, ImmutableList.of());
+ }
+
+ public TurbineNullType(ModelFactory factory) {
+ super(factory);
+ }
+
+ @Override
+ protected ImmutableList<AnnoInfo> annos() {
+ return ImmutableList.of();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return obj instanceof NullType;
+ }
+
+ @Override
+ public int hashCode() {
+ return super.hashCode();
+ }
+
+ @Override
+ public TypeKind getKind() {
+ return TypeKind.NULL;
+ }
+
+ @Override
+ public <R, P> R accept(TypeVisitor<R, P> v, P p) {
+ return v.visitNull(this, p);
+ }
+ }
+
+ /** An {@link ExecutableType} implementation backed by a {@link MethodTy}. */
+ public static class TurbineExecutableType extends TurbineTypeMirror implements ExecutableType {
+
+ @Override
+ public String toString() {
+ return type.toString();
+ }
+
+ @Override
+ public MethodTy asTurbineType() {
+ return type;
+ }
+
+ public final MethodTy type;
+
+ TurbineExecutableType(ModelFactory factory, MethodTy type) {
+ super(factory);
+ this.type = type;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ return obj instanceof TurbineExecutableType
+ && type.equals(((TurbineExecutableType) obj).type);
+ }
+
+ @Override
+ public int hashCode() {
+ return type.hashCode();
+ }
+
+ @Override
+ public List<? extends TypeVariable> getTypeVariables() {
+ ImmutableList.Builder<TypeVariable> result = ImmutableList.builder();
+ for (TyVarSymbol tyVar : type.tyParams()) {
+ result.add((TypeVariable) factory.asTypeMirror(TyVar.create(tyVar, ImmutableList.of())));
+ }
+ return result.build();
+ }
+
+ @Override
+ public TypeMirror getReturnType() {
+ return factory.asTypeMirror(type.returnType());
+ }
+
+ @Override
+ public List<? extends TypeMirror> getParameterTypes() {
+ return factory.asTypeMirrors(type.parameters());
+ }
+
+ @Override
+ public TypeMirror getReceiverType() {
+ return type.receiverType() != null
+ ? factory.asTypeMirror(type.receiverType())
+ : factory.noType();
+ }
+
+ @Override
+ public List<? extends TypeMirror> getThrownTypes() {
+ return factory.asTypeMirrors(type.thrown());
+ }
+
+ @Override
+ public TypeKind getKind() {
+ return TypeKind.EXECUTABLE;
+ }
+
+ @Override
+ public <R, P> R accept(TypeVisitor<R, P> v, P p) {
+ return v.visitExecutable(this, p);
+ }
+
+ @Override
+ protected ImmutableList<AnnoInfo> annos() {
+ return ImmutableList.of();
+ }
+ }
+}
diff --git a/java/com/google/turbine/processing/TurbineTypes.java b/java/com/google/turbine/processing/TurbineTypes.java
new file mode 100644
index 0000000..f65f921
--- /dev/null
+++ b/java/com/google/turbine/processing/TurbineTypes.java
@@ -0,0 +1,1132 @@
+/*
+ * Copyright 2019 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.processing;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Verify.verify;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.base.Function;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.turbine.binder.bound.TypeBoundClass;
+import com.google.turbine.binder.bound.TypeBoundClass.TyVarInfo;
+import com.google.turbine.binder.sym.ClassSymbol;
+import com.google.turbine.binder.sym.FieldSymbol;
+import com.google.turbine.binder.sym.MethodSymbol;
+import com.google.turbine.binder.sym.ParamSymbol;
+import com.google.turbine.binder.sym.Symbol;
+import com.google.turbine.binder.sym.TyVarSymbol;
+import com.google.turbine.model.TurbineConstantTypeKind;
+import com.google.turbine.model.TurbineTyKind;
+import com.google.turbine.processing.TurbineElement.TurbineExecutableElement;
+import com.google.turbine.processing.TurbineElement.TurbineFieldElement;
+import com.google.turbine.processing.TurbineElement.TurbineTypeElement;
+import com.google.turbine.processing.TurbineElement.TurbineTypeParameterElement;
+import com.google.turbine.processing.TurbineTypeMirror.TurbineDeclaredType;
+import com.google.turbine.processing.TurbineTypeMirror.TurbineErrorType;
+import com.google.turbine.processing.TurbineTypeMirror.TurbineTypeVariable;
+import com.google.turbine.type.Type;
+import com.google.turbine.type.Type.ArrayTy;
+import com.google.turbine.type.Type.ClassTy;
+import com.google.turbine.type.Type.ClassTy.SimpleClassTy;
+import com.google.turbine.type.Type.IntersectionTy;
+import com.google.turbine.type.Type.MethodTy;
+import com.google.turbine.type.Type.PrimTy;
+import com.google.turbine.type.Type.TyKind;
+import com.google.turbine.type.Type.TyVar;
+import com.google.turbine.type.Type.WildTy;
+import com.google.turbine.type.Type.WildTy.BoundKind;
+import com.google.turbine.type.Type.WildUnboundedTy;
+import com.google.turbine.types.Erasure;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.type.ArrayType;
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.ExecutableType;
+import javax.lang.model.type.NoType;
+import javax.lang.model.type.NullType;
+import javax.lang.model.type.PrimitiveType;
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.type.WildcardType;
+import javax.lang.model.util.Types;
+import org.checkerframework.checker.nullness.qual.Nullable;
+
+/** An implementation of {@link Types} backed by turbine's {@link TypeMirror}. */
+public class TurbineTypes implements Types {
+
+ private final ModelFactory factory;
+
+ public TurbineTypes(ModelFactory factory) {
+ this.factory = factory;
+ }
+
+ private static Type asTurbineType(TypeMirror typeMirror) {
+ if (!(typeMirror instanceof TurbineTypeMirror)) {
+ throw new IllegalArgumentException(typeMirror.toString());
+ }
+ return ((TurbineTypeMirror) typeMirror).asTurbineType();
+ }
+
+ @Override
+ public Element asElement(TypeMirror t) {
+ switch (t.getKind()) {
+ case DECLARED:
+ return ((TurbineDeclaredType) t).asElement();
+ case TYPEVAR:
+ return ((TurbineTypeVariable) t).asElement();
+ case ERROR:
+ return ((TurbineErrorType) t).asElement();
+ default:
+ return null;
+ }
+ }
+
+ @Override
+ public boolean isSameType(TypeMirror a, TypeMirror b) {
+ Type t1 = asTurbineType(a);
+ Type t2 = asTurbineType(b);
+ if (t1.tyKind() == TyKind.WILD_TY || t2.tyKind() == TyKind.WILD_TY) {
+ // wild card types that appear at the top-level are never equal to each other.
+ // Note that generics parameterized by wildcards may be equal, so the recursive
+ // `isSameType(Type, Type)` below does handle wildcards.
+ return false;
+ }
+ return isSameType(t1, t2);
+ }
+
+ private boolean isSameType(Type a, Type b) {
+ switch (a.tyKind()) {
+ case PRIM_TY:
+ return b.tyKind() == TyKind.PRIM_TY && ((PrimTy) a).primkind() == ((PrimTy) b).primkind();
+ case VOID_TY:
+ return b.tyKind() == TyKind.VOID_TY;
+ case NONE_TY:
+ return b.tyKind() == TyKind.NONE_TY;
+ case CLASS_TY:
+ return isSameClassType((ClassTy) a, b);
+ case ARRAY_TY:
+ return b.tyKind() == TyKind.ARRAY_TY
+ && isSameType(((ArrayTy) a).elementType(), ((ArrayTy) b).elementType());
+ case TY_VAR:
+ return b.tyKind() == TyKind.TY_VAR && ((TyVar) a).sym().equals(((TyVar) b).sym());
+ case WILD_TY:
+ return isSameWildType((WildTy) a, b);
+ case INTERSECTION_TY:
+ return b.tyKind() == TyKind.INTERSECTION_TY
+ && isSameIntersectionType((IntersectionTy) a, (IntersectionTy) b);
+ case METHOD_TY:
+ return b.tyKind() == TyKind.METHOD_TY && isSameMethodType((MethodTy) a, (MethodTy) b);
+ case ERROR_TY:
+ return false;
+ }
+ throw new AssertionError(a.tyKind());
+ }
+
+ /**
+ * Returns true if the given method types are equivalent.
+ *
+ * <p>Receiver parameters are ignored, regardless of whether they were explicitly specified in
+ * source. Thrown exception types are also ignored.
+ */
+ private boolean isSameMethodType(MethodTy a, MethodTy b) {
+ ImmutableMap<TyVarSymbol, Type> mapping = getMapping(a, b);
+ if (mapping == null) {
+ return false;
+ }
+ if (!sameTypeParameterBounds(a, b, mapping)) {
+ return false;
+ }
+ if (!isSameType(a.returnType(), subst(b.returnType(), mapping))) {
+ return false;
+ }
+ if (!isSameTypes(a.parameters(), substAll(b.parameters(), mapping))) {
+ return false;
+ }
+ return true;
+ }
+
+ private boolean sameTypeParameterBounds(
+ MethodTy a, MethodTy b, ImmutableMap<TyVarSymbol, Type> mapping) {
+ if (a.tyParams().size() != b.tyParams().size()) {
+ return false;
+ }
+ Iterator<TyVarSymbol> ax = a.tyParams().iterator();
+ Iterator<TyVarSymbol> bx = b.tyParams().iterator();
+ while (ax.hasNext()) {
+ TyVarSymbol x = ax.next();
+ TyVarSymbol y = bx.next();
+ if (!isSameType(
+ factory.getTyVarInfo(x).upperBound(),
+ subst(factory.getTyVarInfo(y).upperBound(), mapping))) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private boolean isSameTypes(ImmutableList<Type> a, ImmutableList<Type> b) {
+ if (a.size() != b.size()) {
+ return false;
+ }
+ Iterator<Type> ax = a.iterator();
+ Iterator<Type> bx = b.iterator();
+ while (ax.hasNext()) {
+ if (!isSameType(ax.next(), bx.next())) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private boolean isSameIntersectionType(IntersectionTy a, IntersectionTy b) {
+ return isSameTypes(getBounds(a), getBounds(b));
+ }
+
+ private ImmutableList<Type> getBounds(IntersectionTy a) {
+ return getBounds(factory, a);
+ }
+
+ static ImmutableList<Type> getBounds(ModelFactory factory, IntersectionTy type) {
+ ImmutableList<Type> bounds = type.bounds();
+ if (implicitObjectBound(factory, bounds)) {
+ return ImmutableList.<Type>builder().add(ClassTy.OBJECT).addAll(bounds).build();
+ }
+ return bounds;
+ }
+
+ private static boolean implicitObjectBound(ModelFactory factory, ImmutableList<Type> bounds) {
+ if (bounds.isEmpty()) {
+ return true;
+ }
+ ClassTy first = (ClassTy) bounds.get(0);
+ return factory.getSymbol(first.sym()).kind().equals(TurbineTyKind.INTERFACE);
+ }
+
+ private boolean isSameWildType(WildTy a, Type other) {
+ switch (other.tyKind()) {
+ case WILD_TY:
+ break;
+ case CLASS_TY:
+ // `? super Object` = Object
+ return ((ClassTy) other).sym().equals(ClassSymbol.OBJECT)
+ && a.boundKind() == BoundKind.LOWER
+ && a.bound().tyKind() == TyKind.CLASS_TY
+ && ((ClassTy) a.bound()).sym().equals(ClassSymbol.OBJECT);
+ default:
+ return false;
+ }
+ WildTy b = (WildTy) other;
+ switch (a.boundKind()) {
+ case NONE:
+ switch (b.boundKind()) {
+ case UPPER:
+ // `?` = `? extends Object`
+ return isObjectType(b.bound());
+ case LOWER:
+ return false;
+ case NONE:
+ return true;
+ }
+ break;
+ case UPPER:
+ switch (b.boundKind()) {
+ case UPPER:
+ return isSameType(a.bound(), b.bound());
+ case LOWER:
+ return false;
+ case NONE:
+ // `? extends Object` = `?`
+ return isObjectType(a.bound());
+ }
+ break;
+ case LOWER:
+ return b.boundKind() == BoundKind.LOWER && isSameType(a.bound(), b.bound());
+ }
+ throw new AssertionError(a.boundKind());
+ }
+
+ private boolean isSameClassType(ClassTy a, Type other) {
+ switch (other.tyKind()) {
+ case CLASS_TY:
+ break;
+ case WILD_TY:
+ WildTy w = (WildTy) other;
+ return a.sym().equals(ClassSymbol.OBJECT)
+ && w.boundKind() == BoundKind.LOWER
+ && w.bound().tyKind() == TyKind.CLASS_TY
+ && ((ClassTy) w.bound()).sym().equals(ClassSymbol.OBJECT);
+ default:
+ return false;
+ }
+ ClassTy b = (ClassTy) other;
+ if (!a.sym().equals(b.sym())) {
+ return false;
+ }
+ Iterator<SimpleClassTy> ax = a.classes().reverse().iterator();
+ Iterator<SimpleClassTy> bx = b.classes().reverse().iterator();
+ while (ax.hasNext() && bx.hasNext()) {
+ if (!isSameSimpleClassType(ax.next(), bx.next())) {
+ return false;
+ }
+ }
+ // The class type may be in non-canonical form, e.g. may or may not have entries in 'classes'
+ // corresponding to enclosing instances. Don't require the enclosing instances' representations
+ // to be identical unless one of them has type arguments.
+ if (hasTyArgs(ax) || hasTyArgs(bx)) {
+ return false;
+ }
+ return true;
+ }
+
+ /** Returns true if any {@link SimpleClassTy} in the given iterator has type arguments. */
+ private static boolean hasTyArgs(Iterator<SimpleClassTy> it) {
+ while (it.hasNext()) {
+ if (!it.next().targs().isEmpty()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private boolean isSameSimpleClassType(SimpleClassTy a, SimpleClassTy b) {
+ return a.sym().equals(b.sym()) && isSameTypes(a.targs(), b.targs());
+ }
+
+ /** Returns true if type {@code a} is a subtype of type {@code b}. See JLS 4.1.0, 'subtyping'. */
+ @Override
+ public boolean isSubtype(TypeMirror a, TypeMirror b) {
+ return isSubtype(asTurbineType(a), asTurbineType(b), /* strict= */ true);
+ }
+
+ /**
+ * Returns true if type {@code a} is a subtype of type {@code b}. See JLS 4.1.0, 'subtyping'.
+ *
+ * @param strict true if raw types should not be considered subtypes of parameterized types. See
+ * also {@link #isAssignable}, which sets {@code strict} to {@code false} to handle unchecked
+ * conversions.
+ */
+ private boolean isSubtype(Type a, Type b, boolean strict) {
+ if (b.tyKind() == TyKind.INTERSECTION_TY) {
+ for (Type bound : getBounds((IntersectionTy) b)) {
+ // TODO(cushon): javac rejects e.g. `|List| isAssignable Serializable&ArrayList<?>`,
+ // i.e. it does a strict subtype test against the intersection type. Is that a bug?
+ if (!isSubtype(a, bound, /* strict= */ true)) {
+ return false;
+ }
+ }
+ return true;
+ }
+ switch (a.tyKind()) {
+ case CLASS_TY:
+ return isClassSubtype((ClassTy) a, b, strict);
+ case PRIM_TY:
+ return isPrimSubtype((PrimTy) a, b);
+ case ARRAY_TY:
+ return isArraySubtype((ArrayTy) a, b, strict);
+ case TY_VAR:
+ return isTyVarSubtype((TyVar) a, b, strict);
+ case INTERSECTION_TY:
+ return isIntersectionSubtype((IntersectionTy) a, b, strict);
+ case VOID_TY:
+ return b.tyKind() == TyKind.VOID_TY;
+ case NONE_TY:
+ return b.tyKind() == TyKind.NONE_TY;
+ case WILD_TY:
+ // TODO(cushon): javac takes wildcards as input to isSubtype and sometimes returns `true`,
+ // see JDK-8039198
+ return false;
+ case ERROR_TY:
+ // for compatibility with javac, treat error as bottom
+ return true;
+ case METHOD_TY:
+ return false;
+ }
+ throw new AssertionError(a.tyKind());
+ }
+
+ private boolean isTyVarSubtype(TyVar a, Type b, boolean strict) {
+ if (b.tyKind() == TyKind.TY_VAR) {
+ return a.sym().equals(((TyVar) b).sym());
+ }
+ TyVarInfo tyVarInfo = factory.getTyVarInfo(a.sym());
+ return isSubtype(tyVarInfo.upperBound(), b, strict);
+ }
+
+ private boolean isIntersectionSubtype(IntersectionTy a, Type b, boolean strict) {
+ for (Type bound : getBounds(a)) {
+ if (isSubtype(bound, b, strict)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ // see JLS 4.10.3, 'subtyping among array types'
+ // https://docs.oracle.com/javase/specs/jls/se11/html/jls-4.html#jls-4.10.3
+ private boolean isArraySubtype(ArrayTy a, Type b, boolean strict) {
+ switch (b.tyKind()) {
+ case ARRAY_TY:
+ Type ae = a.elementType();
+ Type be = ((ArrayTy) b).elementType();
+ if (ae.tyKind() == TyKind.PRIM_TY) {
+ return isSameType(ae, be);
+ }
+ return isSubtype(ae, be, strict);
+ case CLASS_TY:
+ ClassSymbol bsym = ((ClassTy) b).sym();
+ switch (bsym.binaryName()) {
+ case "java/lang/Object":
+ case "java/lang/Cloneable":
+ case "java/io/Serializable":
+ return true;
+ default:
+ return false;
+ }
+ default:
+ return false;
+ }
+ }
+
+ // see JLS 4.10.1, 'subtyping among primitive types'
+ // https://docs.oracle.com/javase/specs/jls/se11/html/jls-4.html#jls-4.10.1
+ private static boolean isPrimSubtype(PrimTy a, Type other) {
+ if (other.tyKind() != TyKind.PRIM_TY) {
+ return false;
+ }
+ PrimTy b = (PrimTy) other;
+ switch (a.primkind()) {
+ case CHAR:
+ switch (b.primkind()) {
+ case CHAR:
+ case INT:
+ case LONG:
+ case FLOAT:
+ case DOUBLE:
+ return true;
+ default:
+ return false;
+ }
+ case BYTE:
+ switch (b.primkind()) {
+ case BYTE:
+ case SHORT:
+ case INT:
+ case LONG:
+ case FLOAT:
+ case DOUBLE:
+ return true;
+ default:
+ return false;
+ }
+ case SHORT:
+ switch (b.primkind()) {
+ case SHORT:
+ case INT:
+ case LONG:
+ case FLOAT:
+ case DOUBLE:
+ return true;
+ default:
+ return false;
+ }
+ case INT:
+ switch (b.primkind()) {
+ case INT:
+ case LONG:
+ case FLOAT:
+ case DOUBLE:
+ return true;
+ default:
+ return false;
+ }
+ case LONG:
+ switch (b.primkind()) {
+ case LONG:
+ case FLOAT:
+ case DOUBLE:
+ return true;
+ default:
+ return false;
+ }
+ case FLOAT:
+ switch (b.primkind()) {
+ case FLOAT:
+ case DOUBLE:
+ return true;
+ default:
+ return false;
+ }
+ case DOUBLE:
+ case STRING:
+ case BOOLEAN:
+ return a.primkind() == b.primkind();
+ case NULL:
+ break;
+ }
+ throw new AssertionError(a.primkind());
+ }
+
+ private boolean isClassSubtype(ClassTy a, Type other, boolean strict) {
+ if (other.tyKind() != TyKind.CLASS_TY) {
+ return false;
+ }
+ ClassTy b = (ClassTy) other;
+ if (!a.sym().equals(b.sym())) {
+ // find a path from a to b in the type hierarchy
+ ImmutableList<ClassTy> path = factory.cha().search(a, b.sym());
+ if (path.isEmpty()) {
+ return false;
+ }
+ // perform repeated type substitution to get an instance of B with the type arguments
+ // provided by A
+ a = path.get(0);
+ for (ClassTy ty : path) {
+ ImmutableMap<TyVarSymbol, Type> mapping = getMapping(ty);
+ if (mapping == null) {
+ // if we encounter a raw type on the path from A to B the result is erased
+ a = (ClassTy) erasure(a);
+ break;
+ }
+ a = substClassTy(a, mapping);
+ }
+ }
+ Iterator<SimpleClassTy> ax = a.classes().reverse().iterator();
+ Iterator<SimpleClassTy> bx = b.classes().reverse().iterator();
+ while (ax.hasNext() && bx.hasNext()) {
+ if (!tyArgsContains(ax.next(), bx.next(), strict)) {
+ return false;
+ }
+ }
+ return !hasTyArgs(ax) && !hasTyArgs(bx);
+ }
+
+ /**
+ * Given two parameterizations of the same {@link SimpleClassTy}, {@code a} and {@code b}, teturns
+ * true if the type arguments of {@code a} are pairwise contained by the type arguments of {@code
+ * b}.
+ *
+ * @see {@link #contains} and JLS 4.5.1.
+ */
+ private boolean tyArgsContains(SimpleClassTy a, SimpleClassTy b, boolean strict) {
+ verify(a.sym().equals(b.sym()));
+ Iterator<Type> ax = a.targs().iterator();
+ Iterator<Type> bx = b.targs().iterator();
+ while (ax.hasNext() && bx.hasNext()) {
+ if (!containedBy(ax.next(), bx.next(), strict)) {
+ return false;
+ }
+ }
+ // C<F1, ..., FN> <= |C|, but |C| is not a subtype of C<F1, ..., FN>
+ // https://docs.oracle.com/javase/specs/jls/se11/html/jls-4.html#jls-4.8
+ if (strict) {
+ return !bx.hasNext();
+ }
+ return true;
+ }
+
+ private Type subst(Type type, Map<TyVarSymbol, Type> mapping) {
+ switch (type.tyKind()) {
+ case CLASS_TY:
+ return substClassTy((ClassTy) type, mapping);
+ case ARRAY_TY:
+ return substArrayTy((ArrayTy) type, mapping);
+ case TY_VAR:
+ return substTyVar((TyVar) type, mapping);
+ case PRIM_TY:
+ case VOID_TY:
+ case NONE_TY:
+ case ERROR_TY:
+ return type;
+ case METHOD_TY:
+ return substMethod((MethodTy) type, mapping);
+ case INTERSECTION_TY:
+ return substIntersectionTy((IntersectionTy) type, mapping);
+ case WILD_TY:
+ return substWildTy((WildTy) type, mapping);
+ }
+ throw new AssertionError(type.tyKind());
+ }
+
+ private Type substWildTy(WildTy type, Map<TyVarSymbol, Type> mapping) {
+ switch (type.boundKind()) {
+ case NONE:
+ return type;
+ case UPPER:
+ return Type.WildUpperBoundedTy.create(subst(type.bound(), mapping), ImmutableList.of());
+ case LOWER:
+ return Type.WildLowerBoundedTy.create(subst(type.bound(), mapping), ImmutableList.of());
+ }
+ throw new AssertionError(type.boundKind());
+ }
+
+ private Type substIntersectionTy(IntersectionTy type, Map<TyVarSymbol, Type> mapping) {
+ return IntersectionTy.create(substAll(getBounds(type), mapping));
+ }
+
+ private MethodTy substMethod(MethodTy method, Map<TyVarSymbol, Type> mapping) {
+ return MethodTy.create(
+ method.tyParams(),
+ subst(method.returnType(), mapping),
+ method.receiverType() != null ? subst(method.receiverType(), mapping) : null,
+ substAll(method.parameters(), mapping),
+ substAll(method.thrown(), mapping));
+ }
+
+ private ImmutableList<Type> substAll(
+ ImmutableList<? extends Type> types, Map<TyVarSymbol, Type> mapping) {
+ ImmutableList.Builder<Type> result = ImmutableList.builder();
+ for (Type type : types) {
+ result.add(subst(type, mapping));
+ }
+ return result.build();
+ }
+
+ private Type substTyVar(TyVar type, Map<TyVarSymbol, Type> mapping) {
+ return mapping.getOrDefault(type.sym(), type);
+ }
+
+ private Type substArrayTy(ArrayTy type, Map<TyVarSymbol, Type> mapping) {
+ return ArrayTy.create(subst(type.elementType(), mapping), type.annos());
+ }
+
+ private ClassTy substClassTy(ClassTy type, Map<TyVarSymbol, Type> mapping) {
+ ImmutableList.Builder<SimpleClassTy> simples = ImmutableList.builder();
+ for (SimpleClassTy simple : type.classes()) {
+ ImmutableList.Builder<Type> args = ImmutableList.builder();
+ for (Type arg : simple.targs()) {
+ args.add(subst(arg, mapping));
+ }
+ simples.add(SimpleClassTy.create(simple.sym(), args.build(), simple.annos()));
+ }
+ return ClassTy.create(simples.build());
+ }
+
+ /**
+ * Returns a mapping that can be used to adapt the signature 'b' to the type parameters of 'a', or
+ * {@code null} if no such mapping exists.
+ */
+ @Nullable
+ private static ImmutableMap<TyVarSymbol, Type> getMapping(MethodTy a, MethodTy b) {
+ if (a.tyParams().size() != b.tyParams().size()) {
+ return null;
+ }
+ Iterator<TyVarSymbol> ax = a.tyParams().iterator();
+ Iterator<TyVarSymbol> bx = b.tyParams().iterator();
+ ImmutableMap.Builder<TyVarSymbol, Type> mapping = ImmutableMap.builder();
+ while (ax.hasNext()) {
+ TyVarSymbol s = ax.next();
+ TyVarSymbol t = bx.next();
+ mapping.put(t, TyVar.create(s, ImmutableList.of()));
+ }
+ return mapping.build();
+ }
+
+ /**
+ * Returns a map from formal type parameters to their arguments for a given class type, or an
+ * empty map for non-parameterized types, or {@code null} for raw types.
+ */
+ @Nullable
+ private ImmutableMap<TyVarSymbol, Type> getMapping(ClassTy ty) {
+ ImmutableMap.Builder<TyVarSymbol, Type> mapping = ImmutableMap.builder();
+ for (SimpleClassTy s : ty.classes()) {
+ TypeBoundClass info = factory.getSymbol(s.sym());
+ if (s.targs().isEmpty() && !info.typeParameters().isEmpty()) {
+ return null; // rawtypes
+ }
+ Iterator<TyVarSymbol> ax = info.typeParameters().values().iterator();
+ Iterator<Type> bx = s.targs().iterator();
+ while (ax.hasNext()) {
+ mapping.put(ax.next(), bx.next());
+ }
+ verify(!bx.hasNext());
+ }
+ return mapping.build();
+ }
+
+ @Override
+ public boolean isAssignable(TypeMirror a1, TypeMirror a2) {
+ return isAssignable(asTurbineType(a1), asTurbineType(a2));
+ }
+
+ private boolean isAssignable(Type t1, Type t2) {
+ switch (t1.tyKind()) {
+ case PRIM_TY:
+ if (t2.tyKind() == TyKind.CLASS_TY) {
+ ClassSymbol boxed = boxedClass(((PrimTy) t1).primkind());
+ t1 = ClassTy.asNonParametricClassTy(boxed);
+ }
+ break;
+ case CLASS_TY:
+ switch (t2.tyKind()) {
+ case PRIM_TY:
+ TurbineConstantTypeKind unboxed = unboxedType((ClassTy) t1);
+ if (unboxed == null) {
+ return false;
+ }
+ t1 = PrimTy.create(unboxed, ImmutableList.of());
+ break;
+ case CLASS_TY:
+ break;
+ default: // fall out
+ }
+ break;
+ default: // fall out
+ }
+ return isSubtype(t1, t2, /* strict= */ false);
+ }
+
+ private static boolean isObjectType(Type type) {
+ return type.tyKind() == TyKind.CLASS_TY && ((ClassTy) type).sym().equals(ClassSymbol.OBJECT);
+ }
+
+ @Override
+ public boolean contains(TypeMirror a, TypeMirror b) {
+ return contains(asTurbineType(a), asTurbineType(b), /* strict= */ true);
+ }
+
+ private boolean contains(Type t1, Type t2, boolean strict) {
+ return containedBy(t2, t1, strict);
+ }
+
+ // See JLS 4.5.1, 'type containment'
+ // https://docs.oracle.com/javase/specs/jls/se11/html/jls-4.html#jls-4.5.1
+ private boolean containedBy(Type t1, Type t2, boolean strict) {
+ if (t1.tyKind() == TyKind.WILD_TY) {
+ WildTy w1 = (WildTy) t1;
+ Type t;
+ switch (w1.boundKind()) {
+ case UPPER:
+ t = w1.bound();
+ if (t2.tyKind() == TyKind.WILD_TY) {
+ WildTy w2 = (WildTy) t2;
+ switch (w2.boundKind()) {
+ case UPPER:
+ // ? extends T <= ? extends S if T <: S
+ return isSubtype(t, w2.bound(), strict);
+ case NONE:
+ // ? extends T <= ? [extends Object] if T <: Object
+ return true;
+ case LOWER:
+ // ? extends T <= ? super S
+ return false;
+ }
+ throw new AssertionError(w1.boundKind());
+ }
+ return false;
+ case LOWER:
+ t = w1.bound();
+ if (t2.tyKind() == TyKind.WILD_TY) {
+ WildTy w2 = (WildTy) t2;
+ switch (w2.boundKind()) {
+ case LOWER:
+ // ? super T <= ? super S if S <: T
+ return isSubtype(w2.bound(), t, strict);
+ case NONE:
+ // ? super T <= ? [extends Object]
+ return true;
+ case UPPER:
+ // ? super T <= ? extends Object
+ return isObjectType(w2.bound());
+ }
+ throw new AssertionError(w2.boundKind());
+ }
+ // ? super Object <= Object
+ return isObjectType(t2) && isObjectType(t);
+ case NONE:
+ if (t2.tyKind() == TyKind.WILD_TY) {
+ WildTy w2 = (WildTy) t2;
+ switch (w2.boundKind()) {
+ case NONE:
+ // ? [extends Object] <= ? extends Object
+ return true;
+ case LOWER:
+ // ? [extends Object] <= ? super S
+ return false;
+ case UPPER:
+ // ? [extends Object] <= ? extends S if Object <: S
+ return isObjectType(w2.bound());
+ }
+ throw new AssertionError(w2.boundKind());
+ }
+ return false;
+ }
+ throw new AssertionError(w1.boundKind());
+ }
+ if (t2.tyKind() == TyKind.WILD_TY) {
+ WildTy w2 = (WildTy) t2;
+ switch (w2.boundKind()) {
+ case LOWER:
+ // T <= ? super S
+ return isSubtype(w2.bound(), t1, strict);
+ case UPPER:
+ // T <= ? extends S
+ return isSubtype(t1, w2.bound(), strict);
+ case NONE:
+ // T <= ? [extends Object]
+ return true;
+ }
+ throw new AssertionError(w2.boundKind());
+ }
+ if (isSameType(t1, t2)) {
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public boolean isSubsignature(ExecutableType m1, ExecutableType m2) {
+ return isSubsignature((MethodTy) asTurbineType(m1), (MethodTy) asTurbineType(m2));
+ }
+
+ private boolean isSubsignature(MethodTy a, MethodTy b) {
+ return isSameSignature(a, b) || isSameSignature(a, (MethodTy) erasure(b));
+ }
+
+ private boolean isSameSignature(MethodTy a, MethodTy b) {
+ if (a.parameters().size() != b.parameters().size()) {
+ return false;
+ }
+ ImmutableMap<TyVarSymbol, Type> mapping = getMapping(a, b);
+ if (mapping == null) {
+ return false;
+ }
+ if (!sameTypeParameterBounds(a, b, mapping)) {
+ return false;
+ }
+ Iterator<Type> ax = a.parameters().iterator();
+ // adapt the formal parameter types of 'b' to the type parameters of 'a'
+ Iterator<Type> bx = substAll(b.parameters(), mapping).iterator();
+ while (ax.hasNext()) {
+ if (!isSameType(ax.next(), bx.next())) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public List<? extends TypeMirror> directSupertypes(TypeMirror m) {
+ return factory.asTypeMirrors(directSupertypes(asTurbineType(m)));
+ }
+
+ public ImmutableList<Type> directSupertypes(Type t) {
+ switch (t.tyKind()) {
+ case CLASS_TY:
+ return directSupertypes((ClassTy) t);
+ case INTERSECTION_TY:
+ return ((IntersectionTy) t).bounds();
+ case TY_VAR:
+ return getBounds(factory.getTyVarInfo(((TyVar) t).sym()).upperBound());
+ case ARRAY_TY:
+ return directSupertypes((ArrayTy) t);
+ case PRIM_TY:
+ case VOID_TY:
+ case WILD_TY:
+ case ERROR_TY:
+ case NONE_TY:
+ return ImmutableList.of();
+ case METHOD_TY:
+ break;
+ }
+ throw new IllegalArgumentException(t.tyKind().name());
+ }
+
+ private ImmutableList<Type> directSupertypes(ArrayTy t) {
+ Type elem = t.elementType();
+ if (elem.tyKind() == TyKind.PRIM_TY || isObjectType(elem)) {
+ return ImmutableList.of(
+ IntersectionTy.create(
+ ImmutableList.of(ClassTy.OBJECT, ClassTy.SERIALIZABLE, ClassTy.CLONEABLE)));
+ }
+ ImmutableList<Type> ex = directSupertypes(elem);
+ return ImmutableList.of(ArrayTy.create(ex.iterator().next(), ImmutableList.of()));
+ }
+
+ private ImmutableList<Type> directSupertypes(ClassTy t) {
+ if (t.sym().equals(ClassSymbol.OBJECT)) {
+ return ImmutableList.of();
+ }
+ TypeBoundClass info = factory.getSymbol(t.sym());
+ Map<TyVarSymbol, Type> mapping = getMapping(t);
+ boolean raw = mapping == null;
+ ImmutableList.Builder<Type> builder = ImmutableList.builder();
+ if (info.superClassType() != null) {
+ builder.add(raw ? erasure(info.superClassType()) : subst(info.superClassType(), mapping));
+ } else {
+ builder.add(ClassTy.OBJECT);
+ }
+ for (Type interfaceType : info.interfaceTypes()) {
+ builder.add(raw ? erasure(interfaceType) : subst(interfaceType, mapping));
+ }
+ return builder.build();
+ }
+
+ @Override
+ public TypeMirror erasure(TypeMirror typeMirror) {
+ return factory.asTypeMirror(erasure(asTurbineType(typeMirror)));
+ }
+
+ private Type erasure(Type type) {
+ return Erasure.erase(
+ type,
+ new Function<TyVarSymbol, TyVarInfo>() {
+ @Override
+ public TyVarInfo apply(TyVarSymbol input) {
+ return factory.getTyVarInfo(input);
+ }
+ });
+ }
+
+ @Override
+ public TypeElement boxedClass(PrimitiveType p) {
+ return factory.typeElement(boxedClass(((PrimTy) asTurbineType(p)).primkind()));
+ }
+
+ static ClassSymbol boxedClass(TurbineConstantTypeKind kind) {
+ switch (kind) {
+ case CHAR:
+ return ClassSymbol.CHARACTER;
+ case SHORT:
+ return ClassSymbol.SHORT;
+ case INT:
+ return ClassSymbol.INTEGER;
+ case LONG:
+ return ClassSymbol.LONG;
+ case FLOAT:
+ return ClassSymbol.FLOAT;
+ case DOUBLE:
+ return ClassSymbol.DOUBLE;
+ case BOOLEAN:
+ return ClassSymbol.BOOLEAN;
+ case BYTE:
+ return ClassSymbol.BYTE;
+ case STRING:
+ case NULL:
+ break;
+ }
+ throw new AssertionError(kind);
+ }
+
+ @Override
+ public PrimitiveType unboxedType(TypeMirror typeMirror) {
+ Type type = asTurbineType(typeMirror);
+ if (type.tyKind() != TyKind.CLASS_TY) {
+ throw new IllegalArgumentException(type.toString());
+ }
+ TurbineConstantTypeKind unboxed = unboxedType((ClassTy) type);
+ if (unboxed == null) {
+ throw new IllegalArgumentException(type.toString());
+ }
+ return (PrimitiveType) factory.asTypeMirror(PrimTy.create(unboxed, ImmutableList.of()));
+ }
+
+ private static TurbineConstantTypeKind unboxedType(ClassTy classTy) {
+ switch (classTy.sym().binaryName()) {
+ case "java/lang/Boolean":
+ return TurbineConstantTypeKind.BOOLEAN;
+ case "java/lang/Byte":
+ return TurbineConstantTypeKind.BYTE;
+ case "java/lang/Short":
+ return TurbineConstantTypeKind.SHORT;
+ case "java/lang/Integer":
+ return TurbineConstantTypeKind.INT;
+ case "java/lang/Long":
+ return TurbineConstantTypeKind.LONG;
+ case "java/lang/Character":
+ return TurbineConstantTypeKind.CHAR;
+ case "java/lang/Float":
+ return TurbineConstantTypeKind.FLOAT;
+ case "java/lang/Double":
+ return TurbineConstantTypeKind.DOUBLE;
+ default:
+ return null;
+ }
+ }
+
+ @Override
+ public TypeMirror capture(TypeMirror typeMirror) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public PrimitiveType getPrimitiveType(TypeKind kind) {
+ checkArgument(kind.isPrimitive(), "%s is not a primitive type", kind);
+ return (PrimitiveType)
+ factory.asTypeMirror(PrimTy.create(primitiveType(kind), ImmutableList.of()));
+ }
+
+ private static TurbineConstantTypeKind primitiveType(TypeKind kind) {
+ switch (kind) {
+ case BOOLEAN:
+ return TurbineConstantTypeKind.BOOLEAN;
+ case BYTE:
+ return TurbineConstantTypeKind.BYTE;
+ case SHORT:
+ return TurbineConstantTypeKind.SHORT;
+ case INT:
+ return TurbineConstantTypeKind.INT;
+ case LONG:
+ return TurbineConstantTypeKind.LONG;
+ case CHAR:
+ return TurbineConstantTypeKind.CHAR;
+ case FLOAT:
+ return TurbineConstantTypeKind.FLOAT;
+ case DOUBLE:
+ return TurbineConstantTypeKind.DOUBLE;
+ default:
+ throw new IllegalArgumentException(kind + " is not a primitive type");
+ }
+ }
+
+ @Override
+ public NullType getNullType() {
+ return factory.nullType();
+ }
+
+ @Override
+ public NoType getNoType(TypeKind kind) {
+ switch (kind) {
+ case VOID:
+ return (NoType) factory.asTypeMirror(Type.VOID);
+ case NONE:
+ return factory.noType();
+ default:
+ throw new IllegalArgumentException(kind.toString());
+ }
+ }
+
+ @Override
+ public ArrayType getArrayType(TypeMirror componentType) {
+ return (ArrayType)
+ factory.asTypeMirror(ArrayTy.create(asTurbineType(componentType), ImmutableList.of()));
+ }
+
+ @Override
+ public WildcardType getWildcardType(TypeMirror extendsBound, TypeMirror superBound) {
+ WildTy type;
+ if (extendsBound != null) {
+ type = WildTy.WildUpperBoundedTy.create(asTurbineType(extendsBound), ImmutableList.of());
+ } else if (superBound != null) {
+ type = WildTy.WildLowerBoundedTy.create(asTurbineType(superBound), ImmutableList.of());
+ } else {
+ type = WildUnboundedTy.create(ImmutableList.of());
+ }
+ return (WildcardType) factory.asTypeMirror(type);
+ }
+
+ @Override
+ public DeclaredType getDeclaredType(TypeElement typeElem, TypeMirror... typeArgs) {
+ requireNonNull(typeElem);
+ ImmutableList.Builder<Type> args = ImmutableList.builder();
+ for (TypeMirror t : typeArgs) {
+ args.add(asTurbineType(t));
+ }
+ TurbineTypeElement element = (TurbineTypeElement) typeElem;
+ return (DeclaredType)
+ factory.asTypeMirror(
+ ClassTy.create(
+ ImmutableList.of(
+ SimpleClassTy.create(element.sym(), args.build(), ImmutableList.of()))));
+ }
+
+ @Override
+ public DeclaredType getDeclaredType(
+ DeclaredType containing, TypeElement typeElem, TypeMirror... typeArgs) {
+ if (containing == null) {
+ return getDeclaredType(typeElem, typeArgs);
+ }
+ requireNonNull(typeElem);
+ ClassTy base = (ClassTy) asTurbineType(containing);
+ TurbineTypeElement element = (TurbineTypeElement) typeElem;
+ ImmutableList.Builder<Type> args = ImmutableList.builder();
+ for (TypeMirror t : typeArgs) {
+ args.add(asTurbineType(t));
+ }
+ return (DeclaredType)
+ factory.asTypeMirror(
+ ClassTy.create(
+ ImmutableList.<SimpleClassTy>builder()
+ .addAll(base.classes())
+ .add(SimpleClassTy.create(element.sym(), args.build(), ImmutableList.of()))
+ .build()));
+ }
+
+ private static ClassSymbol enclosingClass(Symbol symbol) {
+ switch (symbol.symKind()) {
+ case CLASS:
+ return (ClassSymbol) symbol;
+ case TY_PARAM:
+ return enclosingClass(((TyVarSymbol) symbol).owner());
+ case METHOD:
+ return ((MethodSymbol) symbol).owner();
+ case FIELD:
+ return ((FieldSymbol) symbol).owner();
+ case PARAMETER:
+ return ((ParamSymbol) symbol).owner().owner();
+ case MODULE:
+ case PACKAGE:
+ throw new IllegalArgumentException(symbol.symKind().toString());
+ }
+ throw new AssertionError(symbol.symKind());
+ }
+
+ private static Type type(Element element) {
+ switch (element.getKind()) {
+ case TYPE_PARAMETER:
+ return TyVar.create(((TurbineTypeParameterElement) element).sym(), ImmutableList.of());
+ case FIELD:
+ return ((TurbineFieldElement) element).info().type();
+ case METHOD:
+ case CONSTRUCTOR:
+ return ((TurbineExecutableElement) element).info().asType();
+ default:
+ throw new UnsupportedOperationException(element.toString());
+ }
+ }
+
+ /**
+ * Returns the {@link TypeMirror} of the given {@code element} as a member of {@code containing},
+ * or else {@code null} if it is not a member.
+ *
+ * <p>e.g. given a class {@code MyStringList} that implements {@code List<String>}, the type of
+ * {@code List.add} would be {@code add(String)}.
+ */
+ @Override
+ public TypeMirror asMemberOf(DeclaredType containing, Element element) {
+ ClassTy c = ((TurbineDeclaredType) containing).asTurbineType();
+ ClassSymbol symbol = enclosingClass(((TurbineElement) element).sym());
+ ImmutableList<ClassTy> path = factory.cha().search(c, enclosingClass(symbol));
+ if (path.isEmpty()) {
+ return null;
+ }
+ Type type = type(element);
+ for (ClassTy ty : path) {
+ ImmutableMap<TyVarSymbol, Type> mapping = getMapping(ty);
+ if (mapping == null) {
+ type = erasure(type);
+ break;
+ }
+ type = subst(type, mapping);
+ }
+ return factory.asTypeMirror(type);
+ }
+}
diff --git a/java/com/google/turbine/tree/Pretty.java b/java/com/google/turbine/tree/Pretty.java
index 2b9374e..b693a42 100644
--- a/java/com/google/turbine/tree/Pretty.java
+++ b/java/com/google/turbine/tree/Pretty.java
@@ -501,8 +501,6 @@ public class Pretty implements Tree.Visitor<Void, Void> {
case ACC_SYNTHETIC:
case ACC_BRIDGE:
break;
- default:
- throw new AssertionError(mod);
}
}
}
diff --git a/java/com/google/turbine/tree/Tree.java b/java/com/google/turbine/tree/Tree.java
index a20b106..d36c3ab 100644
--- a/java/com/google/turbine/tree/Tree.java
+++ b/java/com/google/turbine/tree/Tree.java
@@ -661,6 +661,7 @@ public abstract class Tree {
private final Tree ty;
private final Ident name;
private final Optional<Expression> init;
+ private final String javadoc;
public VarDecl(
int position,
@@ -668,13 +669,15 @@ public abstract class Tree {
ImmutableList<Anno> annos,
Tree ty,
Ident name,
- Optional<Expression> init) {
+ Optional<Expression> init,
+ String javadoc) {
super(position);
this.mods = ImmutableSet.copyOf(mods);
this.annos = annos;
this.ty = ty;
this.name = name;
this.init = init;
+ this.javadoc = javadoc;
}
@Override
@@ -706,6 +709,14 @@ public abstract class Tree {
public Optional<Expression> init() {
return init;
}
+
+ /**
+ * A javadoc comment, excluding the opening and closing delimiters but including all interior
+ * characters and whitespace.
+ */
+ public String javadoc() {
+ return javadoc;
+ }
}
/** A JLS 8.4 method declaration. */
@@ -718,6 +729,7 @@ public abstract class Tree {
private final ImmutableList<VarDecl> params;
private final ImmutableList<ClassTy> exntys;
private final Optional<Tree> defaultValue;
+ private final String javadoc;
public MethDecl(
int position,
@@ -728,7 +740,8 @@ public abstract class Tree {
Ident name,
ImmutableList<VarDecl> params,
ImmutableList<ClassTy> exntys,
- Optional<Tree> defaultValue) {
+ Optional<Tree> defaultValue,
+ String javadoc) {
super(position);
this.mods = ImmutableSet.copyOf(mods);
this.annos = annos;
@@ -738,6 +751,7 @@ public abstract class Tree {
this.params = params;
this.exntys = exntys;
this.defaultValue = defaultValue;
+ this.javadoc = javadoc;
}
@Override
@@ -781,6 +795,13 @@ public abstract class Tree {
public Optional<Tree> defaultValue() {
return defaultValue;
}
+ /**
+ * A javadoc comment, excluding the opening and closing delimiters but including all interior
+ * characters and whitespace.
+ */
+ public String javadoc() {
+ return javadoc;
+ }
}
/** A JLS 9.7 annotation. */
@@ -852,6 +873,7 @@ public abstract class Tree {
private final ImmutableList<ClassTy> impls;
private final ImmutableList<Tree> members;
private final TurbineTyKind tykind;
+ private final String javadoc;
public TyDecl(
int position,
@@ -862,7 +884,8 @@ public abstract class Tree {
Optional<ClassTy> xtnds,
ImmutableList<ClassTy> impls,
ImmutableList<Tree> members,
- TurbineTyKind tykind) {
+ TurbineTyKind tykind,
+ String javadoc) {
super(position);
this.mods = ImmutableSet.copyOf(mods);
this.annos = annos;
@@ -872,6 +895,7 @@ public abstract class Tree {
this.impls = impls;
this.members = members;
this.tykind = tykind;
+ this.javadoc = javadoc;
}
@Override
@@ -915,6 +939,13 @@ public abstract class Tree {
public TurbineTyKind tykind() {
return tykind;
}
+ /**
+ * A javadoc comment, excluding the opening and closing delimiters but including all interior
+ * characters and whitespace.
+ */
+ public String javadoc() {
+ return javadoc;
+ }
}
/** A JLS 4.4. type variable declaration. */
diff --git a/java/com/google/turbine/type/AnnoInfo.java b/java/com/google/turbine/type/AnnoInfo.java
index 9c907aa..ff902b3 100644
--- a/java/com/google/turbine/type/AnnoInfo.java
+++ b/java/com/google/turbine/type/AnnoInfo.java
@@ -16,6 +16,9 @@
package com.google.turbine.type;
+import static com.google.common.collect.Iterables.getOnlyElement;
+import static java.util.Objects.requireNonNull;
+
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.turbine.binder.sym.ClassSymbol;
@@ -24,6 +27,7 @@ import com.google.turbine.model.Const;
import com.google.turbine.tree.Tree;
import com.google.turbine.tree.Tree.Anno;
import com.google.turbine.tree.Tree.Expression;
+import java.util.Map;
import java.util.Objects;
/** An annotation use. */
@@ -38,7 +42,7 @@ public class AnnoInfo {
this.source = source;
this.sym = sym;
this.tree = tree;
- this.values = values;
+ this.values = requireNonNull(values);
}
/** The annotation's source, for diagnostics. */
@@ -66,6 +70,10 @@ public class AnnoInfo {
return sym;
}
+ public Tree.Anno tree() {
+ return tree;
+ }
+
public AnnoInfo withValues(ImmutableMap<String, Const> values) {
return new AnnoInfo(source, sym, tree, values);
}
@@ -83,4 +91,27 @@ public class AnnoInfo {
AnnoInfo that = (AnnoInfo) obj;
return sym.equals(that.sym) && values.equals(that.values);
}
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append('@').append(sym.binaryName().replace('/', '.').replace('$', '.'));
+ boolean first = true;
+ if (values != null && !values.isEmpty()) {
+ sb.append('(');
+ if (values.size() == 1 && values.containsKey("value")) {
+ sb.append(getOnlyElement(values.values()));
+ } else {
+ for (Map.Entry<String, Const> e : values.entrySet()) {
+ if (!first) {
+ sb.append(", ");
+ }
+ sb.append(e.getKey()).append('=').append(e.getValue());
+ first = false;
+ }
+ }
+ sb.append(')');
+ }
+ return sb.toString();
+ }
}
diff --git a/java/com/google/turbine/type/Type.java b/java/com/google/turbine/type/Type.java
index 8950ec0..daba2ae 100644
--- a/java/com/google/turbine/type/Type.java
+++ b/java/com/google/turbine/type/Type.java
@@ -16,14 +16,23 @@
package com.google.turbine.type;
+import static com.google.common.collect.Iterables.getLast;
+import static java.util.Objects.requireNonNull;
+
import com.google.auto.value.AutoValue;
+import com.google.auto.value.extension.memoized.Memoized;
import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.turbine.binder.sym.ClassSymbol;
import com.google.turbine.binder.sym.TyVarSymbol;
import com.google.turbine.model.TurbineConstantTypeKind;
+import com.google.turbine.tree.Tree;
+import java.util.ArrayList;
import java.util.Arrays;
+import java.util.List;
+import org.checkerframework.checker.nullness.qual.Nullable;
/** JLS 4 types. */
public interface Type {
@@ -48,8 +57,11 @@ public interface Type {
WILD_TY,
/** An intersection type. */
INTERSECTION_TY,
+ /** A method type. */
+ METHOD_TY,
- ERROR_TY
+ ERROR_TY,
+ NONE_TY,
}
/** The type kind. */
@@ -62,9 +74,34 @@ public interface Type {
public TyKind tyKind() {
return TyKind.VOID_TY;
}
+
+ @Override
+ public final String toString() {
+ return "void";
+ }
+ };
+
+ /** The void type. */
+ Type NONE =
+ new Type() {
+ @Override
+ public TyKind tyKind() {
+ return TyKind.NONE_TY;
+ }
+
+ @Override
+ public final String toString() {
+ return "none";
+ }
};
- /** A class type. */
+ /**
+ * A class type.
+ *
+ * <p>Qualified types (e.g. {@code OuterClass<Foo>.InnerClass<Bar>}) are repesented as a list
+ * {@link SimpleClassTy}s (enclosing types first), each of which contains a {@link ClassSymbol}
+ * and an optional list of type arguments.
+ */
@AutoValue
abstract class ClassTy implements Type {
@@ -77,19 +114,17 @@ public interface Type {
/** The {@link ClassTy} for {@code java.lang.String}. */
public static final ClassTy STRING = asNonParametricClassTy(ClassSymbol.STRING);
+ public static final ClassTy CLONEABLE = asNonParametricClassTy(ClassSymbol.CLONEABLE);
+ public static final ClassTy SERIALIZABLE = asNonParametricClassTy(ClassSymbol.SERIALIZABLE);
+
/** Returns a {@link ClassTy} with no type arguments for the given {@link ClassSymbol}. */
public static ClassTy asNonParametricClassTy(ClassSymbol i) {
- return create(Arrays.asList(SimpleClassTy.create(i, ImmutableList.of(), ImmutableList.of())));
+ return ClassTy.create(
+ Arrays.asList(SimpleClassTy.create(i, ImmutableList.of(), ImmutableList.of())));
}
public abstract ImmutableList<SimpleClassTy> classes();
- /**
- * A class type. Qualified types are repesented as a list tuples, each of which contains a
- * {@link ClassSymbol} and an optional list of type arguments.
- *
- * @param classes components of a qualified class type, possibly with type arguments.
- */
public static ClassTy create(Iterable<SimpleClassTy> classes) {
return new AutoValue_Type_ClassTy(ImmutableList.copyOf(classes));
}
@@ -101,7 +136,7 @@ public interface Type {
/** The class symbol. */
public ClassSymbol sym() {
- return Iterables.getLast(classes()).sym();
+ return getLast(classes()).sym();
}
@Override
@@ -109,11 +144,15 @@ public interface Type {
StringBuilder sb = new StringBuilder();
boolean first = true;
for (SimpleClassTy c : classes()) {
+ for (AnnoInfo anno : c.annos()) {
+ sb.append(anno);
+ sb.append(' ');
+ }
if (!first) {
sb.append('.');
sb.append(c.sym().binaryName().substring(c.sym().binaryName().lastIndexOf('$') + 1));
} else {
- sb.append(c.sym().binaryName());
+ sb.append(c.sym().binaryName().replace('/', '.').replace('$', '.'));
}
if (!c.targs().isEmpty()) {
sb.append('<');
@@ -142,6 +181,46 @@ public interface Type {
/** The type annotations. */
public abstract ImmutableList<AnnoInfo> annos();
+
+ @Memoized
+ @Override
+ public abstract int hashCode();
+ }
+
+ @Memoized
+ @Override
+ public int hashCode() {
+ return Iterables.getLast(classes()).hashCode();
+ }
+
+ @Override
+ public final boolean equals(Object obj) {
+ if (!(obj instanceof ClassTy)) {
+ return false;
+ }
+ ClassTy that = (ClassTy) obj;
+ int i = this.classes().size() - 1;
+ int j = that.classes().size() - 1;
+ for (; i >= 0 && j >= 0; i--, j--) {
+ if (!this.classes().get(i).equals(that.classes().get(j))) {
+ return false;
+ }
+ }
+ // don't rely on canonical form for simple class names
+ if (hasTargs(this.classes(), i) || hasTargs(that.classes(), j)) {
+ return false;
+ }
+ return true;
+ }
+
+ private static boolean hasTargs(ImmutableList<SimpleClassTy> classes, int idx) {
+ for (; idx >= 0; idx--) {
+ SimpleClassTy simple = classes.get(idx);
+ if (!simple.targs().isEmpty() || !simple.annos().isEmpty()) {
+ return true;
+ }
+ }
+ return false;
}
}
@@ -163,6 +242,22 @@ public interface Type {
/** The type annotations. */
public abstract ImmutableList<AnnoInfo> annos();
+
+ @Override
+ public final String toString() {
+ StringBuilder sb = new StringBuilder();
+ for (AnnoInfo anno : annos()) {
+ sb.append(anno);
+ sb.append(' ');
+ }
+ sb.append(elementType());
+ sb.append("[]");
+ return sb.toString();
+ }
+
+ @Memoized
+ @Override
+ public abstract int hashCode();
}
/** A type variable. */
@@ -183,11 +278,21 @@ public interface Type {
@Override
public final String toString() {
- return sym().owner() + "#" + sym().name();
+ StringBuilder sb = new StringBuilder();
+ for (AnnoInfo anno : annos()) {
+ sb.append(anno);
+ sb.append(' ');
+ }
+ sb.append(sym().name());
+ return sb.toString();
}
/** The type annotations. */
public abstract ImmutableList<AnnoInfo> annos();
+
+ @Memoized
+ @Override
+ public abstract int hashCode();
}
/** A primitive type. */
@@ -208,6 +313,21 @@ public interface Type {
/** The type annotations. */
public abstract ImmutableList<AnnoInfo> annos();
+
+ @Override
+ public final String toString() {
+ StringBuilder sb = new StringBuilder();
+ for (AnnoInfo anno : annos()) {
+ sb.append(anno);
+ sb.append(' ');
+ }
+ sb.append(primkind());
+ return sb.toString();
+ }
+
+ @Memoized
+ @Override
+ public abstract int hashCode();
}
/** A wildcard type, valid only inside (possibly nested) type arguments. */
@@ -248,6 +368,22 @@ public interface Type {
public BoundKind boundKind() {
return BoundKind.UPPER;
}
+
+ @Override
+ public final String toString() {
+ StringBuilder sb = new StringBuilder();
+ for (AnnoInfo anno : annotations()) {
+ sb.append(anno);
+ sb.append(' ');
+ }
+ sb.append("? extends ");
+ sb.append(bound());
+ return sb.toString();
+ }
+
+ @Memoized
+ @Override
+ public abstract int hashCode();
}
/** An lower-bounded wildcard type. */
@@ -266,6 +402,22 @@ public interface Type {
public BoundKind boundKind() {
return BoundKind.LOWER;
}
+
+ @Override
+ public final String toString() {
+ StringBuilder sb = new StringBuilder();
+ for (AnnoInfo anno : annotations()) {
+ sb.append(anno);
+ sb.append(' ');
+ }
+ sb.append("? super ");
+ sb.append(bound());
+ return sb.toString();
+ }
+
+ @Memoized
+ @Override
+ public abstract int hashCode();
}
/** An unbounded wildcard type. */
@@ -285,6 +437,21 @@ public interface Type {
public Type bound() {
throw new IllegalStateException();
}
+
+ @Override
+ public final String toString() {
+ StringBuilder sb = new StringBuilder();
+ for (AnnoInfo anno : annotations()) {
+ sb.append(anno);
+ sb.append(' ');
+ }
+ sb.append('?');
+ return sb.toString();
+ }
+
+ @Memoized
+ @Override
+ public abstract int hashCode();
}
/** An intersection type. */
@@ -301,18 +468,121 @@ public interface Type {
public TyKind tyKind() {
return TyKind.INTERSECTION_TY;
}
+
+ @Memoized
+ @Override
+ public abstract int hashCode();
+
+ @Override
+ public final String toString() {
+ return Joiner.on('&').join(bounds());
+ }
}
- /** An error type. */
+ /** A method type. */
@AutoValue
- abstract class ErrorTy implements Type {
- public static ErrorTy create() {
- return new AutoValue_Type_ErrorTy();
+ abstract class MethodTy implements Type {
+
+ public abstract ImmutableSet<TyVarSymbol> tyParams();
+
+ public abstract Type returnType();
+
+ /** The type of the receiver parameter (see JLS 8.4.1). */
+ @Nullable
+ public abstract Type receiverType();
+
+ public abstract ImmutableList<Type> parameters();
+
+ public abstract ImmutableList<Type> thrown();
+
+ public static MethodTy create(
+ ImmutableSet<TyVarSymbol> tyParams,
+ Type returnType,
+ Type receiverType,
+ ImmutableList<Type> parameters,
+ ImmutableList<Type> thrown) {
+ return new AutoValue_Type_MethodTy(tyParams, returnType, receiverType, parameters, thrown);
+ }
+
+ @Override
+ public TyKind tyKind() {
+ return TyKind.METHOD_TY;
+ }
+
+ @Override
+ public final String toString() {
+ StringBuilder sb = new StringBuilder();
+ if (!tyParams().isEmpty()) {
+ sb.append('<');
+ Joiner.on(',').appendTo(sb, tyParams());
+ sb.append('>');
+ }
+ sb.append('(');
+ Joiner.on(',').appendTo(sb, parameters());
+ sb.append(')');
+ sb.append(returnType());
+ return sb.toString();
+ }
+
+ @Memoized
+ @Override
+ public abstract int hashCode();
+ }
+
+ /** An error type. */
+ final class ErrorTy implements Type {
+
+ private final String name;
+
+ private ErrorTy(String name) {
+ this.name = requireNonNull(name);
+ }
+
+ /**
+ * Best-effort syntactic context for use in diagnostics or by annotation processors. This may be
+ * a simple or qualified name; it is not a canonical qualified name.
+ */
+ public String name() {
+ return name;
+ }
+
+ public static ErrorTy create(Iterable<Tree.Ident> names) {
+ List<String> bits = new ArrayList<>();
+ for (Tree.Ident ident : names) {
+ bits.add(ident.value());
+ }
+ return create(Joiner.on('.').join(bits));
+ }
+
+ public static ErrorTy create(String name) {
+ return new ErrorTy(name);
}
@Override
public TyKind tyKind() {
return TyKind.ERROR_TY;
}
+
+ @Override
+ public final String toString() {
+ return name();
+ }
+
+ @Override
+ public final int hashCode() {
+ return System.identityHashCode(this);
+ }
+
+ @Override
+ public final boolean equals(Object other) {
+ // The name associated with an error type is context for use in diagnostics or by annotations
+ // processors. Two error types with the same name don't necessarily represent the same type.
+
+ // TODO(cushon): should error types compare equal to themselves if they correspond to the same
+ // source location? Investigate storing the source position for this type, or replacing with
+ // `this == other` (and removing interning in ModelFactory).
+
+ return false;
+ }
}
}
diff --git a/java/com/google/turbine/types/Canonicalize.java b/java/com/google/turbine/types/Canonicalize.java
index ab73618..22df069 100644
--- a/java/com/google/turbine/types/Canonicalize.java
+++ b/java/com/google/turbine/types/Canonicalize.java
@@ -100,6 +100,7 @@ public class Canonicalize {
case PRIM_TY:
case VOID_TY:
case TY_VAR:
+ case ERROR_TY:
return type;
case WILD_TY:
return canonicalizeWildTy(base, (WildTy) type);
@@ -118,6 +119,9 @@ public class Canonicalize {
}
private ClassTy canon(ClassSymbol base, ClassTy ty) {
+ if (ty.sym().equals(ClassSymbol.ERROR)) {
+ return ty;
+ }
if (isRaw(ty)) {
return Erasure.eraseClassTy(ty);
}
@@ -277,7 +281,7 @@ public class Canonicalize {
}
/** Instantiates a type argument using the given mapping. */
- private Type instantiate(Map<TyVarSymbol, Type> mapping, Type type) {
+ private static Type instantiate(Map<TyVarSymbol, Type> mapping, Type type) {
if (type == null) {
return null;
}
@@ -286,6 +290,7 @@ public class Canonicalize {
return instantiateWildTy(mapping, (WildTy) type);
case PRIM_TY:
case VOID_TY:
+ case ERROR_TY:
return type;
case CLASS_TY:
return instantiateClassTy(mapping, (ClassTy) type);
@@ -304,7 +309,7 @@ public class Canonicalize {
}
}
- private Type instantiateWildTy(Map<TyVarSymbol, Type> mapping, WildTy type) {
+ private static Type instantiateWildTy(Map<TyVarSymbol, Type> mapping, WildTy type) {
switch (type.boundKind()) {
case NONE:
return type;
@@ -314,12 +319,11 @@ public class Canonicalize {
case LOWER:
return Type.WildLowerBoundedTy.create(
instantiate(mapping, type.bound()), type.annotations());
- default:
- throw new AssertionError(type.boundKind());
}
+ throw new AssertionError(type.boundKind());
}
- private Type instantiateClassTy(Map<TyVarSymbol, Type> mapping, ClassTy type) {
+ private static Type instantiateClassTy(Map<TyVarSymbol, Type> mapping, ClassTy type) {
ImmutableList.Builder<SimpleClassTy> simples = ImmutableList.builder();
for (SimpleClassTy simple : type.classes()) {
ImmutableList.Builder<Type> args = ImmutableList.builder();
@@ -336,7 +340,7 @@ public class Canonicalize {
* reference, or else {@code null}.
*/
@Nullable
- private TyVarSymbol tyVarSym(Type type) {
+ private static TyVarSymbol tyVarSym(Type type) {
if (type.tyKind() == TyKind.TY_VAR) {
return ((TyVar) type).sym();
}
diff --git a/java/com/google/turbine/types/Erasure.java b/java/com/google/turbine/types/Erasure.java
index e2c7d8f..9042897 100644
--- a/java/com/google/turbine/types/Erasure.java
+++ b/java/com/google/turbine/types/Erasure.java
@@ -18,6 +18,7 @@ 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;
@@ -26,15 +27,14 @@ import com.google.turbine.type.Type.ArrayTy;
import com.google.turbine.type.Type.ClassTy;
import com.google.turbine.type.Type.ClassTy.SimpleClassTy;
import com.google.turbine.type.Type.IntersectionTy;
+import com.google.turbine.type.Type.MethodTy;
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) {
switch (ty.tyKind()) {
- case PRIM_TY:
- case VOID_TY:
- return ty;
case CLASS_TY:
return eraseClassTy((Type.ClassTy) ty);
case ARRAY_TY:
@@ -43,20 +43,37 @@ public class Erasure {
return eraseTyVar((TyVar) ty, tenv);
case INTERSECTION_TY:
return eraseIntersectionTy((Type.IntersectionTy) ty, tenv);
- default:
- throw new AssertionError(ty.tyKind());
+ case WILD_TY:
+ return eraseWildTy((Type.WildTy) ty, tenv);
+ case METHOD_TY:
+ return erasureMethodTy((Type.MethodTy) ty, tenv);
+ case PRIM_TY:
+ case VOID_TY:
+ case ERROR_TY:
+ case NONE_TY:
+ return ty;
+ }
+ throw new AssertionError(ty.tyKind());
+ }
+
+ private static ImmutableList<Type> erase(
+ ImmutableList<Type> types, Function<TyVarSymbol, TyVarInfo> tenv) {
+ ImmutableList.Builder<Type> result = ImmutableList.builder();
+ for (Type type : types) {
+ result.add(erase(type, tenv));
}
+ return result.build();
}
private static Type eraseIntersectionTy(
IntersectionTy ty, Function<TyVarSymbol, TyVarInfo> tenv) {
- return erase(ty.bounds().get(0), tenv);
+ 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());
- return erase(info.bound(), tenv);
+ return erase(info.upperBound(), tenv);
}
private static Type.ArrayTy eraseArrayTy(
@@ -75,4 +92,24 @@ public class Erasure {
}
return ClassTy.create(classes.build());
}
+
+ private static Type eraseWildTy(WildTy ty, Function<TyVarSymbol, TyVarInfo> tenv) {
+ switch (ty.boundKind()) {
+ case NONE:
+ case LOWER:
+ return ClassTy.OBJECT;
+ case UPPER:
+ return erase(ty.bound(), tenv);
+ }
+ throw new AssertionError(ty.boundKind());
+ }
+
+ private static Type erasureMethodTy(MethodTy ty, Function<TyVarSymbol, TyVarInfo> tenv) {
+ return MethodTy.create(
+ /* tyParams= */ ImmutableSet.of(),
+ erase(ty.returnType(), tenv),
+ ty.receiverType() != null ? erase(ty.receiverType(), tenv) : null,
+ erase(ty.parameters(), tenv),
+ erase(ty.thrown(), tenv));
+ }
}