aboutsummaryrefslogtreecommitdiff
path: root/java/com/google/turbine/processing
diff options
context:
space:
mode:
Diffstat (limited to 'java/com/google/turbine/processing')
-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
14 files changed, 5629 insertions, 0 deletions
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);
+ }
+}