378 lines
12 KiB
Java
378 lines
12 KiB
Java
/*******************************************************************************
|
|
* Copyright (c) 2002,2006 IBM Corporation.
|
|
* All rights reserved. This program and the accompanying materials
|
|
* are made available under the terms of the Eclipse Public License v1.0
|
|
* which accompanies this distribution, and is available at
|
|
* http://www.eclipse.org/legal/epl-v10.html
|
|
*
|
|
* Contributors:
|
|
* IBM Corporation - initial API and implementation
|
|
*******************************************************************************/
|
|
package com.ibm.wala.shrikeCT;
|
|
|
|
import java.util.Arrays;
|
|
import java.util.Map;
|
|
|
|
import com.ibm.wala.util.collections.HashMapFactory;
|
|
import com.ibm.wala.util.collections.Pair;
|
|
import com.ibm.wala.util.debug.Assertions;
|
|
|
|
/**
|
|
* This class reads Annotations attributes, e.g., RuntimeInvisibleAnnotations.
|
|
*
|
|
* @author sjfink
|
|
*/
|
|
public class AnnotationsReader extends AttributeReader {
|
|
|
|
/**
|
|
* offset in class file where this attribute begins
|
|
*/
|
|
private final int beginOffset;
|
|
|
|
public AnnotationsReader(ClassReader.AttrIterator iter, String label) throws InvalidClassFileException {
|
|
super(iter, label);
|
|
beginOffset = attr;
|
|
}
|
|
|
|
/**
|
|
* @return number of annotations in this attribute
|
|
* @throws InvalidClassFileException
|
|
*/
|
|
public int getAnnotationCount() throws InvalidClassFileException {
|
|
int offset = beginOffset + 6;
|
|
checkSize(offset, 2);
|
|
return cr.getUShort(offset);
|
|
}
|
|
|
|
/**
|
|
* @return total length of this attribute in bytes, <bf>including</bf> the
|
|
* first 6 bytes
|
|
* @throws InvalidClassFileException
|
|
*/
|
|
public int getAttributeSize() throws InvalidClassFileException {
|
|
int offset = beginOffset + 2;
|
|
checkSize(offset, 4);
|
|
return cr.getInt(offset) + 6;
|
|
}
|
|
|
|
/**
|
|
* get the Utf8 constant pool value, where the constant pool offset is given
|
|
* in the class
|
|
*
|
|
* @param offset
|
|
* offset in the class file at which the constant pool offset is
|
|
* given
|
|
*/
|
|
private String getUtf8ConstantPoolValue(int offset) throws InvalidClassFileException {
|
|
checkSize(offset, 2);
|
|
int cpOffset = cr.getUShort(offset);
|
|
return cr.getCP().getCPUtf8(cpOffset);
|
|
}
|
|
|
|
/**
|
|
* Marker interface for possible element values in an annotation attribute.
|
|
*
|
|
* @see AnnotationsReader#readElementValueAndSize(int)
|
|
*
|
|
*/
|
|
@SuppressWarnings("javadoc")
|
|
public static interface ElementValue {
|
|
}
|
|
|
|
/**
|
|
* Represents a constant argument to an annotation. Class arguments (e.g.,
|
|
* <code>Foo.class</code>) are also represented with this type, with the value
|
|
* being the String class name.
|
|
*/
|
|
public static class ConstantElementValue implements ElementValue {
|
|
|
|
/**
|
|
* the constant value
|
|
*/
|
|
public final Object val;
|
|
|
|
public ConstantElementValue(Object val) {
|
|
this.val = val;
|
|
}
|
|
|
|
@Override
|
|
public String toString() {
|
|
return String.valueOf(val);
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* Represents enum constant annotation arguments.
|
|
*/
|
|
public static class EnumElementValue implements ElementValue {
|
|
|
|
/**
|
|
* the name of the enum type
|
|
*/
|
|
public final String enumType;
|
|
|
|
/**
|
|
* the enum value
|
|
*/
|
|
public final String enumVal;
|
|
|
|
public EnumElementValue(String enumType, String enumVal) {
|
|
super();
|
|
this.enumType = enumType;
|
|
this.enumVal = enumVal;
|
|
}
|
|
|
|
@Override
|
|
public String toString() {
|
|
return "EnumElementValue [type=" + enumType + ", val=" + enumVal + "]";
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* represents an annotation argument that itself is an array of arguments
|
|
*/
|
|
public static class ArrayElementValue implements ElementValue {
|
|
|
|
/**
|
|
* the values contained in the array
|
|
*/
|
|
public final ElementValue[] vals;
|
|
|
|
public ArrayElementValue(ElementValue[] vals) {
|
|
super();
|
|
this.vals = vals;
|
|
}
|
|
|
|
@Override
|
|
public String toString() {
|
|
return "ArrayElementValue [vals=" + Arrays.toString(vals) + "]";
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* get all the annotations declared in this attribute.
|
|
*
|
|
* @throws InvalidClassFileException
|
|
*/
|
|
public AnnotationAttribute[] getAllAnnotations() throws InvalidClassFileException {
|
|
AnnotationAttribute[] result = new AnnotationAttribute[getAnnotationCount()];
|
|
int offset = beginOffset + 8; // skip attribute_name_index,
|
|
// attribute_length, and num_annotations
|
|
for (int i = 0; i < result.length; i++) {
|
|
Pair<AnnotationAttribute, Integer> attributeAndSize = getAttributeAndSize(offset);
|
|
result[i] = attributeAndSize.fst;
|
|
offset += attributeAndSize.snd;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* <pre>
|
|
* param_annotations {
|
|
* u2 attribute_name_index;
|
|
* u4 attribute_length;
|
|
* u1 num_parameters;
|
|
* {
|
|
* u2 num_annotations;
|
|
* annotation annotations[num_annotations];
|
|
* } parameter_annotations[num_parameters];
|
|
* </pre>
|
|
*/
|
|
public AnnotationAttribute[][] getAllParameterAnnotations() throws InvalidClassFileException {
|
|
int numParamOffset = beginOffset + 6;
|
|
checkSize(numParamOffset, 1);
|
|
int paramCount = cr.getByte(numParamOffset);
|
|
AnnotationAttribute[][] result = new AnnotationAttribute[paramCount][];
|
|
// skip attribute_name_index, attribute_length, and num_parameters
|
|
int offset = beginOffset + 7;
|
|
for (int i = 0; i < result.length; i++) {
|
|
checkSize(offset, 2);
|
|
result[i] = new AnnotationAttribute[cr.getUShort(offset)];
|
|
offset += 2;
|
|
for (int j = 0; j < result[i].length; j++) {
|
|
Pair<AnnotationAttribute, Integer> attributeAndSize = getAttributeAndSize(offset);
|
|
result[i][j] = attributeAndSize.fst;
|
|
offset += attributeAndSize.snd;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
/**
|
|
* <pre>
|
|
* annotation {
|
|
* u2 type_index;
|
|
* u2 num_element_value_pairs;
|
|
* { u2 element_name_index;
|
|
* element_value value;
|
|
* } element_value_pairs[num_element_value_pairs]
|
|
* </pre>
|
|
*
|
|
* @throws InvalidClassFileException
|
|
*/
|
|
private Pair<AnnotationAttribute, Integer> getAttributeAndSize(int begin) throws InvalidClassFileException {
|
|
String type = getUtf8ConstantPoolValue(begin);
|
|
int numElementValuePairs = cr.getUShort(begin + 2);
|
|
int size = 4;
|
|
int offset = begin + 4;
|
|
Map<String, ElementValue> elementName2Val = HashMapFactory.make();
|
|
for (int i = 0; i < numElementValuePairs; i++) {
|
|
String elementName = getUtf8ConstantPoolValue(offset);
|
|
offset += 2;
|
|
Pair<ElementValue, Integer> elementValAndSize = readElementValueAndSize(offset);
|
|
offset += elementValAndSize.snd;
|
|
size += elementValAndSize.snd + 2;
|
|
elementName2Val.put(elementName, elementValAndSize.fst);
|
|
}
|
|
return Pair.make(new AnnotationAttribute(type, elementName2Val), size);
|
|
}
|
|
|
|
/**
|
|
* Representation of an annotation attribute. An annotation has the following
|
|
* format in the bytecode:
|
|
*
|
|
* <pre>
|
|
* annotation {
|
|
* u2 type_index;
|
|
* u2 num_element_value_pairs;
|
|
* { u2 element_name_index;
|
|
* element_value value;
|
|
* } element_value_pairs[num_element_value_pairs];
|
|
* </pre>
|
|
*
|
|
* See the JVM specification section 4.7.16 for details.
|
|
*
|
|
* This class implements {@link ElementValue} to handle nested annotations.
|
|
*/
|
|
public static class AnnotationAttribute implements ElementValue {
|
|
|
|
/**
|
|
* the type of the annotation
|
|
*/
|
|
public final String type;
|
|
|
|
/**
|
|
* the arguments to the annotation
|
|
*/
|
|
public final Map<String, ElementValue> elementValues;
|
|
|
|
public AnnotationAttribute(String type, Map<String, ElementValue> elementValues) {
|
|
super();
|
|
this.type = type;
|
|
this.elementValues = elementValues;
|
|
}
|
|
|
|
@Override
|
|
public String toString() {
|
|
return "AnnotationElementValue [type=" + type + ", elementValues=" + elementValues + "]";
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* <pre>
|
|
* element_value {
|
|
* u1 tag;
|
|
* union {
|
|
* u2 const_value_index;
|
|
* { u2 type_name_index;
|
|
* u2 const_name_index;
|
|
* } enum_const_value;
|
|
* u2 class_info_index;
|
|
* annotation annotation_value;
|
|
* { u2 num_values;
|
|
* element_value values[num_values];
|
|
* } array_value;
|
|
* } value;
|
|
* </pre>
|
|
*
|
|
* A constant value (including class info) is represented by a
|
|
* {@link ConstantElementValue}. An enum constant value is represented by an
|
|
* {@link EnumElementValue}. An array value is represented by an
|
|
* {@link ArrayElementValue}. Finally, a nested annotation is represented by
|
|
* an {@link AnnotationAttribute}.
|
|
*
|
|
* @throws InvalidClassFileException
|
|
* @throws IllegalArgumentException
|
|
*/
|
|
private Pair<ElementValue, Integer> readElementValueAndSize(int offset) throws IllegalArgumentException,
|
|
InvalidClassFileException {
|
|
char tag = (char) cr.getByte(offset);
|
|
// meaning of this short depends on the tag
|
|
int nextShort = cr.getUShort(offset + 1);
|
|
switch (tag) {
|
|
case 'B':
|
|
case 'C':
|
|
case 'I':
|
|
case 'S':
|
|
case 'Z':
|
|
return Pair.<ElementValue, Integer> make(new ConstantElementValue(cr.getCP().getCPInt(nextShort)), 3);
|
|
case 'J':
|
|
return Pair.<ElementValue, Integer> make(new ConstantElementValue(cr.getCP().getCPLong(nextShort)), 3);
|
|
case 'D':
|
|
return Pair.<ElementValue, Integer> make(new ConstantElementValue(cr.getCP().getCPDouble(nextShort)), 3);
|
|
case 'F':
|
|
return Pair.<ElementValue, Integer> make(new ConstantElementValue(cr.getCP().getCPFloat(nextShort)), 3);
|
|
case 's': // string
|
|
case 'c': // class; just represent as a constant element with the type name
|
|
return Pair.<ElementValue, Integer> make(new ConstantElementValue(cr.getCP().getCPUtf8(nextShort)), 3);
|
|
case 'e': // enum
|
|
return Pair.<ElementValue, Integer> make(
|
|
new EnumElementValue(cr.getCP().getCPUtf8(nextShort), cr.getCP().getCPUtf8(cr.getUShort(offset + 3))), 5);
|
|
case '[': // array
|
|
int numValues = nextShort;
|
|
int numArrayBytes = 3; // start with 3 for the tag and num_values bytes
|
|
ElementValue[] vals = new ElementValue[numValues];
|
|
// start curOffset at beginning of array values
|
|
int curArrayOffset = offset + 3;
|
|
for (int i = 0; i < numValues; i++) {
|
|
Pair<ElementValue, Integer> arrayElemValueAndSize = readElementValueAndSize(curArrayOffset);
|
|
vals[i] = arrayElemValueAndSize.fst;
|
|
curArrayOffset += arrayElemValueAndSize.snd;
|
|
numArrayBytes += arrayElemValueAndSize.snd;
|
|
}
|
|
return Pair.<ElementValue, Integer> make(new ArrayElementValue(vals), numArrayBytes);
|
|
case '@': // annotation
|
|
Pair<AnnotationAttribute, Integer> attributeAndSize = getAttributeAndSize(offset + 1);
|
|
// add 1 to size for the tag
|
|
return Pair.<ElementValue, Integer> make(attributeAndSize.fst, attributeAndSize.snd + 1);
|
|
default:
|
|
assert false;
|
|
return null;
|
|
}
|
|
}
|
|
|
|
// //////////////
|
|
// utility methods for reading well-known annotation types
|
|
// //////////////
|
|
|
|
public static enum AnnotationType {
|
|
RuntimeVisibleAnnotations, RuntimeInvisibleAnnotations,RuntimeVisibleParameterAnnotations, RuntimeInvisibleParameterAnnotations
|
|
}
|
|
|
|
public static boolean isKnownAnnotation(String name) {
|
|
for (AnnotationType t : AnnotationType.values()) {
|
|
if (t.name().equals(name)) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public static AnnotationsReader getReaderForAnnotation(AnnotationType type, ClassReader.AttrIterator iter) {
|
|
// search for the desired attribute
|
|
final String attrName = type.toString();
|
|
try {
|
|
for (; iter.isValid(); iter.advance()) {
|
|
if (iter.getName().equals(attrName)) {
|
|
return new AnnotationsReader(iter, attrName);
|
|
}
|
|
}
|
|
} catch (InvalidClassFileException e) {
|
|
Assertions.UNREACHABLE();
|
|
}
|
|
return null;
|
|
}
|
|
} |