Skip to content

Commit

Permalink
Use a two-element enum for annotation visibility
Browse files Browse the repository at this point in the history
PiperOrigin-RevId: 576610828
  • Loading branch information
cushon authored and Javac Team committed Oct 26, 2023
1 parent 05fee69 commit a6d5eec
Show file tree
Hide file tree
Showing 10 changed files with 216 additions and 44 deletions.
16 changes: 12 additions & 4 deletions java/com/google/turbine/bytecode/ClassFile.java
Original file line number Diff line number Diff line change
Expand Up @@ -381,14 +381,22 @@ public int access() {
/** The contents of a JVMS §4.7.16 annotation structure. */
public static class AnnotationInfo {

/** Whether the annotation is visible at runtime. */
public enum RuntimeVisibility {
VISIBLE,
INVISIBLE
}

private final String typeName;
private final boolean runtimeVisible;
private final RuntimeVisibility runtimeVisibility;
private final Map<String, ElementValue> elementValuePairs;

public AnnotationInfo(
String typeName, boolean runtimeVisible, Map<String, ElementValue> elementValuePairs) {
String typeName,
RuntimeVisibility runtimeVisibility,
Map<String, ElementValue> elementValuePairs) {
this.typeName = typeName;
this.runtimeVisible = runtimeVisible;
this.runtimeVisibility = runtimeVisibility;
this.elementValuePairs = elementValuePairs;
}

Expand All @@ -399,7 +407,7 @@ public String typeName() {

/** Returns true if the annotation is visible at runtime. */
public boolean isRuntimeVisible() {
return runtimeVisible;
return runtimeVisibility == RuntimeVisibility.VISIBLE;
}

/** The element-value pairs of the annotation. */
Expand Down
56 changes: 34 additions & 22 deletions java/com/google/turbine/bytecode/ClassReader.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import com.google.turbine.bytecode.ClassFile.AnnotationInfo.ElementValue.ConstTurbineClassValue;
import com.google.turbine.bytecode.ClassFile.AnnotationInfo.ElementValue.ConstValue;
import com.google.turbine.bytecode.ClassFile.AnnotationInfo.ElementValue.EnumConstValue;
import com.google.turbine.bytecode.ClassFile.AnnotationInfo.RuntimeVisibility;
import com.google.turbine.bytecode.ClassFile.MethodInfo.ParameterInfo;
import com.google.turbine.bytecode.ClassFile.ModuleInfo;
import com.google.turbine.bytecode.ClassFile.ModuleInfo.ExportInfo;
Expand Down Expand Up @@ -113,14 +114,16 @@ private ClassFile read() {
String name = constantPool.utf8(attributeNameIndex);
switch (name) {
case "RuntimeInvisibleAnnotations":
readAnnotations(annotations, constantPool, RuntimeVisibility.INVISIBLE);
break;
case "RuntimeVisibleAnnotations":
readAnnotations(annotations, constantPool);
readAnnotations(annotations, constantPool, RuntimeVisibility.VISIBLE);
break;
case "Signature":
signature = readSignature(constantPool);
break;
case "InnerClasses":
innerclasses = readInnerClasses(constantPool, thisClass);
innerclasses = readInnerClasses(constantPool);
break;
case "Module":
module = readModule(constantPool);
Expand Down Expand Up @@ -163,8 +166,7 @@ private String readSignature(ConstantPoolReader constantPool) {
}

/** Reads JVMS 4.7.6 InnerClasses attributes. */
private List<ClassFile.InnerClass> readInnerClasses(
ConstantPoolReader constantPool, String thisClass) {
private List<ClassFile.InnerClass> readInnerClasses(ConstantPoolReader constantPool) {
int unusedLength = reader.u4();
int numberOfClasses = reader.u2();
List<ClassFile.InnerClass> innerclasses = new ArrayList<>();
Expand All @@ -177,9 +179,7 @@ private List<ClassFile.InnerClass> readInnerClasses(
int innerNameIndex = reader.u2();
String innerName = innerNameIndex != 0 ? constantPool.utf8(innerNameIndex) : null;
int innerClassAccessFlags = reader.u2();
if (innerName != null
&& outerClass != null
&& (thisClass.equals(innerClass) || thisClass.equals(outerClass))) {
if (innerName != null && outerClass != null) {
innerclasses.add(
new ClassFile.InnerClass(innerClass, outerClass, innerName, innerClassAccessFlags));
}
Expand All @@ -195,17 +195,20 @@ private List<ClassFile.InnerClass> readInnerClasses(
*/
private void readAnnotations(
ImmutableList.Builder<ClassFile.AnnotationInfo> annotations,
ConstantPoolReader constantPool) {
ConstantPoolReader constantPool,
RuntimeVisibility runtimeVisibility) {
int unusedLength = reader.u4();
int numAnnotations = reader.u2();
for (int n = 0; n < numAnnotations; n++) {
annotations.add(readAnnotation(constantPool));
annotations.add(readAnnotation(constantPool, runtimeVisibility));
}
}

/** Processes a JVMS 4.7.18 RuntimeVisibleParameterAnnotations attribute */
public void readParameterAnnotations(
List<ImmutableList.Builder<AnnotationInfo>> annotations, ConstantPoolReader constantPool) {
List<ImmutableList.Builder<AnnotationInfo>> annotations,
ConstantPoolReader constantPool,
RuntimeVisibility runtimeVisibility) {
int unusedLength = reader.u4();
int numParameters = reader.u1();
while (annotations.size() < numParameters) {
Expand All @@ -214,7 +217,7 @@ public void readParameterAnnotations(
for (int i = 0; i < numParameters; i++) {
int numAnnotations = reader.u2();
for (int n = 0; n < numAnnotations; n++) {
annotations.get(i).add(readAnnotation(constantPool));
annotations.get(i).add(readAnnotation(constantPool, runtimeVisibility));
}
}
}
Expand Down Expand Up @@ -319,7 +322,8 @@ private ModuleInfo readModule(ConstantPoolReader constantPool) {
* Extracts an {@link @Retention} or {@link ElementType} {@link ClassFile.AnnotationInfo}, or else
* skips over the annotation.
*/
private ClassFile.AnnotationInfo readAnnotation(ConstantPoolReader constantPool) {
private ClassFile.AnnotationInfo readAnnotation(
ConstantPoolReader constantPool, RuntimeVisibility runtimeVisibility) {
int typeIndex = reader.u2();
String annotationType = constantPool.utf8(typeIndex);
int numElementValuePairs = reader.u2();
Expand All @@ -330,12 +334,7 @@ private ClassFile.AnnotationInfo readAnnotation(ConstantPoolReader constantPool)
ElementValue value = readElementValue(constantPool);
values.put(key, value);
}
return new ClassFile.AnnotationInfo(
annotationType,
// The runtimeVisible bit in AnnotationInfo is only used during lowering; earlier passes
// read the meta-annotations.
/* runtimeVisible= */ false,
values.buildOrThrow());
return new ClassFile.AnnotationInfo(annotationType, runtimeVisibility, values.buildOrThrow());
}

private ElementValue readElementValue(ConstantPoolReader constantPool) {
Expand Down Expand Up @@ -371,7 +370,12 @@ private ElementValue readElementValue(ConstantPoolReader constantPool) {
return new ConstTurbineClassValue(className);
}
case '@':
return new ConstTurbineAnnotationValue(readAnnotation(constantPool));
// The runtime visibility stored in the AnnotationInfo is never used for annotations that
// appear in element-values of other annotations. For top-level annotations, it determines
// the attribute the annotation appears in (e.g. Runtime{Invisible,Visible}Annotations).
// See also JVMS 4.7.16.1.
return new ConstTurbineAnnotationValue(
readAnnotation(constantPool, RuntimeVisibility.INVISIBLE));
case '[':
{
int numValues = reader.u2();
Expand Down Expand Up @@ -427,12 +431,18 @@ private List<ClassFile.MethodInfo> readMethods(ConstantPoolReader constantPool)
defaultValue = readElementValue(constantPool);
break;
case "RuntimeInvisibleAnnotations":
readAnnotations(annotations, constantPool, RuntimeVisibility.INVISIBLE);
break;
case "RuntimeVisibleAnnotations":
readAnnotations(annotations, constantPool);
readAnnotations(annotations, constantPool, RuntimeVisibility.VISIBLE);
break;
case "RuntimeInvisibleParameterAnnotations":
readParameterAnnotations(
parameterAnnotationsBuilder, constantPool, RuntimeVisibility.INVISIBLE);
break;
case "RuntimeVisibleParameterAnnotations":
readParameterAnnotations(parameterAnnotationsBuilder, constantPool);
readParameterAnnotations(
parameterAnnotationsBuilder, constantPool, RuntimeVisibility.VISIBLE);
break;
case "MethodParameters":
readMethodParameters(parameters, constantPool);
Expand Down Expand Up @@ -500,8 +510,10 @@ private List<ClassFile.FieldInfo> readFields(ConstantPoolReader constantPool) {
value = constantPool.constant(reader.u2());
break;
case "RuntimeInvisibleAnnotations":
readAnnotations(annotations, constantPool, RuntimeVisibility.INVISIBLE);
break;
case "RuntimeVisibleAnnotations":
readAnnotations(annotations, constantPool);
readAnnotations(annotations, constantPool, RuntimeVisibility.VISIBLE);
break;
case "Signature":
signature = readSignature(constantPool);
Expand Down
27 changes: 14 additions & 13 deletions java/com/google/turbine/lower/Lower.java
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
import com.google.turbine.bytecode.ClassFile;
import com.google.turbine.bytecode.ClassFile.AnnotationInfo;
import com.google.turbine.bytecode.ClassFile.AnnotationInfo.ElementValue;
import com.google.turbine.bytecode.ClassFile.AnnotationInfo.RuntimeVisibility;
import com.google.turbine.bytecode.ClassFile.MethodInfo.ParameterInfo;
import com.google.turbine.bytecode.ClassFile.TypeAnnotationInfo;
import com.google.turbine.bytecode.ClassFile.TypeAnnotationInfo.Target;
Expand Down Expand Up @@ -433,13 +434,13 @@ private ImmutableList<ImmutableList<AnnotationInfo>> parameterAnnotations(Method
}
ImmutableList.Builder<AnnotationInfo> parameterAnnotations = ImmutableList.builder();
for (AnnoInfo annotation : parameter.annotations()) {
Boolean visible = isVisible(annotation.sym());
if (visible == null) {
RuntimeVisibility visibility = getVisibility(annotation.sym());
if (visibility == null) {
continue;
}
String desc = sig.objectType(annotation.sym());
parameterAnnotations.add(
new AnnotationInfo(desc, visible, annotationValues(annotation.values())));
new AnnotationInfo(desc, visibility, annotationValues(annotation.values())));
}
annotations.add(parameterAnnotations.build());
}
Expand Down Expand Up @@ -638,26 +639,26 @@ private ImmutableList<AnnotationInfo> lowerAnnotations(ImmutableList<AnnoInfo> a
}

private @Nullable AnnotationInfo lowerAnnotation(AnnoInfo annotation) {
Boolean visible = isVisible(annotation.sym());
if (visible == null) {
RuntimeVisibility visibility = getVisibility(annotation.sym());
if (visibility == null) {
return null;
}
return new AnnotationInfo(
sig.objectType(annotation.sym()), visible, annotationValues(annotation.values()));
sig.objectType(annotation.sym()), visibility, annotationValues(annotation.values()));
}

/**
* Returns true if the annotation is visible at runtime, false if it is not visible at runtime,
* and {@code null} if it should not be retained in bytecode.
*/
private @Nullable Boolean isVisible(ClassSymbol sym) {
private @Nullable RuntimeVisibility getVisibility(ClassSymbol sym) {
RetentionPolicy retention =
requireNonNull(env.getNonNull(sym).annotationMetadata()).retention();
switch (retention) {
case CLASS:
return false;
return RuntimeVisibility.INVISIBLE;
case RUNTIME:
return true;
return RuntimeVisibility.VISIBLE;
case SOURCE:
return null;
}
Expand Down Expand Up @@ -698,14 +699,14 @@ private ElementValue annotationValue(Const value) {
case ANNOTATION:
{
TurbineAnnotationValue annotationValue = (TurbineAnnotationValue) value;
Boolean visible = isVisible(annotationValue.sym());
if (visible == null) {
visible = true;
RuntimeVisibility visibility = getVisibility(annotationValue.sym());
if (visibility == null) {
visibility = RuntimeVisibility.VISIBLE;
}
return new ElementValue.ConstTurbineAnnotationValue(
new AnnotationInfo(
sig.objectType(annotationValue.sym()),
visible,
visibility,
annotationValues(annotationValue.values())));
}
case PRIMITIVE:
Expand Down
6 changes: 3 additions & 3 deletions java/com/google/turbine/types/Canonicalize.java
Original file line number Diff line number Diff line change
Expand Up @@ -124,9 +124,6 @@ private ClassTy canon(ClassSymbol base, ClassTy ty) {
if (ty.sym().equals(ClassSymbol.ERROR)) {
return ty;
}
if (isRaw(ty)) {
return Erasure.eraseClassTy(ty);
}
// if the first name is a simple name resolved inside a nested class, add explicit qualifiers
// for the enclosing declarations
Iterator<ClassTy.SimpleClassTy> it = ty.classes().iterator();
Expand All @@ -140,6 +137,9 @@ private ClassTy canon(ClassSymbol base, ClassTy ty) {
while (it.hasNext()) {
canon = canonOne(canon, it.next());
}
if (isRaw(canon)) {
canon = Erasure.eraseClassTy(canon);
}
return canon;
}

Expand Down
10 changes: 8 additions & 2 deletions javatests/com/google/turbine/bytecode/ClassWriterTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -202,13 +202,19 @@ public void record() {
"Ljava/util/List;",
"Ljava/util/List<Ljava/lang/Integer;>;",
ImmutableList.of(
new ClassFile.AnnotationInfo("LA;", true, ImmutableMap.of())),
new ClassFile.AnnotationInfo(
"LA;",
ClassFile.AnnotationInfo.RuntimeVisibility.VISIBLE,
ImmutableMap.of())),
ImmutableList.of(
new ClassFile.TypeAnnotationInfo(
ClassFile.TypeAnnotationInfo.TargetType.FIELD,
ClassFile.TypeAnnotationInfo.EMPTY_TARGET,
ClassFile.TypeAnnotationInfo.TypePath.root(),
new ClassFile.AnnotationInfo("LA;", true, ImmutableMap.of())))),
new ClassFile.AnnotationInfo(
"LA;",
ClassFile.AnnotationInfo.RuntimeVisibility.VISIBLE,
ImmutableMap.of())))),
new ClassFile.RecordInfo.RecordComponentInfo(
"y", "I", null, ImmutableList.of(), ImmutableList.of()))),
/* transitiveJar= */ null);
Expand Down
15 changes: 15 additions & 0 deletions javatests/com/google/turbine/lower/IntegrationTestSupport.java
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,21 @@ public static Map<String, byte[]> canonicalize(Map<String, byte[]> in) {
return toByteCode(classes);
}

public static Map<String, byte[]> removeUnsupportedAttributes(Map<String, byte[]> in) {
List<ClassNode> classes = toClassNodes(in);
for (ClassNode c : classes) {
c.nestMembers = null;
c.nestHostClass = null;
// TODO(b/307939333): class reading for sealed classes
c.permittedSubclasses = null;
// TODO(b/307939164): class reading for record components
c.recordComponents = null;
// this is a synthetic access flag that ASM sets if recordComponents is present
c.access &= ~Opcodes.ACC_RECORD;
}
return toByteCode(classes);
}

private static boolean isLocal(ClassNode n) {
return n.outerMethod != null;
}
Expand Down
Loading

0 comments on commit a6d5eec

Please sign in to comment.