Support repeatable annotations
Also consolidate handling of annotation meta-information (target,
retention, and now repeatable).
MOE_MIGRATED_REVID=138010789
diff --git a/java/com/google/turbine/binder/CanonicalTypeBinder.java b/java/com/google/turbine/binder/CanonicalTypeBinder.java
index edfc02d..0e4e267 100644
--- a/java/com/google/turbine/binder/CanonicalTypeBinder.java
+++ b/java/com/google/turbine/binder/CanonicalTypeBinder.java
@@ -67,8 +67,7 @@
base.enclosingScope(),
base.scope(),
base.memberImports(),
- base.annotationRetention(),
- base.annotationTarget(),
+ base.annotationMetadata(),
base.annotations(),
base.source());
}
diff --git a/java/com/google/turbine/binder/ConstBinder.java b/java/com/google/turbine/binder/ConstBinder.java
index 959e62a..03c728e 100644
--- a/java/com/google/turbine/binder/ConstBinder.java
+++ b/java/com/google/turbine/binder/ConstBinder.java
@@ -19,6 +19,8 @@
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
+import com.google.turbine.binder.bound.AnnotationMetadata;
+import com.google.turbine.binder.bound.ClassValue;
import com.google.turbine.binder.bound.EnumConstantValue;
import com.google.turbine.binder.bound.SourceTypeBoundClass;
import com.google.turbine.binder.bound.TypeBoundClass;
@@ -43,6 +45,7 @@
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.TyKind;
import com.google.turbine.type.Type.TyVar;
import com.google.turbine.type.Type.WildLowerBoundedTy;
import com.google.turbine.type.Type.WildTy;
@@ -50,9 +53,7 @@
import com.google.turbine.type.Type.WildUpperBoundedTy;
import java.lang.annotation.ElementType;
import java.lang.annotation.RetentionPolicy;
-import java.util.EnumSet;
import java.util.Map;
-import javax.annotation.Nullable;
/** Binding pass to evaluate constant expressions. */
public class ConstBinder {
@@ -93,8 +94,7 @@
base.enclosingScope(),
base.scope(),
base.memberImports(),
- bindRetention(base.kind(), annos),
- bindTarget(base.kind(), annos),
+ bindAnnotationMetadata(base.kind(), annos),
annos,
base.source());
}
@@ -143,46 +143,42 @@
return new ParamInfo(bindType(base.type()), annos, base.synthetic());
}
- /** Returns the {@link RetentionPolicy} for an annotation declaration, or {@code null}. */
- @Nullable
- static RetentionPolicy bindRetention(TurbineTyKind kind, Iterable<AnnoInfo> annotations) {
+ static AnnotationMetadata bindAnnotationMetadata(
+ TurbineTyKind kind, Iterable<AnnoInfo> annotations) {
if (kind != TurbineTyKind.ANNOTATION) {
return null;
}
- for (AnnoInfo annotation : annotations) {
- if (annotation.sym().toString().equals("java/lang/annotation/Retention")) {
- Const value = annotation.values().get("value");
- if (value.kind() != Const.Kind.ENUM_CONSTANT) {
- break;
- }
- EnumConstantValue enumValue = (EnumConstantValue) value;
- if (!enumValue.sym().owner().toString().equals("java/lang/annotation/RetentionPolicy")) {
- break;
- }
- return RetentionPolicy.valueOf(enumValue.sym().name());
- }
- }
- return RetentionPolicy.CLASS;
- }
-
- /** Returns the target {@link ElementType}s for an annotation declaration, or {@code null}. */
- @Nullable
- static ImmutableSet<ElementType> bindTarget(TurbineTyKind kind, Iterable<AnnoInfo> annotations) {
- if (kind != TurbineTyKind.ANNOTATION) {
- return null;
- }
+ RetentionPolicy retention = null;
+ ImmutableSet<ElementType> target = null;
+ ClassSymbol repeatable = null;
for (AnnoInfo annotation : annotations) {
switch (annotation.sym().binaryName()) {
+ case "java/lang/annotation/Retention":
+ retention = bindRetention(annotation);
+ break;
case "java/lang/annotation/Target":
- return bindTarget(annotation);
+ target = bindTarget(annotation);
+ break;
+ case "java/lang/annotation/Repeatable":
+ repeatable = bindRepeatable(annotation);
+ break;
default:
break;
}
}
- EnumSet<ElementType> target = EnumSet.allOf(ElementType.class);
- target.remove(ElementType.TYPE_USE);
- target.remove(ElementType.TYPE_PARAMETER);
- return ImmutableSet.copyOf(target);
+ return new AnnotationMetadata(retention, target, repeatable);
+ }
+
+ private static RetentionPolicy bindRetention(AnnoInfo annotation) {
+ Const value = annotation.values().get("value");
+ if (value.kind() != Kind.ENUM_CONSTANT) {
+ return null;
+ }
+ EnumConstantValue enumValue = (EnumConstantValue) value;
+ if (!enumValue.sym().owner().toString().equals("java/lang/annotation/RetentionPolicy")) {
+ return null;
+ }
+ return RetentionPolicy.valueOf(enumValue.sym().name());
}
private static ImmutableSet<ElementType> bindTarget(AnnoInfo annotation) {
@@ -205,6 +201,18 @@
return result.build();
}
+ private static ClassSymbol bindRepeatable(AnnoInfo annotation) {
+ Const value = annotation.values().get("value");
+ if (value.kind() != Kind.CLASS_LITERAL) {
+ return null;
+ }
+ Type type = ((ClassValue) value).type();
+ if (type.tyKind() != TyKind.CLASS_TY) {
+ return null;
+ }
+ return ((ClassTy) type).sym();
+ }
+
private static void bindTargetElement(
ImmutableSet.Builder<ElementType> target, EnumConstantValue enumVal) {
if (enumVal.sym().owner().binaryName().equals("java/lang/annotation/ElementType")) {
diff --git a/java/com/google/turbine/binder/DisambiguateTypeAnnotations.java b/java/com/google/turbine/binder/DisambiguateTypeAnnotations.java
index 656d511..0f6c9b4 100644
--- a/java/com/google/turbine/binder/DisambiguateTypeAnnotations.java
+++ b/java/com/google/turbine/binder/DisambiguateTypeAnnotations.java
@@ -16,8 +16,14 @@
package com.google.turbine.binder;
+import static com.google.common.collect.Iterables.getOnlyElement;
+
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableList.Builder;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.LinkedHashMultimap;
+import com.google.common.collect.Multimap;
+import com.google.turbine.binder.bound.AnnotationValue;
import com.google.turbine.binder.bound.SourceTypeBoundClass;
import com.google.turbine.binder.bound.TypeBoundClass;
import com.google.turbine.binder.bound.TypeBoundClass.FieldInfo;
@@ -25,6 +31,7 @@
import com.google.turbine.binder.bound.TypeBoundClass.ParamInfo;
import com.google.turbine.binder.env.Env;
import com.google.turbine.binder.sym.ClassSymbol;
+import com.google.turbine.model.Const;
import com.google.turbine.type.AnnoInfo;
import com.google.turbine.type.Type;
import com.google.turbine.type.Type.ArrayTy;
@@ -33,6 +40,8 @@
import com.google.turbine.type.Type.PrimTy;
import com.google.turbine.type.Type.TyVar;
import java.lang.annotation.ElementType;
+import java.util.Collection;
+import java.util.Map;
import java.util.Set;
/**
@@ -73,9 +82,8 @@
base.enclosingScope(),
base.scope(),
base.memberImports(),
- base.annotationRetention(),
- base.annotationTarget(),
- base.annotations(),
+ base.annotationMetadata(),
+ groupRepeated(env, base.annotations()),
base.source());
}
@@ -133,9 +141,12 @@
Type type,
ImmutableList<AnnoInfo> annotations,
Builder<AnnoInfo> declarationAnnotations) {
+ // desugar @Repeatable annotations before disambiguating: annotation containers may target
+ // a subset of the types targeted by their element annotation
+ annotations = groupRepeated(env, annotations);
ImmutableList.Builder<AnnoInfo> typeAnnotations = ImmutableList.builder();
for (AnnoInfo anno : annotations) {
- Set<ElementType> target = env.get(anno.sym()).annotationTarget();
+ Set<ElementType> target = env.get(anno.sym()).annotationMetadata().target();
if (target.contains(ElementType.TYPE_USE)) {
typeAnnotations.add(anno);
}
@@ -218,7 +229,7 @@
Builder<AnnoInfo> removed) {
ImmutableList.Builder<AnnoInfo> result = ImmutableList.builder();
for (AnnoInfo anno : annos) {
- Set<ElementType> target = env.get(anno.sym()).annotationTarget();
+ Set<ElementType> target = env.get(anno.sym()).annotationMetadata().target();
if (target.contains(ElementType.TYPE_USE)) {
result.add(anno);
} else {
@@ -228,4 +239,44 @@
result.addAll(extra);
return result.build();
}
+
+ /**
+ * Group repeated annotations and wrap them in their container annotation.
+ *
+ * <p>For example, convert {@code @Foo @Foo} to {@code @Foos({@Foo, @Foo})}.
+ *
+ * <p>This method is used by {@link DisambiguateTypeAnnotations} for declaration annotations, and
+ * by {@link Lower} for type annotations. We could group type annotations here, but it would
+ * require another rewrite pass.
+ */
+ public static ImmutableList<AnnoInfo> groupRepeated(
+ Env<ClassSymbol, TypeBoundClass> env, ImmutableList<AnnoInfo> annotations) {
+ Multimap<ClassSymbol, AnnoInfo> repeated = LinkedHashMultimap.create();
+ for (AnnoInfo anno : annotations) {
+ repeated.put(anno.sym(), anno);
+ }
+ Builder<AnnoInfo> result = ImmutableList.builder();
+ for (Map.Entry<ClassSymbol, Collection<AnnoInfo>> entry : repeated.asMap().entrySet()) {
+ ClassSymbol symbol = entry.getKey();
+ Collection<AnnoInfo> infos = entry.getValue();
+ if (infos.size() > 1) {
+ Builder<Const> elements = ImmutableList.builder();
+ for (AnnoInfo element : infos) {
+ elements.add(new AnnotationValue(element.sym(), element.values()));
+ }
+ ClassSymbol container = env.get(symbol).annotationMetadata().repeatable();
+ if (container == null) {
+ throw new AssertionError(symbol);
+ }
+ result.add(
+ new AnnoInfo(
+ container,
+ null,
+ ImmutableMap.of("value", new Const.ArrayInitValue(elements.build()))));
+ } else {
+ result.add(getOnlyElement(infos));
+ }
+ }
+ return result.build();
+ }
}
diff --git a/java/com/google/turbine/binder/TypeBinder.java b/java/com/google/turbine/binder/TypeBinder.java
index 21920ef..a1feb37 100644
--- a/java/com/google/turbine/binder/TypeBinder.java
+++ b/java/com/google/turbine/binder/TypeBinder.java
@@ -19,7 +19,6 @@
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.HeaderBoundClass;
import com.google.turbine.binder.bound.SourceHeaderBoundClass;
import com.google.turbine.binder.bound.SourceTypeBoundClass;
@@ -229,8 +228,7 @@
enclosingScope,
scope,
base.memberImports(),
- /*retention*/ null,
- /*target*/ ImmutableSet.of(),
+ /* annotation metadata */ null,
annotations,
base.source());
}
diff --git a/java/com/google/turbine/binder/bound/AnnotationMetadata.java b/java/com/google/turbine/binder/bound/AnnotationMetadata.java
new file mode 100644
index 0000000..1155e51
--- /dev/null
+++ b/java/com/google/turbine/binder/bound/AnnotationMetadata.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2016 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.turbine.binder.bound;
+
+import static com.google.common.base.MoreObjects.firstNonNull;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.turbine.binder.sym.ClassSymbol;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.RetentionPolicy;
+import java.util.EnumSet;
+
+/**
+ * Annotation metadata, e.g. from {@link @java.lang.annotation.Target}, {@link
+ * java.lang.annotation.Retention}, and {@link java.lang.annotation.Repeatable}.
+ */
+public class AnnotationMetadata {
+
+ private static final ImmutableSet<ElementType> DEFAULT_TARGETS = getDefaultElements();
+
+ private static ImmutableSet<ElementType> getDefaultElements() {
+ EnumSet<ElementType> values = EnumSet.allOf(ElementType.class);
+ values.remove(ElementType.TYPE_PARAMETER);
+ values.remove(ElementType.TYPE_USE);
+ return ImmutableSet.copyOf(values);
+ }
+
+ private final RetentionPolicy retention;
+ private final ImmutableSet<ElementType> target;
+ private final ClassSymbol repeatable;
+
+ public AnnotationMetadata(
+ RetentionPolicy retention,
+ ImmutableSet<ElementType> annotationTarget,
+ ClassSymbol repeatable) {
+ this.retention = firstNonNull(retention, RetentionPolicy.CLASS);
+ this.target = firstNonNull(annotationTarget, DEFAULT_TARGETS);
+ this.repeatable = repeatable;
+ }
+
+ /** The retention policy specified by the {@code @Retention} meta-annotation. */
+ public RetentionPolicy retention() {
+ return retention;
+ }
+
+ /** Target element types specified by the {@code @Target} meta-annotation. */
+ public ImmutableSet<ElementType> target() {
+ return target;
+ }
+
+ /** The container annotation for {@code @Repeated} annotations. */
+ public ClassSymbol repeatable() {
+ return repeatable;
+ }
+}
diff --git a/java/com/google/turbine/binder/bound/SourceTypeBoundClass.java b/java/com/google/turbine/binder/bound/SourceTypeBoundClass.java
index 75008c4..fb2a3c5 100644
--- a/java/com/google/turbine/binder/bound/SourceTypeBoundClass.java
+++ b/java/com/google/turbine/binder/bound/SourceTypeBoundClass.java
@@ -18,7 +18,6 @@
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSet;
import com.google.turbine.binder.lookup.CompoundScope;
import com.google.turbine.binder.lookup.MemberImportIndex;
import com.google.turbine.binder.sym.ClassSymbol;
@@ -28,8 +27,6 @@
import com.google.turbine.type.AnnoInfo;
import com.google.turbine.type.Type;
import com.google.turbine.type.Type.ClassTy;
-import java.lang.annotation.ElementType;
-import java.lang.annotation.RetentionPolicy;
import javax.annotation.Nullable;
/** A HeaderBoundClass for classes compiled from source. */
@@ -52,8 +49,7 @@
private final CompoundScope enclosingScope;
private final CompoundScope scope;
private final MemberImportIndex memberImports;
- private final RetentionPolicy retention;
- private final ImmutableSet<ElementType> annotationTarget;
+ private final AnnotationMetadata annotationMetadata;
private final ImmutableList<AnnoInfo> annotations;
private final SourceFile source;
@@ -73,8 +69,7 @@
CompoundScope enclosingScope,
CompoundScope scope,
MemberImportIndex memberImports,
- RetentionPolicy retention,
- ImmutableSet<ElementType> annotationTarget,
+ AnnotationMetadata annotationMetadata,
ImmutableList<AnnoInfo> annotations,
SourceFile source) {
this.interfaceTypes = interfaceTypes;
@@ -92,8 +87,7 @@
this.enclosingScope = enclosingScope;
this.scope = scope;
this.memberImports = memberImports;
- this.retention = retention;
- this.annotationTarget = annotationTarget;
+ this.annotationMetadata = annotationMetadata;
this.annotations = annotations;
this.source = source;
}
@@ -152,13 +146,8 @@
}
@Override
- public RetentionPolicy annotationRetention() {
- return retention;
- }
-
- @Override
- public ImmutableSet<ElementType> annotationTarget() {
- return annotationTarget;
+ public AnnotationMetadata annotationMetadata() {
+ return annotationMetadata;
}
/** Declared fields. */
diff --git a/java/com/google/turbine/binder/bound/TypeBoundClass.java b/java/com/google/turbine/binder/bound/TypeBoundClass.java
index 6232b93..d942cbe 100644
--- a/java/com/google/turbine/binder/bound/TypeBoundClass.java
+++ b/java/com/google/turbine/binder/bound/TypeBoundClass.java
@@ -18,7 +18,6 @@
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSet;
import com.google.turbine.binder.sym.FieldSymbol;
import com.google.turbine.binder.sym.MethodSymbol;
import com.google.turbine.binder.sym.TyVarSymbol;
@@ -27,8 +26,6 @@
import com.google.turbine.tree.Tree.MethDecl;
import com.google.turbine.type.AnnoInfo;
import com.google.turbine.type.Type;
-import java.lang.annotation.ElementType;
-import java.lang.annotation.RetentionPolicy;
/** A bound node that augments {@link HeaderBoundClass} with type information. */
public interface TypeBoundClass extends HeaderBoundClass {
@@ -44,11 +41,11 @@
/** Declared methods. */
ImmutableList<MethodInfo> methods();
- /** Retention policy for annotation declarations, {@code null} for other declarations. */
- RetentionPolicy annotationRetention();
-
- /** Target element types for annotation declarations, {@code null} for other declarations. */
- ImmutableSet<ElementType> annotationTarget();
+ /**
+ * Annotation metadata, e.g. from {@link @java.lang.annotation.Target}, {@link
+ * java.lang.annotation.Retention}, and {@link java.lang.annotation.Repeatable}.
+ */
+ AnnotationMetadata annotationMetadata();
/** A type parameter declaration. */
class TyVarInfo {
diff --git a/java/com/google/turbine/binder/bytecode/BytecodeBoundClass.java b/java/com/google/turbine/binder/bytecode/BytecodeBoundClass.java
index f0eed37..16aabc5 100644
--- a/java/com/google/turbine/binder/bytecode/BytecodeBoundClass.java
+++ b/java/com/google/turbine/binder/bytecode/BytecodeBoundClass.java
@@ -24,6 +24,7 @@
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
+import com.google.turbine.binder.bound.AnnotationMetadata;
import com.google.turbine.binder.bound.BoundClass;
import com.google.turbine.binder.bound.HeaderBoundClass;
import com.google.turbine.binder.bound.TypeBoundClass;
@@ -36,6 +37,7 @@
import com.google.turbine.bytecode.ClassFile.AnnotationInfo;
import com.google.turbine.bytecode.ClassFile.AnnotationInfo.ElementValue;
import com.google.turbine.bytecode.ClassFile.AnnotationInfo.ElementValue.ArrayValue;
+import com.google.turbine.bytecode.ClassFile.AnnotationInfo.ElementValue.ConstClassValue;
import com.google.turbine.bytecode.ClassFile.AnnotationInfo.ElementValue.EnumConstValue;
import com.google.turbine.bytecode.ClassFile.AnnotationInfo.ElementValue.Kind;
import com.google.turbine.bytecode.ClassReader;
@@ -49,7 +51,6 @@
import com.google.turbine.type.Type.ClassTy;
import java.lang.annotation.ElementType;
import java.lang.annotation.RetentionPolicy;
-import java.util.EnumSet;
import java.util.Map;
import java.util.function.Function;
import javax.annotation.Nullable;
@@ -389,60 +390,48 @@
return methods.get();
}
- final Supplier<RetentionPolicy> retention =
+ final Supplier<AnnotationMetadata> annotationMetadata =
Suppliers.memoize(
- new Supplier<RetentionPolicy>() {
+ new Supplier<AnnotationMetadata>() {
@Override
- public RetentionPolicy get() {
+ public AnnotationMetadata get() {
if ((access() & TurbineFlag.ACC_ANNOTATION) != TurbineFlag.ACC_ANNOTATION) {
return null;
}
- for (ClassFile.AnnotationInfo annotation : classFile.get().annotations()) {
- if (!annotation.typeName().equals("Ljava/lang/annotation/Retention;")) {
- continue;
- }
- ElementValue val = annotation.elementValuePairs().get("value");
- if (val.kind() != ElementValue.Kind.ENUM) {
- continue;
- }
- ElementValue.EnumConstValue enumVal = (ElementValue.EnumConstValue) val;
- if (!enumVal.typeName().equals("Ljava/lang/annotation/RetentionPolicy;")) {
- continue;
- }
- return RetentionPolicy.valueOf(enumVal.constName());
- }
- return RetentionPolicy.CLASS;
- }
- });
-
- @Override
- public RetentionPolicy annotationRetention() {
- return retention.get();
- }
-
- final Supplier<ImmutableSet<ElementType>> target =
- Suppliers.memoize(
- new Supplier<ImmutableSet<ElementType>>() {
- @Override
- public ImmutableSet<ElementType> get() {
- if ((access() & TurbineFlag.ACC_ANNOTATION) != TurbineFlag.ACC_ANNOTATION) {
- return null;
- }
+ RetentionPolicy retention = null;
+ ImmutableSet<ElementType> target = null;
+ ClassSymbol repeatable = null;
for (ClassFile.AnnotationInfo annotation : classFile.get().annotations()) {
switch (annotation.typeName()) {
+ case "Ljava/lang/annotation/Retention;":
+ retention = bindRetention(annotation);
+ break;
case "Ljava/lang/annotation/Target;":
- return bindTarget(annotation);
+ target = bindTarget(annotation);
+ break;
+ case "Ljava/lang/annotation/Repeatable;":
+ repeatable = bindRepeatable(annotation);
+ break;
default:
break;
}
}
- EnumSet<ElementType> target = EnumSet.allOf(ElementType.class);
- target.remove(ElementType.TYPE_USE);
- target.remove(ElementType.TYPE_PARAMETER);
- return ImmutableSet.copyOf(target);
+ return new AnnotationMetadata(retention, target, repeatable);
}
});
+ private RetentionPolicy bindRetention(AnnotationInfo annotation) {
+ ElementValue val = annotation.elementValuePairs().get("value");
+ if (val.kind() != Kind.ENUM) {
+ return null;
+ }
+ EnumConstValue enumVal = (EnumConstValue) val;
+ if (!enumVal.typeName().equals("Ljava/lang/annotation/RetentionPolicy;")) {
+ return null;
+ }
+ return RetentionPolicy.valueOf(enumVal.constName());
+ }
+
private static ImmutableSet<ElementType> bindTarget(AnnotationInfo annotation) {
ImmutableSet.Builder<ElementType> result = ImmutableSet.builder();
ElementValue val = annotation.elementValuePairs().get("value");
@@ -470,9 +459,21 @@
}
}
+ private static ClassSymbol bindRepeatable(AnnotationInfo annotation) {
+ ElementValue val = annotation.elementValuePairs().get("value");
+ switch (val.kind()) {
+ case CLASS:
+ String className = ((ConstClassValue) val).className();
+ return new ClassSymbol(className.substring(1, className.length() - 1));
+ default:
+ break;
+ }
+ return null;
+ }
+
@Override
- public ImmutableSet<ElementType> annotationTarget() {
- return target.get();
+ public AnnotationMetadata annotationMetadata() {
+ return annotationMetadata.get();
}
/**
diff --git a/java/com/google/turbine/bytecode/ClassReader.java b/java/com/google/turbine/bytecode/ClassReader.java
index 5311fdc..bcf1c40 100644
--- a/java/com/google/turbine/bytecode/ClassReader.java
+++ b/java/com/google/turbine/bytecode/ClassReader.java
@@ -19,6 +19,7 @@
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.turbine.bytecode.ClassFile.AnnotationInfo.ElementValue;
+import com.google.turbine.bytecode.ClassFile.AnnotationInfo.ElementValue.ConstClassValue;
import com.google.turbine.bytecode.ClassFile.AnnotationInfo.ElementValue.EnumConstValue;
import com.google.turbine.model.Const;
import com.google.turbine.model.TurbineFlag;
@@ -172,6 +173,7 @@
switch (annotationType) {
case "Ljava/lang/annotation/Retention;":
case "Ljava/lang/annotation/Target;":
+ case "Ljava/lang/annotation/Repeatable;":
read = true;
break;
default:
@@ -193,8 +195,8 @@
}
/**
- * Extracts the value of an {@link @Retention} annotation, or else skips over the element value
- * pair.
+ * Extracts the value of an annotation declaration meta-annotation, or else skips over the element
+ * value pair.
*/
private ElementValue readElementValue(ConstantPoolReader constantPool, boolean value) {
int tag = reader.u1();
@@ -228,8 +230,9 @@
break;
}
case 'c':
- reader.u2(); // classInfoIndex
- break;
+ int classInfoIndex = reader.u2();
+ String className = constantPool.utf8(classInfoIndex);
+ return new ConstClassValue(className);
case '@':
readAnnotation(constantPool);
break;
diff --git a/java/com/google/turbine/lower/Lower.java b/java/com/google/turbine/lower/Lower.java
index 4da59ad..d7b22fd 100644
--- a/java/com/google/turbine/lower/Lower.java
+++ b/java/com/google/turbine/lower/Lower.java
@@ -16,6 +16,8 @@
package com.google.turbine.lower;
+import static com.google.turbine.binder.DisambiguateTypeAnnotations.groupRepeated;
+
import com.google.common.base.Function;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableList.Builder;
@@ -356,22 +358,17 @@
// anything that lexically encloses the class being lowered
// must be in the same compilation unit, so we have source
// information for it
- // TODO(cushon): remove this cast once we're reading type parameters from bytecode
TypeBoundClass owner = env.get((ClassSymbol) ownerSym);
- if (!(owner instanceof SourceTypeBoundClass)) {
- throw new AssertionError(sym);
- }
- return ((SourceTypeBoundClass) owner).typeParameterTypes().get(sym);
+ return owner.typeParameterTypes().get(sym);
}
}
private ImmutableList<AnnotationInfo> lowerAnnotations(ImmutableList<AnnoInfo> annotations) {
ImmutableList.Builder<AnnotationInfo> lowered = ImmutableList.builder();
- outer:
for (AnnoInfo annotation : annotations) {
AnnotationInfo anno = lowerAnnotation(annotation);
if (anno == null) {
- continue outer;
+ continue;
}
lowered.add(anno);
}
@@ -399,7 +396,7 @@
*/
@Nullable
private Boolean isVisible(ClassSymbol sym) {
- RetentionPolicy retention = env.get(sym).annotationRetention();
+ RetentionPolicy retention = env.get(sym).annotationMetadata().retention();
switch (retention) {
case CLASS:
return false;
@@ -540,7 +537,7 @@
TargetType boundTargetType) {
int typeParameterIndex = 0;
for (TyVarInfo p : typeParameters) {
- for (AnnoInfo anno : p.annotations()) {
+ for (AnnoInfo anno : groupRepeated(env, p.annotations())) {
AnnotationInfo info = lowerAnnotation(anno);
if (info == null) {
continue;
@@ -621,7 +618,7 @@
/** Lower a list of type annotations. */
private void lowerTypeAnnotations(ImmutableList<AnnoInfo> annos, TypePath path) {
- for (AnnoInfo anno : annos) {
+ for (AnnoInfo anno : groupRepeated(env, annos)) {
AnnotationInfo info = lowerAnnotation(anno);
if (info == null) {
continue;
diff --git a/java/com/google/turbine/type/AnnoInfo.java b/java/com/google/turbine/type/AnnoInfo.java
index f821a0f..4af2e66 100644
--- a/java/com/google/turbine/type/AnnoInfo.java
+++ b/java/com/google/turbine/type/AnnoInfo.java
@@ -16,6 +16,8 @@
package com.google.turbine.type;
+import static java.util.Objects.requireNonNull;
+
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.turbine.binder.sym.ClassSymbol;
@@ -30,7 +32,7 @@
public AnnoInfo(
ClassSymbol sym, ImmutableList<Expression> args, ImmutableMap<String, Const> values) {
- this.sym = sym;
+ this.sym = requireNonNull(sym);
this.args = args;
this.values = values;
}
diff --git a/javatests/com/google/turbine/lower/LowerIntegrationTest.java b/javatests/com/google/turbine/lower/LowerIntegrationTest.java
index b48928c..d2406d9 100644
--- a/javatests/com/google/turbine/lower/LowerIntegrationTest.java
+++ b/javatests/com/google/turbine/lower/LowerIntegrationTest.java
@@ -259,6 +259,7 @@
"c_array.test",
"type_anno_retention.test",
"member_import_clash.test",
+ "anno_repeated.test",
};
List<Object[]> tests =
ImmutableList.copyOf(testCases).stream().map(x -> new Object[] {x}).collect(toList());
diff --git a/javatests/com/google/turbine/lower/LowerTest.java b/javatests/com/google/turbine/lower/LowerTest.java
index 6864a7d..42f395d 100644
--- a/javatests/com/google/turbine/lower/LowerTest.java
+++ b/javatests/com/google/turbine/lower/LowerTest.java
@@ -181,7 +181,6 @@
null,
null,
null,
- null,
ImmutableList.of(),
null);
@@ -203,7 +202,6 @@
null,
null,
null,
- null,
ImmutableList.of(),
null);
diff --git a/javatests/com/google/turbine/lower/testdata/anno_repeated.test b/javatests/com/google/turbine/lower/testdata/anno_repeated.test
new file mode 100644
index 0000000..928739c
--- /dev/null
+++ b/javatests/com/google/turbine/lower/testdata/anno_repeated.test
@@ -0,0 +1,51 @@
+=== A.java ===
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Repeatable;
+import java.lang.annotation.Target;
+
+@Target({
+ ElementType.TYPE_USE,
+ ElementType.FIELD,
+ ElementType.TYPE_PARAMETER,
+ ElementType.PARAMETER,
+})
+@interface As {
+ A[] value() default {};
+}
+
+@Repeatable(As.class)
+@Target({
+ ElementType.TYPE_USE,
+ ElementType.FIELD,
+ ElementType.TYPE_PARAMETER,
+ ElementType.PARAMETER,
+})
+@interface A {}
+
+%%% B.java %%%
+import java.lang.annotation.Repeatable;
+
+@interface Bs {
+ B[] value() default {};
+}
+
+@Repeatable(Bs.class)
+@interface B {}
+
+=== C.java ===
+import java.lang.annotation.Repeatable;
+
+@interface Cs {
+ C[] value() default {};
+}
+
+@Repeatable(Cs.class)
+@interface C {}
+
+=== Test.java ===
+import java.util.Map;
+class Test {
+ @A @A @B @B int x;
+ Map<@A @A Integer, @A String> y;
+ <@A @A X> @A X f(@A @A int x) { return null; }
+}