blob: d1de0cc8d1798fb9b4cf70473c674e892e76eb1b [file] [log] [blame]
// This class is a complete ClassVisitor with many hidden classes that do
// the work of reading annotations from a class file and inserting them into
// an AScene.
package annotations.io.classfile;
/*>>>
import org.checkerframework.checker.nullness.qual.*;
*/
import java.io.File;
import java.util.*;
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.Label;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.TypeAnnotationVisitor;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Type;
import org.objectweb.asm.commons.EmptyVisitor;
import annotations.*;
import annotations.el.*;
import annotations.field.*;
import com.sun.tools.javac.code.TargetType;
import com.sun.tools.javac.code.TypeAnnotationPosition.TypePathEntry;
/**
* A <code> ClassAnnotationSceneReader </code> is a
* {@link org.objectweb.asm.ClassVisitor} that will insert all annotations it
* encounters while visiting a class into a given {@link AScene}.
*
* The "read" in <code> ClassAnnotationSceneReader </code> refers to a class
* file being read into a scene. Also see {@link ClassAnnotationSceneWriter}.
*
* <p>
*
* The proper usage of this class is to construct a
* <code>ClassAnnotationSceneReader}</code> with an {@link AScene} into which
* annotations should be inserted, then pass this as a
* {@link org.objectweb.asm.ClassVisitor} to
* {@link org.objectweb.asm.ClassReader#accept}
*
* <p>
*
* All other methods are intended to be called only by
* {@link org.objectweb.asm.ClassReader#accept},
* and should not be called anywhere else, due to the order in which
* {@link org.objectweb.asm.ClassVisitor} methods should be called.
*/
public class ClassAnnotationSceneReader
extends EmptyVisitor {
// general strategy:
// -only "Runtime[In]visible[Type]Annotations" are supported
// -use an empty visitor for everything besides annotations, fields and
// methods; for those three, use a special visitor that does all the work
// and inserts the annotations correctly into the specified AElement
// Whether to output tracing information
private static final boolean trace = false;
// Whether to output error messages for unsupported cases
private static final boolean strict = false;
// Whether to include annotations on compiler-generated methods
private final boolean ignoreBridgeMethods;
// The scene into which this class will insert annotations.
private final AScene scene;
// The AClass that represents this class in scene.
private AClass aClass;
private final ClassReader cr;
/**
* Holds definitions we've seen so far. Maps from annotation name to
* the definition itself. Maps from both the qualified name and the
* unqualified name. If the unqualified name is not unique, it maps
* to null and the qualified name should be used instead. */
private final Map<String, AnnotationDef> adefs = initAdefs();
private static Map<String,AnnotationDef> initAdefs() {
Map<String,AnnotationDef> result = new HashMap<String,AnnotationDef>();
for (AnnotationDef ad : Annotations.standardDefs) {
result.put(ad.name, ad);
}
return result;
}
/**
* constructs a new <code> ClassAnnotationSceneReader </code> that will
* insert all the annotations in the class that it visits into
* <code> scene </code>
* @param cr
*
* @param scene the annotation scene into which annotations this visits
* will be inserted
* @param ignoreBridgeMethods whether to omit annotations on
* compiler-generated methods
*/
public ClassAnnotationSceneReader(ClassReader cr, AScene scene,
boolean ignoreBridgeMethods) {
this.cr = cr;
this.scene = scene;
this.ignoreBridgeMethods = ignoreBridgeMethods;
}
/**
* @see org.objectweb.asm.commons.EmptyVisitor#visit(int, int, java.lang.String, java.lang.String, java.lang.String, java.lang.String[])
*/
@Override
public void visit(int version, int access, String name, String signature,
String superName, String[] interfaces) {
aClass = scene.classes.vivify(name.replace('/', '.'));
}
/**
* @see org.objectweb.asm.commons.EmptyVisitor#visitAnnotation(java.lang.String, boolean)
*/
@Override
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
if (trace) { System.out.printf("visitAnnotation(%s, %s) in %s (%s)%n", desc, visible, this, this.getClass()); }
return visitTypeAnnotation(desc, visible, false);
}
/**
* @see org.objectweb.asm.commons.EmptyVisitor#visitTypeAnnotation(java.lang.String, boolean, boolean)
*/
@Override
public TypeAnnotationVisitor visitTypeAnnotation(String desc, boolean visible, boolean inCode) {
if (trace) { System.out.printf("visitTypeAnnotation(%s, %s, %s); aClass=%s in %s (%s)%n", desc, inCode, visible, aClass, this, this.getClass()); }
return new AnnotationSceneReader(desc, visible, aClass);
}
/**
* @see org.objectweb.asm.commons.EmptyVisitor#visitField(int, java.lang.String, java.lang.String, java.lang.String, java.lang.Object)
*/
@Override
public FieldVisitor visitField(
int access,
String name,
String desc,
String signature,
Object value ) {
if (trace) { System.out.printf("visitField(%s, %s, %s, %s, %s) in %s (%s)%n", access, name, desc, signature, value, this, this.getClass()); }
AField aField = aClass.fields.vivify(name);
return new FieldAnnotationSceneReader(name, desc, signature, value, aField);
}
/**
* @see org.objectweb.asm.commons.EmptyVisitor#visitMethod(int, java.lang.String, java.lang.String, java.lang.String, java.lang.String[])
*/
@Override
public MethodVisitor visitMethod(
int access,
String name,
String desc,
String signature,
String[] exceptions) {
if (ignoreBridgeMethods && (access & Opcodes.ACC_BRIDGE) != 0) {
return null;
}
if (trace) { System.out.printf("visitMethod(%s, %s, %s, %s, %s) in %s (%s)%n", access, name, desc, signature, exceptions, this, this.getClass()); }
AMethod aMethod = aClass.methods.vivify(name+desc);
return new MethodAnnotationSceneReader(name, desc, signature, aMethod);
}
// converts JVML format to Java format
private static String classDescToName(String desc) {
return desc.substring(1, desc.length() - 1).replace('/', '.');
}
///////////////////////////////////////////////////////////////////////////
/// Inner classes
///
// Hackish workaround for odd subclassing.
@SuppressWarnings("signature")
String dummyDesc = "dummy";
/*
* Most of the complexity behind reading annotations from a class file into
* a scene is in AnnotationSceneReader, which fully implements the
* TypeAnnotationVisitor interface (and therefore also implements the
* AnnotationVisitor interface). It keeps an AElement of the
* element into which this should insert the annotations it visits in
* a class file. Thus, constructing an AnnotationSceneReader with an
* AElement of the right type is sufficient for writing out annotations
* to that element, which will be done once visitEnd() is called. Note that
* for when inner annotations are expected, the aElement passed in must be
* of the correct form (ATypeElement, or AMethod depending on the
* target type of the extended annotation).
*/
private class AnnotationSceneReader implements TypeAnnotationVisitor {
// Implementation strategy:
// For field values and enums, simply pass the information
// onto annotationBuilder.
// For arrays, use an ArrayAnnotationBuilder that will
// properly call the right annotationBuilder methods on its visitEnd().
// For nested annotations, use a NestedAnnotationSceneReader that will
// properly call the right annotationBuilder methods on its visitEnd().
// For extended information, store all arguments passed in and on
// this.visitEnd(), handle all the information based on target type.
// The AElement into which the annotation visited should be inserted.
protected AElement aElement;
// Whether or not this annotation is visible at runtime.
protected boolean visible;
// The AnnotationBuilder used to create this annotation.
private AnnotationBuilder annotationBuilder;
// since AnnotationSceneReader will work for both normal
// and extended annotations, all of the following information
// may or may not be present, so use a list to store
// information as it is received from visitX* methods, and
// correctly interpret the information in visitEnd()
// note that all of these should contain 0 or 1 elements,
// except for xLocations, which is actually a list
private final List<Integer> xTargetTypeArgs;
private final List<Integer> xIndexArgs;
private final List<Integer> xLengthArgs;
private final List<TypePathEntry> xLocationsArgs;
private final List<Integer> xLocationLengthArgs;
private final List<Integer> xOffsetArgs;
private final List<Integer> xStartPcArgs;
private final List<Integer> xParamIndexArgs;
private final List<Integer> xBoundIndexArgs;
private final List<Integer> xExceptionIndexArgs;
private final List<Integer> xTypeIndexArgs;
// private AnnotationDef getAnnotationDef(Object o) {
// if (o instanceof AnnotationDef) {
// return (AnnotationDef) o;
// } else if (o instanceof String) {
// return getAnnotationDef((String) o);
// } else {
// throw new Error(String.format("bad type %s : %s", o.getClass(), o));
// }
// }
@SuppressWarnings("unchecked")
private AnnotationDef getAnnotationDef(String jvmlClassName) {
String annoTypeName = classDescToName(jvmlClassName);
// It would be better to not require the .class file to be on the
// classpath, but to search for it on a path that is passed to this
// program. Worry about that later.
Class<? extends java.lang.annotation.Annotation> annoClass;
try {
annoClass = (Class<? extends java.lang.annotation.Annotation>) Class.forName(annoTypeName);
} catch (ClassNotFoundException e) {
System.out.printf("Could not find class: %s%n", e.getMessage());
printClasspath();
if (annoTypeName.contains("+")) {
return Annotations.createValueAnnotationDef(annoTypeName,
Annotations.noAnnotations, BasicAFT.forType(int.class));
}
throw new Error(e);
}
AnnotationDef ad = AnnotationDef.fromClass(annoClass, adefs);
return ad;
}
/*
* Constructs a new AnnotationScene reader with the given description and
* visibility. Calling visitEnd() will ensure that this writes out the
* annotation it visits into aElement.
* @param desc JVML format for the field being read, or ClassAnnotationSceneReader.dummyDesc
*/
public AnnotationSceneReader(String desc, boolean visible, AElement aElement) {
if (trace) { System.out.printf("AnnotationSceneReader(%s, %s, %s)%n", desc, visible, aElement); }
this.visible = visible;
this.aElement = aElement;
if (desc != dummyDesc) { // interned
AnnotationDef ad = getAnnotationDef(desc);
AnnotationBuilder ab = AnnotationFactory.saf.beginAnnotation(ad);
if (ab == null) {
throw new IllegalArgumentException("bad description: " + desc);
} else {
this.annotationBuilder = ab;
}
}
// For legal annotations, and except for xLocationsArgs, these should
// contain at most one element.
this.xTargetTypeArgs = new ArrayList<Integer>(1);
this.xIndexArgs = new ArrayList<Integer>(1);
this.xLengthArgs = new ArrayList<Integer>(1);
this.xLocationLengthArgs = new ArrayList<Integer>(1);
this.xOffsetArgs = new ArrayList<Integer>(1);
this.xStartPcArgs = new ArrayList<Integer>(1);
this.xLocationsArgs = new ArrayList<TypePathEntry>();
this.xParamIndexArgs = new ArrayList<Integer>(1);
this.xBoundIndexArgs = new ArrayList<Integer>(1);
this.xExceptionIndexArgs = new ArrayList<Integer>(1);
this.xTypeIndexArgs = new ArrayList<Integer>(1);
}
/*
* @see org.objectweb.asm.AnnotationVisitor#visit(java.lang.String, java.lang.Object)
*/
@Override
public void visit(String name, Object value) {
if (trace) { System.out.printf("visit(%s, %s) on %s%n", name, value, this); }
// BasicAFT.forType(Class) expects int.class instead of Integer.class,
// and so on for all primitives. String.class is ok, since it has no
// primitive type.
Class<?> c = value.getClass();
if (c.equals(Boolean.class)) {
c = boolean.class;
} else if (c.equals(Byte.class)) {
c = byte.class;
} else if (c.equals(Character.class)) {
c = char.class;
} else if (c.equals(Short.class)) {
c = short.class;
} else if (c.equals(Integer.class)) {
c = int.class;
} else if (c.equals(Long.class)) {
c = long.class;
} else if (c.equals(Float.class)) {
c = float.class;
} else if (c.equals(Double.class)) {
c = double.class;
} else if (c.equals(Type.class)) {
try {
annotationBuilder.addScalarField(name, ClassTokenAFT.ctaft, Class.forName(((Type)value).getClassName()));
} catch (ClassNotFoundException e) {
throw new RuntimeException("Could not load Class for Type: " + value, e);
}
// Return here, otherwise the annotationBuilder would be called
// twice for the same value.
return;
} else if (!c.equals(String.class)) {
// Only possible type for value is String, in which case c is already
// String.class, or array of primitive
c = c.getComponentType();
ArrayBuilder arrayBuilder = annotationBuilder.beginArrayField(
name, new ArrayAFT(BasicAFT.forType(c)));
// value is of type c[], now add in all the elements of the array
for (Object o : asList(value)) {
arrayBuilder.appendElement(o);
}
arrayBuilder.finish();
return;
}
// handle everything but arrays
annotationBuilder.addScalarField(name, BasicAFT.forType(c),value);
}
/*
* Method that accepts an Object whose actual type is c[], where c is a
* primitive, and returns an equivalent List<Object> that contains
* the same elements as in hiddenArray.
*/
private List<Object> asList(Object hiddenArray) {
List<Object> objects = new ArrayList<Object>();
Class<?> c = hiddenArray.getClass().getComponentType();
if (c.equals(boolean.class)) {
for (boolean o : (boolean[]) hiddenArray) {
objects.add(o);
}
} else if (c.equals(byte.class)) {
for (byte o : (byte[]) hiddenArray) {
objects.add(o);
}
} else if (c.equals(char.class)) {
for (char o : (char[]) hiddenArray) {
objects.add(o);
}
} else if (c.equals(short.class)) {
for (short o : (short[]) hiddenArray) {
objects.add(o);
}
} else if (c.equals(int.class)) {
for (int o : (int[]) hiddenArray) {
objects.add(o);
}
} else if (c.equals(long.class)) {
for (long o : (long[]) hiddenArray) {
objects.add(o);
}
} else if (c.equals(float.class)) {
for (float o : (float[]) hiddenArray) {
objects.add(o);
}
} else if (c.equals(double.class)) {
for (double o : (double[]) hiddenArray) {
objects.add(o);
}
} else {
throw new RuntimeException("Array has unknown type: " + hiddenArray);
}
return objects;
}
/*
* @see org.objectweb.asm.AnnotationVisitor#visitEnum(java.lang.String, java.lang.String, java.lang.String)
*/
@Override
public void visitEnum(String name, String desc, String value) {
if (trace) { System.out.printf("visitEnum(%s, %s) in %s (%s)%n", name, desc, this, this.getClass()); }
annotationBuilder.addScalarField(name, new EnumAFT(desc), value);
}
/*
* @see org.objectweb.asm.AnnotationVisitor#visitAnnotation(java.lang.String, java.lang.String)
*/
@Override
public AnnotationVisitor visitAnnotation(String name, String desc) {
if (trace) { System.out.printf("visitAnnotation(%s, %s) in %s (%s)%n", name, desc, this, this.getClass()); }
return new NestedAnnotationSceneReader(this, name, desc);
}
/*
* @see org.objectweb.asm.AnnotationVisitor#visitArray(java.lang.String)
*/
@Override
public AnnotationVisitor visitArray(String name) {
if (trace) { System.out.printf("visitArray(%s) in %s (%s)%n", name, this, this.getClass()); }
ArrayAFT aaft = (ArrayAFT) annotationBuilder.fieldTypes().get(name);
ScalarAFT aft = aaft.elementType;
return new ArrayAnnotationSceneReader(this, name, aft);
}
/*
* @see org.objectweb.asm.TypeAnnotationVisitor#visitXTargetType(int)
*/
@Override
public void visitXTargetType(int target_type) {
xTargetTypeArgs.add(target_type);
}
/*
* @see org.objectweb.asm.TypeAnnotationVisitor#visitXIndex(int)
*/
@Override
public void visitXIndex(int index) {
xIndexArgs.add(index);
}
/*
* @see org.objectweb.asm.TypeAnnotationVisitor#visitXLength(int)
*/
@Override
public void visitXLength(int length) {
xLengthArgs.add(length);
}
/*
* @see org.objectweb.asm.TypeAnnotationVisitor#visitXLocation(TypePathEntry)
*/
@Override
public void visitXLocation(TypePathEntry location) {
xLocationsArgs.add(location);
}
/*
* @see org.objectweb.asm.TypeAnnotationVisitor#visitXLocationLength(int)
*/
@Override
public void visitXLocationLength(int location_length) {
xLocationLengthArgs.add(location_length);
}
/*
* @see org.objectweb.asm.TypeAnnotationVisitor#visitXOffset(int)
*/
@Override
public void visitXOffset(int offset) {
xOffsetArgs.add(offset);
}
@Override
public void visitXNumEntries(int num_entries) {
}
/*
* @see org.objectweb.asm.TypeAnnotationVisitor#visitXStartPc(int)
*/
@Override
public void visitXStartPc(int start_pc) {
xStartPcArgs.add(start_pc);
}
/*
* @see org.objectweb.asm.TypeAnnotationVisitor#visitXBoundIndex(int)
*/
@Override
public void visitXParamIndex(int param_index) {
xParamIndexArgs.add(param_index);
}
/*
* @see org.objectweb.asm.TypeAnnotationVisitor#visitXBoundIndex(int)
*/
@Override
public void visitXBoundIndex(int bound_index) {
xBoundIndexArgs.add(bound_index);
}
@Override
public void visitXTypeIndex(int type_index) {
xTypeIndexArgs.add(type_index);
}
@Override
public void visitXExceptionIndex(int exception_index) {
xExceptionIndexArgs.add(exception_index);
}
@Override
public void visitXNameAndArgsSize() {
}
/*
* Visits the end of the annotation, and actually writes out the
* annotation into aElement.
*
* @see org.objectweb.asm.TypeAnnotationVisitor#visitEnd()
*/
@Override
public void visitEnd() {
if (trace) { System.out.printf("visitEnd on %s (%s)%n", this, this.getClass()); }
if (xTargetTypeArgs.size() >= 1) {
TargetType target = TargetType.fromTargetTypeValue(xTargetTypeArgs.get(0));
// TEMP
// If the expression used to initialize a field contains annotations
// on instanceofs, typecasts, or news, the extended compiler enters
// those annotations on the field. If we get such an annotation and
// aElement is a field, skip the annotation for now to avoid crashing.
switch(target) {
case FIELD:
handleField(aElement);
break;
case LOCAL_VARIABLE:
case RESOURCE_VARIABLE:
handleMethodLocalVariable((AMethod) aElement);
break;
case NEW:
if (aElement instanceof AMethod) {
handleMethodObjectCreation((AMethod) aElement);
} else {
// TODO: in field initializers
if (strict) { System.err.println("Unhandled NEW annotation for " + aElement); }
}
break;
case METHOD_FORMAL_PARAMETER:
handleMethodParameterType((AMethod) aElement);
break;
case METHOD_RECEIVER:
handleMethodReceiver((AMethod) aElement);
break;
case CAST:
if (aElement instanceof AMethod) {
handleMethodTypecast((AMethod) aElement);
} else {
// TODO: in field initializers
if (strict) { System.err.println("Unhandled TYPECAST annotation for " + aElement); }
}
break;
case METHOD_RETURN:
handleMethodReturnType((AMethod) aElement);
break;
case INSTANCEOF:
if (aElement instanceof AMethod) {
handleMethodInstanceOf((AMethod) aElement);
} else {
// TODO: in field initializers
if (strict) { System.err.println("Unhandled INSTANCEOF annotation for " + aElement); }
}
break;
case CLASS_TYPE_PARAMETER_BOUND:
handleClassTypeParameterBound((AClass) aElement);
break;
case METHOD_TYPE_PARAMETER_BOUND:
handleMethodTypeParameterBound((AMethod) aElement);
break;
case CLASS_EXTENDS:
handleClassExtends((AClass) aElement);
break;
case THROWS:
handleThrows((AMethod) aElement);
break;
case CONSTRUCTOR_REFERENCE: // TODO
case METHOD_REFERENCE:
handleMethodReference((AMethod) aElement);
break;
case CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT: // TODO
case METHOD_REFERENCE_TYPE_ARGUMENT:
handleReferenceTypeArgument((AMethod) aElement);
break;
case CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT: // TODO
case METHOD_INVOCATION_TYPE_ARGUMENT:
handleCallTypeArgument((AMethod) aElement);
break;
case METHOD_TYPE_PARAMETER:
handleMethodTypeParameter((AMethod) aElement);
break;
case CLASS_TYPE_PARAMETER:
handleClassTypeParameter((AClass) aElement);
break;
// TODO: ensure all cases covered.
default:
// Rather than throw an error here, since a declaration annotation
// is being used as an extended annotation, just make the
// annotation and place it in the given aElement as usual.
// aElement.tlAnnotationsHere.add(makeAnnotation());
Annotation a = makeAnnotation();
aElement.tlAnnotationsHere.add(a);
}
} else {
// This is not an extended annotation visitor, so just
// make the annotation and place it in the given AElement,
// possibly moving it to a type annotation location instead.
Annotation a = makeAnnotation();
if (a.def.isTypeAnnotation() && (aElement instanceof AMethod)) {
AMethod m = (AMethod) aElement;
m.returnType.tlAnnotationsHere.add(a);
// There is not currently a separate location for field/parameter
// type annotations; they are mixed in with the declaration
// annotations. This should be fixed in the future.
// Also, fields/parameters are just represented as AElement.
// } else if (a.def.isTypeAnnotation() && (aElement instanceof AField)) {
} else {
aElement.tlAnnotationsHere.add(a);
}
}
}
// The following are utility methods to facilitate creating all the
// necessary data structures in the scene library.
/*
* Returns an annotation, ready to be placed into the scene, from
* the information visited.
*/
public Annotation makeAnnotation() {
return annotationBuilder.finish();
}
/*
* Returns a LocalLocation for this annotation.
*/
private LocalLocation makeLocalLocation() {
int index = xIndexArgs.get(0);
int length = xLengthArgs.get(0);
int start = xStartPcArgs.get(0);
return new LocalLocation(index, start, length);
}
/*
* Returns an InnerTypeLocation for this annotation.
*/
private InnerTypeLocation makeInnerTypeLocation() {
return new InnerTypeLocation(xLocationsArgs);
}
/*
* Returns the offset for this annotation.
*/
private RelativeLocation makeOffset(boolean needTypeIndex) {
int offset = xOffsetArgs.get(0);
int typeIndex = needTypeIndex ? xTypeIndexArgs.get(0) : 0;
return RelativeLocation.createOffset(offset, typeIndex);
}
/*
* Returns the index for this annotation.
*/
/*
private int makeIndex() {
return xIndexArgs.get(0);
}
*/
/*
* Returns the bound location for this annotation.
*/
private BoundLocation makeTypeParameterLocation() {
if (!xParamIndexArgs.isEmpty()) {
return new BoundLocation(xParamIndexArgs.get(0), -1);
} else {
if (strict) { System.err.println("makeTypeParameterLocation with empty xParamIndexArgs!"); }
return new BoundLocation(Integer.MAX_VALUE, -1);
}
}
/*
* Returns the bound location for this annotation.
* @see #makeTypeParameterLocation()
*/
private BoundLocation makeBoundLocation() {
// TODO: Give up on unbounded wildcards for now!
if (!xParamIndexArgs.isEmpty()) {
return new BoundLocation(xParamIndexArgs.get(0), xBoundIndexArgs.get(0));
} else {
if (strict) { System.err.println("makeBoundLocation with empty xParamIndexArgs!"); }
return new BoundLocation(Integer.MAX_VALUE, Integer.MAX_VALUE);
}
}
private TypeIndexLocation makeTypeIndexLocation() {
return new TypeIndexLocation(xTypeIndexArgs.get(0));
}
// TODO: makeExceptionIndexLocation?
/*
* Creates the inner annotation on aElement.innerTypes.
*/
private void handleField(AElement aElement) {
if (xLocationsArgs.isEmpty()) {
// TODO: resolve issue once classfile format is finalized
if (aElement instanceof AClass) {
// handleFieldOnClass((AClass) aElement);
if (strict) { System.err.println("Unhandled FIELD annotation for " + aElement); }
} else if (aElement instanceof ATypeElement) {
aElement.tlAnnotationsHere.add(makeAnnotation());
} else {
throw new RuntimeException("Unknown FIELD aElement: " + aElement);
}
} else {
// TODO: resolve issue once classfile format is finalized
if (aElement instanceof AClass) {
// handleFieldGenericArrayOnClass((AClass) aElement);
if (strict) { System.err.println("Unhandled FIELD_COMPONENT annotation for " + aElement); }
} else if (aElement instanceof ATypeElement) {
ATypeElement aTypeElement = (ATypeElement) aElement;
aTypeElement
.innerTypes.vivify(makeInnerTypeLocation()).
tlAnnotationsHere.add(makeAnnotation());
} else {
throw new RuntimeException("Unknown FIELD_COMPONENT: " + aElement);
}
}
}
/*
* Creates the method receiver annotation on aMethod.
*/
private void handleMethodReceiver(AMethod aMethod) {
if (xLocationsArgs.isEmpty()) {
aMethod.receiver.type
.tlAnnotationsHere.add(makeAnnotation());
} else {
aMethod.receiver.type
.innerTypes.vivify(makeInnerTypeLocation())
.tlAnnotationsHere.add(makeAnnotation());
}
}
/*
* Creates the local variable annotation on aMethod.
*/
private void handleMethodLocalVariable(AMethod aMethod) {
if (xLocationsArgs.isEmpty()) {
aMethod.body.locals.vivify(makeLocalLocation())
.tlAnnotationsHere.add(makeAnnotation());
} else {
aMethod.body.locals.vivify(makeLocalLocation())
.type.innerTypes.vivify(makeInnerTypeLocation())
.tlAnnotationsHere.add(makeAnnotation());
}
}
/*
* Creates the object creation annotation on aMethod.
*/
private void handleMethodObjectCreation(AMethod aMethod) {
if (xLocationsArgs.isEmpty()) {
aMethod.body.news.vivify(makeOffset(false))
.tlAnnotationsHere.add(makeAnnotation());
} else {
aMethod.body.news.vivify(makeOffset(false))
.innerTypes.vivify(makeInnerTypeLocation())
.tlAnnotationsHere.add(makeAnnotation());
}
}
private int makeParamIndex() {
return xParamIndexArgs.get(0);
}
/*
* Creates the method parameter type generic/array annotation on aMethod.
*/
private void handleMethodParameterType(AMethod aMethod) {
if (xLocationsArgs.isEmpty()) {
aMethod.parameters.vivify(makeParamIndex()).type.tlAnnotationsHere.add(makeAnnotation());
} else {
aMethod.parameters.vivify(makeParamIndex()).type.innerTypes.vivify(
makeInnerTypeLocation()).tlAnnotationsHere.add(makeAnnotation());
}
}
/*
* Creates the typecast annotation on aMethod.
*/
private void handleMethodTypecast(AMethod aMethod) {
if (xLocationsArgs.isEmpty()) {
aMethod.body.typecasts.vivify(makeOffset(true))
.tlAnnotationsHere.add(makeAnnotation());
} else {
aMethod.body.typecasts.vivify(makeOffset(true))
.innerTypes.vivify(makeInnerTypeLocation())
.tlAnnotationsHere.add(makeAnnotation());
}
}
/*
* Creates the method return type generic/array annotation on aMethod.
*/
private void handleMethodReturnType(AMethod aMethod) {
// TODO: why is this traced and not other stuff?
if (trace) { System.out.printf("handleMethodReturnType(%s)%n", aMethod); }
if (xLocationsArgs.isEmpty()) {
aMethod.returnType
.tlAnnotationsHere.add(makeAnnotation());
} else {
aMethod.returnType
.innerTypes.vivify(makeInnerTypeLocation())
.tlAnnotationsHere.add(makeAnnotation());
}
}
/*
* Creates the method instance of annotation on aMethod.
*/
private void handleMethodInstanceOf(AMethod aMethod) {
if (xLocationsArgs.isEmpty()) {
aMethod.body.instanceofs.vivify(makeOffset(false))
.tlAnnotationsHere.add(makeAnnotation());
} else {
aMethod.body.typecasts.vivify(makeOffset(false))
.innerTypes.vivify(makeInnerTypeLocation())
.tlAnnotationsHere.add(makeAnnotation());
}
}
/*
* Creates the class type parameter bound annotation on aClass.
*/
private void handleClassTypeParameter(AClass aClass) {
aClass.bounds.vivify(makeTypeParameterLocation())
.tlAnnotationsHere.add(makeAnnotation());
}
/*
* Creates the class type parameter bound annotation on aClass.
*/
private void handleClassTypeParameterBound(AClass aClass) {
if (xLocationsArgs.isEmpty()) {
aClass.bounds.vivify(makeBoundLocation())
.tlAnnotationsHere.add(makeAnnotation());
} else {
aClass.bounds.vivify(makeBoundLocation())
.innerTypes.vivify(makeInnerTypeLocation())
.tlAnnotationsHere.add(makeAnnotation());
}
}
/*
* Creates the class type parameter bound annotation on aClass.
*/
private void handleMethodTypeParameterBound(AMethod aMethod) {
if (xLocationsArgs.isEmpty()) {
aMethod.bounds.vivify(makeBoundLocation())
.tlAnnotationsHere.add(makeAnnotation());
} else {
aMethod.bounds.vivify(makeBoundLocation())
.innerTypes.vivify(makeInnerTypeLocation())
.tlAnnotationsHere.add(makeAnnotation());
}
}
private void handleClassExtends(AClass aClass) {
if (xLocationsArgs.isEmpty()) {
aClass.extendsImplements.vivify(makeTypeIndexLocation())
.tlAnnotationsHere.add(makeAnnotation());
} else {
aClass.extendsImplements.vivify(makeTypeIndexLocation())
.innerTypes.vivify(makeInnerTypeLocation())
.tlAnnotationsHere.add(makeAnnotation());
}
}
private void handleThrows(AMethod aMethod) {
aMethod.throwsException.vivify(makeTypeIndexLocation())
.tlAnnotationsHere.add(makeAnnotation());
}
private void handleNewTypeArgument(AMethod aMethod) {
if (xLocationsArgs.isEmpty()) {
// aMethod.news.vivify(makeOffset()).innerTypes.vivify();
// makeInnerTypeLocation()).tlAnnotationsHere.add(makeAnnotation());
if (strict) { System.err.println("Unhandled handleNewTypeArgument on aMethod: " + aMethod); }
} else {
// if (strict) { System.err.println("Unhandled handleNewTypeArgumentGenericArray on aMethod: " + aMethod); }
}
}
private void handleMethodReference(AMethod aMethod) {
if (xLocationsArgs.isEmpty()) {
aMethod.body.refs.vivify(makeOffset(false))
.tlAnnotationsHere.add(makeAnnotation());
} else {
aMethod.body.refs.vivify(makeOffset(false))
.innerTypes.vivify(makeInnerTypeLocation())
.tlAnnotationsHere.add(makeAnnotation());
}
}
private void handleReferenceTypeArgument(AMethod aMethod) {
if (xLocationsArgs.isEmpty()) {
aMethod.body.refs.vivify(makeOffset(true))
.tlAnnotationsHere.add(makeAnnotation());
} else {
aMethod.body.refs.vivify(makeOffset(true))
.innerTypes.vivify(makeInnerTypeLocation())
.tlAnnotationsHere.add(makeAnnotation());
}
}
private void handleCallTypeArgument(AMethod aMethod) {
if (xLocationsArgs.isEmpty()) {
aMethod.body.calls.vivify(makeOffset(true))
.tlAnnotationsHere.add(makeAnnotation());
} else {
aMethod.body.calls.vivify(makeOffset(true))
.innerTypes.vivify(makeInnerTypeLocation())
.tlAnnotationsHere.add(makeAnnotation());
}
}
private void handleMethodTypeParameter(AMethod aMethod) {
// TODO: throw new RuntimeException("METHOD_TYPE_PARAMETER: to do");
}
/*
* Hook for NestedAnnotationSceneReader; overridden by
* ArrayAnnotationSceneReader to add an array element instead of a field
*/
void supplySubannotation(String fieldName, Annotation annotation) {
annotationBuilder.addScalarField(fieldName,
new AnnotationAFT(annotation.def()), annotation);
}
@Override
public String toString() {
return String.format("(AnnotationSceneReader: %s %s %s)",
aElement, visible, annotationBuilder);
}
}
/*
* A NestedAnnotationSceneReader is an AnnotationSceneReader
* that will read in an entire annotation on a field (of type annotation)
* of its parent, and once it has fully visited that annotation, it will
* call its parent annotation builder to include that field, so after
* its parent constructs and returns this as an AnnotationVisitor
* (visitAnnotation()), it no longer needs to worry about that field.
*/
private class NestedAnnotationSceneReader extends AnnotationSceneReader {
private final AnnotationSceneReader parent;
private final String name;
// private final String desc;
public NestedAnnotationSceneReader(AnnotationSceneReader parent,
String name, String desc) {
super(desc, parent.visible, parent.aElement);
if (trace) { System.out.printf("NestedAnnotationSceneReader(%s, %s, %s)%n", parent, name, desc); }
this.parent = parent;
this.name = name;
// this.desc = desc;
}
@Override
public void visitEnd() {
// Do not call super, as that already builds the annotation, causing an exception.
// super.visitEnd();
if (trace) { System.out.printf("visitEnd on %s (%s)%n", this, this.getClass()); }
Annotation a = super.makeAnnotation();
parent.supplySubannotation(name, a);
}
}
/*
* An ArrayAnnotationSceneReader is an AnnotationSceneReader
* that reads all elements of an array field
* of its parent, and once it has fully visited the array, it will
* call its parent annotation builder to include that array, so after
* its parent constructs and returns this as an AnnotationVisitor
* (visitArray()), it no longer needs to worry about that array.
*
* Note that by specification of AnnotationVisitor.visitArray(), the only
* methods that should be called on this are visit(String name, Object value)
* and visitEnd().
*/
// An AnnotationSceneReader reads an annotation. An
// ArrayAnnotationSceneReader reads an arbitrary array field, but not an
// entire annotation. So why is ArrayAnnotationSceneReader a subclass of
// AnnotationSceneReader? Pass ClassAnnotationSceneReader.dummyDesc
// in the superclass constructor to
// disable superclass behaviors that would otherwise cause trouble.
private class ArrayAnnotationSceneReader extends AnnotationSceneReader {
private final AnnotationSceneReader parent;
private ArrayBuilder arrayBuilder;
// private ScalarAFT elementType;
private final String arrayName;
// The element type may be unknown when this is called.
// But AnnotationSceneReader expects to know the element type.
public ArrayAnnotationSceneReader(AnnotationSceneReader parent,
String fieldName, AnnotationFieldType eltType) {
super(dummyDesc, parent.visible, parent.aElement);
if (trace) { System.out.printf("ArrayAnnotationSceneReader(%s, %s)%n", parent, fieldName); }
this.parent = parent;
this.arrayName = fieldName;
this.arrayBuilder = null;
}
private void prepareForElement(ScalarAFT elementType) {
if (trace) { System.out.printf("prepareForElement(%s) in %s (%s)%n", elementType, this, this.getClass()); }
assert elementType != null; // but, does this happen when reading from classfile?
if (arrayBuilder == null) {
// this.elementType = elementType;
arrayBuilder = parent.annotationBuilder.beginArrayField(arrayName,
new ArrayAFT(elementType));
}
}
// There are only so many different array types that are permitted in
// an annotation. (I'm not sure how relevant that is here.)
@Override
public void visit(String name, Object value) {
if (trace) { System.out.printf("visit(%s, %s) (%s) in %s (%s)%n", name, value, value.getClass(), this, this.getClass()); }
ScalarAFT aft;
if (value.getClass().equals(org.objectweb.asm.Type.class)) {
// What if it's an annotation?
aft = ClassTokenAFT.ctaft;
try {
value = Class.forName(((org.objectweb.asm.Type) value).getClassName());
} catch (ClassNotFoundException e) {
throw new RuntimeException("Could not load Class for Type: " + value, e);
}
} else {
Class<?> vc = value.getClass();
aft = BasicAFT.forType(vc);
// or: aft = (ScalarAFT) AnnotationFieldType.fromClass(vc, null);
}
assert aft != null;
prepareForElement(aft);
assert arrayBuilder != null;
arrayBuilder.appendElement(value);
}
@Override
public void visitEnum(String name, String desc, String value) {
if (trace) { System.out.printf("visitEnum(%s, %s, %s) in %s (%s)%n", name, desc, value, this, this.getClass()); }
prepareForElement(new EnumAFT(classDescToName(desc)));
assert arrayBuilder != null;
arrayBuilder.appendElement(value);
}
@Override
public AnnotationVisitor visitArray(String name) {
throw new AssertionError("Multidimensional array in annotation!");
}
@Override
public AnnotationVisitor visitAnnotation(String name, String desc) {
if (trace) { System.out.printf("visitAnnotation(%s, %s) in %s (%s)%n", name, desc, this, this.getClass()); }
// The NASR will regurgitate the name we pass here when it calls
// supplySubannotation. Since we ignore the name there, it doesn't
// matter what name we pass here.
return new NestedAnnotationSceneReader(this, name, desc);
}
@Override
public void visitEnd() {
if (trace) { System.out.printf("visitEnd on %s (%s)%n", this, this.getClass()); }
if (arrayBuilder != null) {
arrayBuilder.finish();
} else {
// This was a zero-element array
parent.annotationBuilder.addEmptyArrayField(arrayName);
}
}
@Override
void supplySubannotation(String fieldName, Annotation annotation) {
prepareForElement(new AnnotationAFT(annotation.def()));
assert arrayBuilder != null;
arrayBuilder.appendElement(annotation);
}
}
/*
* A FieldAnnotationSceneReader is a FieldVisitor that only cares about
* visiting [extended]annotations. Attributes are ignored and visitEnd() has
* no effect. An AnnotationSceneReader is returned for declaration and type
* AnnotationVisitors. The AnnotationSceneReaders have a reference to
* an ATypeElement that this is visiting, and they will write out
* all the information to that ATypeElement after visiting each annotation.
*/
private class FieldAnnotationSceneReader extends EmptyVisitor implements FieldVisitor {
/*
private final String name;
private final String desc;
private final String signature;
private final Object value;
*/
private final AElement aField;
public FieldAnnotationSceneReader(
String name,
String desc,
String signature,
Object value,
AElement aField) {
/*
this.name = name;
this.desc = desc;
this.signature = signature;
this.value = value;
*/
this.aField = aField;
}
@Override
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
if (trace) { System.out.printf("visitAnnotation(%s, %s) in %s (%s)%n", desc, visible, this, this.getClass()); }
return new AnnotationSceneReader(desc, visible, aField);
}
@Override
public TypeAnnotationVisitor visitTypeAnnotation(String desc, boolean visible, boolean inCode) {
if (trace) { System.out.printf("visitTypeAnnotation(%s, %s, %s); aField=%s, aField.type=%s in %s (%s)%n", desc, visible, inCode, aField, aField.type, this, this.getClass()); }
return new AnnotationSceneReader(desc, visible, aField.type);
}
}
/*
* Similarly to FieldAnnotationSceneReader, this is a MethodVisitor that
* only cares about visiting [extended]annotations. Attributes other than
* BootstrapMethods are ignored, all code is ignored, and visitEnd() has no
* effect. An AnnotationSceneReader
* is returned for declaration and type AnnotationVisitors. The
* AnnotationSceneReaders have a reference to an AMethod that this is
* visiting, and they will write out all the information to that
* AMethod after visiting each annotation.
*/
private class MethodAnnotationSceneReader extends EmptyVisitor implements MethodVisitor {
// private final String name;
// private final String desc;
// private final String signature;
private final AElement aMethod;
public MethodAnnotationSceneReader(String name, String desc, String signature, AElement aMethod) {
// this.name = name;
// this.desc = desc;
// this.signature = signature;
this.aMethod = aMethod;
}
@Override
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
if (trace) { System.out.printf("visitAnnotation(%s, %s) in %s (%s)%n", desc, visible, this, this.getClass()); }
return visitTypeAnnotation(desc, visible, false);
}
@Override
public TypeAnnotationVisitor visitTypeAnnotation(String desc, boolean visible, boolean inCode) {
if (trace) { System.out.printf("visitTypeAnnotation(%s, %s) method=%s in %s (%s)%n", desc, visible, inCode, aMethod, this, this.getClass()); }
return new AnnotationSceneReader(desc, visible, aMethod);
}
@Override
public AnnotationVisitor visitParameterAnnotation(int parameter, String desc, boolean visible) {
if (trace) { System.out.printf("visitParameterAnnotation(%s, %s, %s) in %s (%s)%n", parameter, desc, visible, this, this.getClass()); }
return new AnnotationSceneReader(desc, visible,
((AMethod) aMethod).parameters.vivify(parameter));
}
@Override
public void visitLocalVariable(String name, String desc, String signature,
Label start, Label end, int index) {
// TODO!
}
// TODO: visit code!
}
public static void printClasspath() {
System.out.println("\nClasspath:");
StringTokenizer tokenizer =
new StringTokenizer(System.getProperty("java.class.path"),
File.pathSeparator);
while (tokenizer.hasMoreTokens()) {
System.out.println(" " + tokenizer.nextToken());
}
}
}