Revamped support for reading Java annotation information from .class files.

The code should handle all cases now, and the APIs are improved.

git-svn-id: https://wala.svn.sourceforge.net/svnroot/wala/trunk@4422 f5eafffb-2e1d-0410-98e4-8ec43c5233c4
This commit is contained in:
msridhar1 2012-01-11 17:11:59 +00:00
parent c40917c3da
commit 7630cd79c9
12 changed files with 465 additions and 242 deletions

View File

@ -0,0 +1,24 @@
/*******************************************************************************
* Copyright (c) 2008 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 annotations;
@AnnotationWithParams(strParam="classStrParam")
public class AnnotatedClass3 {
@AnnotationWithParams(enumParam=AnnotationEnum.VAL1,strArrParam={"biz","boz"},annotParam=@AnnotationWithSingleParam("sdfevs"),strParam="sdfsevs",intParam=25,klassParam=Integer.class)
public static void foo() {
}
@AnnotationWithParams(strArrParam={})
public static void emptyArray() {}
}

View File

@ -0,0 +1,16 @@
/*******************************************************************************
* Copyright (c) 2008 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 annotations;
public enum AnnotationEnum {
VAL1, VAL2
}

View File

@ -0,0 +1,22 @@
/*******************************************************************************
* Copyright (c) 2008 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 annotations;
public @interface AnnotationWithParams {
String strParam() default "strdef";
int intParam() default 0;
Class klassParam() default Object.class;
AnnotationEnum enumParam() default AnnotationEnum.VAL2;
String[] strArrParam() default {"foo","baz"};
int[] intArrParam() default {3,4};
AnnotationWithSingleParam annotParam() default @AnnotationWithSingleParam("fsf");
}

View File

@ -0,0 +1,16 @@
/*******************************************************************************
* Copyright (c) 2008 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 annotations;
public @interface AnnotationWithSingleParam {
String value() default "";
}

View File

@ -4,10 +4,14 @@ import java.io.IOException;
import java.util.Collection;
import java.util.Collections;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;
import com.ibm.wala.classLoader.IClass;
import com.ibm.wala.classLoader.IMethod;
import com.ibm.wala.classLoader.ShrikeCTMethod;
import com.ibm.wala.classLoader.ShrikeClass;
import com.ibm.wala.core.tests.callGraph.CallGraphTestUtil;
import com.ibm.wala.core.tests.util.TestConstants;
@ -15,8 +19,11 @@ import com.ibm.wala.core.tests.util.WalaTestCase;
import com.ibm.wala.ipa.callgraph.AnalysisScope;
import com.ibm.wala.ipa.cha.ClassHierarchy;
import com.ibm.wala.ipa.cha.ClassHierarchyException;
import com.ibm.wala.ipa.cha.IClassHierarchy;
import com.ibm.wala.shrikeCT.InvalidClassFileException;
import com.ibm.wala.types.ClassLoaderReference;
import com.ibm.wala.types.MethodReference;
import com.ibm.wala.types.Selector;
import com.ibm.wala.types.TypeReference;
import com.ibm.wala.types.annotations.Annotation;
import com.ibm.wala.util.collections.HashSetFactory;
@ -29,39 +36,59 @@ public class AnnotationTest extends WalaTestCase {
justThisTest(AnnotationTest.class);
}
@Test public void testClassAnnotations1() throws Exception {
private static IClassHierarchy cha;
@BeforeClass
public static void before() throws IOException, ClassHierarchyException {
AnalysisScope scope = AnalysisScopeReader.readJavaScope(TestConstants.WALA_TESTDATA,
FileProvider.getFile(CallGraphTestUtil.REGRESSION_EXCLUSIONS), AnnotationTest.class.getClassLoader());
cha = ClassHierarchy.make(scope);
}
@AfterClass
public static void after() {
cha = null;
}
@Test
public void testClassAnnotations1() throws Exception {
TypeReference typeUnderTest = TypeReference.findOrCreate(ClassLoaderReference.Application, "Lannotations/AnnotatedClass1");
Collection<Annotation> expectedRuntimeInvisibleAnnotations = HashSetFactory.make();
expectedRuntimeInvisibleAnnotations.add(Annotation.make(TypeReference.findOrCreate(ClassLoaderReference.Application, "Lannotations/RuntimeInvisableAnnotation")));
expectedRuntimeInvisibleAnnotations.add(Annotation.make(TypeReference.findOrCreate(ClassLoaderReference.Application, "Lannotations/DefaultVisableAnnotation")));
expectedRuntimeInvisibleAnnotations.add(Annotation.make(TypeReference.findOrCreate(ClassLoaderReference.Application,
"Lannotations/RuntimeInvisableAnnotation")));
expectedRuntimeInvisibleAnnotations.add(Annotation.make(TypeReference.findOrCreate(ClassLoaderReference.Application,
"Lannotations/DefaultVisableAnnotation")));
Collection<Annotation> expectedRuntimeVisibleAnnotations = HashSetFactory.make();
expectedRuntimeVisibleAnnotations.add(Annotation.make(TypeReference.findOrCreate(ClassLoaderReference.Application, "Lannotations/RuntimeVisableAnnotation")));
testClassAnnontations(typeUnderTest, expectedRuntimeInvisibleAnnotations, expectedRuntimeVisibleAnnotations);
expectedRuntimeVisibleAnnotations.add(Annotation.make(TypeReference.findOrCreate(ClassLoaderReference.Application,
"Lannotations/RuntimeVisableAnnotation")));
testClassAnnotations(typeUnderTest, expectedRuntimeInvisibleAnnotations, expectedRuntimeVisibleAnnotations);
}
@Test public void testClassAnnotations2() throws Exception {
@Test
public void testClassAnnotations2() throws Exception {
TypeReference typeUnderTest = TypeReference.findOrCreate(ClassLoaderReference.Application, "Lannotations/AnnotatedClass2");
Collection<Annotation> expectedRuntimeInvisibleAnnotations = HashSetFactory.make();
expectedRuntimeInvisibleAnnotations.add(Annotation.make(TypeReference.findOrCreate(ClassLoaderReference.Application, "Lannotations/RuntimeInvisableAnnotation")));
expectedRuntimeInvisibleAnnotations.add(Annotation.make(TypeReference.findOrCreate(ClassLoaderReference.Application, "Lannotations/RuntimeInvisableAnnotation2")));
expectedRuntimeInvisibleAnnotations.add(Annotation.make(TypeReference.findOrCreate(ClassLoaderReference.Application,
"Lannotations/RuntimeInvisableAnnotation")));
expectedRuntimeInvisibleAnnotations.add(Annotation.make(TypeReference.findOrCreate(ClassLoaderReference.Application,
"Lannotations/RuntimeInvisableAnnotation2")));
Collection<Annotation> expectedRuntimeVisibleAnnotations = HashSetFactory.make();
expectedRuntimeVisibleAnnotations.add(Annotation.make(TypeReference.findOrCreate(ClassLoaderReference.Application, "Lannotations/RuntimeVisableAnnotation")));
expectedRuntimeVisibleAnnotations.add(Annotation.make(TypeReference.findOrCreate(ClassLoaderReference.Application, "Lannotations/RuntimeVisableAnnotation2")));
testClassAnnontations(typeUnderTest, expectedRuntimeInvisibleAnnotations, expectedRuntimeVisibleAnnotations);
expectedRuntimeVisibleAnnotations.add(Annotation.make(TypeReference.findOrCreate(ClassLoaderReference.Application,
"Lannotations/RuntimeVisableAnnotation")));
expectedRuntimeVisibleAnnotations.add(Annotation.make(TypeReference.findOrCreate(ClassLoaderReference.Application,
"Lannotations/RuntimeVisableAnnotation2")));
testClassAnnotations(typeUnderTest, expectedRuntimeInvisibleAnnotations, expectedRuntimeVisibleAnnotations);
}
private void testClassAnnontations(TypeReference typeUnderTest, Collection<Annotation> expectedRuntimeInvisibleAnnotations,
private void testClassAnnotations(TypeReference typeUnderTest, Collection<Annotation> expectedRuntimeInvisibleAnnotations,
Collection<Annotation> expectedRuntimeVisibleAnnotations) throws IOException, ClassHierarchyException,
InvalidClassFileException, com.ibm.wala.shrikeCT.AnnotationsReader.UnimplementedException {
AnalysisScope scope = AnalysisScopeReader.readJavaScope(TestConstants.WALA_TESTDATA, FileProvider.getFile(CallGraphTestUtil.REGRESSION_EXCLUSIONS), getClass().getClassLoader());
ClassHierarchy cha = ClassHierarchy.make(scope);
InvalidClassFileException {
IClass classUnderTest = cha.lookupClass(typeUnderTest);
Assert.assertNotNull(typeUnderTest.toString() + " not found", classUnderTest);
Assert.assertTrue(classUnderTest instanceof ShrikeClass);
@ -74,23 +101,45 @@ public class AnnotationTest extends WalaTestCase {
assertEqualCollections(expectedRuntimeVisibleAnnotations, runtimeVisibleAnnotations);
}
private <T> void assertEqualCollections(Collection<T> expected,
Collection<T> actual) {
if (expected == null){
private <T> void assertEqualCollections(Collection<T> expected, Collection<T> actual) {
if (expected == null) {
expected = Collections.emptySet();
}
if (actual == null){
if (actual == null) {
actual = Collections.emptySet();
}
if (expected.size() != actual.size()){
if (expected.size() != actual.size()) {
Assert.assertTrue("expected=" + expected + " actual=" + actual, false);
}
for (T a : expected){
Assert.assertTrue ("missing " + a.toString(), actual.contains(a));
for (T a : expected) {
Assert.assertTrue("missing " + a.toString(), actual.contains(a));
}
}
// String methodSig = "annotations.AnnotatedClass1.m1()V;";
// MethodReference mr = StringStuff.makeMethodReference(methodSig );
//
@Test
public void testClassAnnotations3() throws Exception {
TypeReference typeRef = TypeReference.findOrCreate(ClassLoaderReference.Application, "Lannotations/AnnotatedClass3");
IClass klass = cha.lookupClass(typeRef);
Assert.assertNotNull(klass);
ShrikeClass shrikeClass = (ShrikeClass) klass;
Collection<Annotation> classAnnotations = shrikeClass.getAnnotations(true);
Assert.assertEquals("[Annotation type <Application,Lannotations/AnnotationWithParams> {strParam=classStrParam}]",
classAnnotations.toString());
MethodReference methodRefUnderTest = MethodReference.findOrCreate(typeRef, Selector.make("foo()V"));
IMethod methodUnderTest = cha.resolveMethod(methodRefUnderTest);
Assert.assertNotNull(methodRefUnderTest.toString() + " not found", methodUnderTest);
Assert.assertTrue(methodUnderTest instanceof ShrikeCTMethod);
ShrikeCTMethod shrikeCTMethodUnderTest = (ShrikeCTMethod) methodUnderTest;
Collection<Annotation> runtimeInvisibleAnnotations = shrikeCTMethodUnderTest.getAnnotations(true);
Assert
.assertEquals(
"[Annotation type <Application,Lannotations/AnnotationWithParams> {enumParam=EnumElementValue [type=Lannotations/AnnotationEnum;, val=VAL1], strArrParam=ArrayElementValue [vals=[biz, boz]], annotParam=AnnotationElementValue [type=Lannotations/AnnotationWithSingleParam;, elementValues={value=sdfevs}], strParam=sdfsevs, intParam=25, klassParam=Ljava/lang/Integer;}]",
runtimeInvisibleAnnotations.toString());
}
}

View File

@ -11,15 +11,12 @@
package com.ibm.wala.classLoader;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import com.ibm.wala.ipa.cha.IClassHierarchy;
import com.ibm.wala.shrikeBT.Decoder;
import com.ibm.wala.shrikeBT.IndirectionData;
import com.ibm.wala.shrikeBT.shrikeCT.CTDecoder;
import com.ibm.wala.shrikeCT.AnnotationsReader;
import com.ibm.wala.shrikeCT.AnnotationsReader.UnimplementedException;
import com.ibm.wala.shrikeCT.ClassReader;
import com.ibm.wala.shrikeCT.ClassReader.AttrIterator;
import com.ibm.wala.shrikeCT.CodeReader;
@ -33,7 +30,6 @@ import com.ibm.wala.shrikeCT.SignatureReader;
import com.ibm.wala.types.TypeReference;
import com.ibm.wala.types.annotations.Annotation;
import com.ibm.wala.types.generics.MethodTypeSignature;
import com.ibm.wala.util.collections.HashSetFactory;
import com.ibm.wala.util.debug.Assertions;
/**
@ -276,7 +272,7 @@ public final class ShrikeCTMethod extends ShrikeBTMethod implements IBytecodeMet
return result;
}
private AnnotationsReader getAnnotationsReader(boolean runtimeInvisable) {
private AnnotationsReader getAnnotationsReader(boolean runtimeInvisible) {
ClassReader.AttrIterator iter = new AttrIterator();
getClassReader().initMethodAttributeIterator(shrikeMethodIndex, iter);
@ -284,7 +280,7 @@ public final class ShrikeCTMethod extends ShrikeBTMethod implements IBytecodeMet
AnnotationsReader result = null;
try {
for (; iter.isValid(); iter.advance()) {
if (runtimeInvisable) {
if (runtimeInvisible) {
if (iter.getName().equals(RuntimeInvisibleAnnotationsReader.attrName)) {
result = new RuntimeInvisibleAnnotationsReader(iter);
break;
@ -342,56 +338,22 @@ public final class ShrikeCTMethod extends ShrikeBTMethod implements IBytecodeMet
/**
* read the runtime-invisible annotations from the class file
*/
public Collection<Annotation> getRuntimeInvisibleAnnotations() throws InvalidClassFileException, UnimplementedException {
public Collection<Annotation> getRuntimeInvisibleAnnotations() throws InvalidClassFileException {
return getAnnotations(true);
}
/**
* read the runtime-visible annotations from the class file
*/
public Collection<Annotation> getRuntimeVisibleAnnotations() throws InvalidClassFileException, UnimplementedException {
public Collection<Annotation> getRuntimeVisibleAnnotations() throws InvalidClassFileException {
return getAnnotations(false);
}
public Collection<Annotation> getAnnotations(boolean runtimeInvisable) throws InvalidClassFileException, UnimplementedException {
AnnotationsReader r = getAnnotationsReader(runtimeInvisable);
if (r != null) {
int[] offsets = r.getAnnotationOffsets();
Collection<Annotation> result = HashSetFactory.make();
for (int i : offsets) {
String type = r.getAnnotationType(i);
type = type.replaceAll(";", "");
TypeReference t = TypeReference.findOrCreate(getDeclaringClass().getClassLoader().getReference(), type);
result.add(Annotation.make(t));
}
return result;
} else {
return Collections.emptySet();
}
public Collection<Annotation> getAnnotations(boolean runtimeInvisible) throws InvalidClassFileException {
AnnotationsReader r = getAnnotationsReader(runtimeInvisible);
return Annotation.getAnnotationsFromReader(r, getDeclaringClass().getClassLoader().getReference());
}
/**
* Retrieves all runtime-invisible annotations associated with a given index. Returns null if the index is not valid or the
* annotation contains arrays.
*/
public HashMap<String, String> getAnnotations(int index) {
AnnotationsReader r = getAnnotationsReader(true);
if (r == null)
return null;
int offsets[];
try {
offsets = r.getAnnotationOffsets();
if (offsets.length <= index)
return null;
} catch (Exception e) {
return null;
}
int curOffset = offsets[index];
HashMap<String, String> res = r.getAnnotationValues(curOffset);
return res;
}
private static final IndirectionData NO_INDIRECTIONS = new IndirectionData() {

View File

@ -12,14 +12,12 @@ package com.ibm.wala.classLoader;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import com.ibm.wala.ipa.cha.IClassHierarchy;
import com.ibm.wala.shrikeBT.Constants;
import com.ibm.wala.shrikeCT.AnnotationsReader;
import com.ibm.wala.shrikeCT.AnnotationsReader.UnimplementedException;
import com.ibm.wala.shrikeCT.ClassConstants;
import com.ibm.wala.shrikeCT.ClassReader;
import com.ibm.wala.shrikeCT.ClassReader.AttrIterator;
@ -32,7 +30,6 @@ import com.ibm.wala.types.TypeName;
import com.ibm.wala.types.TypeReference;
import com.ibm.wala.types.annotations.Annotation;
import com.ibm.wala.types.generics.ClassSignature;
import com.ibm.wala.util.collections.HashSetFactory;
import com.ibm.wala.util.debug.Assertions;
import com.ibm.wala.util.shrike.ShrikeClassReaderHandle;
import com.ibm.wala.util.strings.Atom;
@ -85,13 +82,8 @@ public final class ShrikeClass extends JVMClass<IClassLoader> {
Atom name = Atom.findOrCreateUnicodeAtom(cr.getFieldName(i));
ImmutableByteArray b = ImmutableByteArray.make(cr.getFieldType(i));
Collection<Annotation> annotations = null;
try {
annotations = getRuntimeInvisibleAnnotations(i);
annotations = annotations.isEmpty() ? null : annotations;
} catch (UnimplementedException e) {
e.printStackTrace();
// keep going
}
if ((accessFlags & ClassConstants.ACC_STATIC) == 0) {
addFieldToList(instanceList, name, b, accessFlags, annotations);
@ -226,29 +218,17 @@ public final class ShrikeClass extends JVMClass<IClassLoader> {
reader.clear();
}
public Collection<Annotation> getRuntimeInvisibleAnnotations() throws InvalidClassFileException, UnimplementedException {
public Collection<Annotation> getRuntimeInvisibleAnnotations() throws InvalidClassFileException {
return getAnnotations(true);
}
public Collection<Annotation> getRuntimeVisibleAnnotations() throws InvalidClassFileException, UnimplementedException {
public Collection<Annotation> getRuntimeVisibleAnnotations() throws InvalidClassFileException {
return getAnnotations(false);
}
public Collection<Annotation> getAnnotations(boolean runtimeInvisable) throws InvalidClassFileException, UnimplementedException {
AnnotationsReader r = getAnnotationsReader(runtimeInvisable);
if (r != null) {
int[] offsets = r.getAnnotationOffsets();
Collection<Annotation> result = HashSetFactory.make();
for (int i : offsets) {
String type = r.getAnnotationType(i);
type = type.replaceAll(";", "");
TypeReference t = TypeReference.findOrCreate(getClassLoader().getReference(), type);
result.add(Annotation.make(t));
}
return result;
} else {
return Collections.emptySet();
}
public Collection<Annotation> getAnnotations(boolean runtimeInvisible) throws InvalidClassFileException {
AnnotationsReader r = getAnnotationsReader(runtimeInvisible);
return Annotation.getAnnotationsFromReader(r, getClassLoader().getReference());
}
private AnnotationsReader getAnnotationsReader(boolean runtimeInvisable) throws InvalidClassFileException {
@ -278,6 +258,7 @@ public final class ShrikeClass extends JVMClass<IClassLoader> {
return result;
}
private InnerClassesReader getInnerClassesReader() throws InvalidClassFileException {
ClassReader r = reader.get();
ClassReader.AttrIterator attrs = new ClassReader.AttrIterator();
@ -320,22 +301,9 @@ public final class ShrikeClass extends JVMClass<IClassLoader> {
/**
* read the runtime-invisible annotations from the class file
*/
public Collection<Annotation> getRuntimeInvisibleAnnotations(int fieldIndex) throws InvalidClassFileException,
UnimplementedException {
public Collection<Annotation> getRuntimeInvisibleAnnotations(int fieldIndex) throws InvalidClassFileException {
RuntimeInvisibleAnnotationsReader r = getRuntimeInvisibleAnnotationsReader(fieldIndex);
if (r != null) {
int[] offsets = r.getAnnotationOffsets();
Collection<Annotation> result = HashSetFactory.make();
for (int i : offsets) {
String type = r.getAnnotationType(i);
type = type.replaceAll(";", "");
TypeReference t = TypeReference.findOrCreate(getClassLoader().getReference(), type);
result.add(Annotation.make(t));
}
return result;
} else {
return Collections.emptySet();
}
return Annotation.getAnnotationsFromReader(r, getClassLoader().getReference());
}
private SignatureReader getSignatureReader() throws InvalidClassFileException {

View File

@ -11,41 +11,95 @@
package com.ibm.wala.types.annotations;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import com.ibm.wala.shrikeCT.AnnotationsReader;
import com.ibm.wala.shrikeCT.AnnotationsReader.AnnotationAttribute;
import com.ibm.wala.shrikeCT.AnnotationsReader.ElementValue;
import com.ibm.wala.shrikeCT.InvalidClassFileException;
import com.ibm.wala.types.ClassLoaderReference;
import com.ibm.wala.types.TypeReference;
import com.ibm.wala.util.collections.HashSetFactory;
import com.ibm.wala.util.collections.Pair;
/**
* Represents a Java 5.0 class file annotation
* Represents a member annotation, e.g., Java 5.0 class file annotations
*/
public class Annotation {
private final TypeReference type;
private final Pair<TypeReference, Object>[] arguments;
private Annotation(TypeReference type, Pair<TypeReference, Object>[] arguments) {
/**
* named arguments to the annotation, represented as a mapping from name to
* value. Note that for Java annotation arguments, the values are always
* Strings, independent of their actual type in the bytecode.
*/
private final Map<String, ElementValue> namedArguments;
/**
* unnamed arguments to the annotation (e.g., constructor arguments for C#
* attributes), represented as an array of pairs (T,V), where T is the
* argument type and V is the value. The array preserves the order in which
* the arguments were passed. If null, there are no unnamed arguments.
*/
private final Pair<TypeReference, Object>[] unnamedArguments;
private Annotation(TypeReference type, Map<String, ElementValue> namedArguments, Pair<TypeReference, Object>[] unnamedArguments) {
this.type = type;
this.arguments = arguments;
if (namedArguments == null) {
throw new IllegalArgumentException("namedArguments is null");
}
this.namedArguments = namedArguments;
this.unnamedArguments = unnamedArguments;
}
public static Annotation make(TypeReference t, Pair<TypeReference, Object>[] arguments) {
return new Annotation(t, arguments);
public static Annotation makeUnnamedAndNamed(TypeReference t, Map<String, ElementValue> namedArguments, Pair<TypeReference,Object>[] unnamedArguments) {
return new Annotation(t, namedArguments, unnamedArguments);
}
public static Annotation makeWithUnnamed(TypeReference t, Pair<TypeReference, Object>[] unnamedArguments) {
return new Annotation(t, Collections.<String,ElementValue>emptyMap(), unnamedArguments);
}
public static Annotation make(TypeReference t) {
return make(t, null);
return new Annotation(t, Collections.<String,ElementValue>emptyMap(), null);
}
public static Annotation makeWithNamed(TypeReference t, Map<String,ElementValue> namedArguments) {
return new Annotation(t, namedArguments, null);
}
public static Collection<Annotation> getAnnotationsFromReader(AnnotationsReader r, ClassLoaderReference clRef) throws InvalidClassFileException {
if (r != null) {
AnnotationAttribute[] allAnnotations = r.getAllAnnotations();
Collection<Annotation> result = HashSetFactory.make();
for (AnnotationAttribute annot : allAnnotations) {
String type = annot.type;
type = type.replaceAll(";", "");
TypeReference t = TypeReference.findOrCreate(clRef, type);
result.add(makeWithNamed(t, annot.elementValues));
}
return result;
} else {
return Collections.emptySet();
}
}
@Override
public String toString() {
StringBuffer sb = new StringBuffer("Annotation type " + type);
if (arguments != null) {
if (unnamedArguments != null) {
sb.append("[");
for(Pair<TypeReference, Object> arg : arguments) {
for (Pair<TypeReference, Object> arg : unnamedArguments) {
sb.append(" " + arg.fst.getName().getClassName() + ":" + arg.snd);
}
sb.append(" ]");
}
if (!namedArguments.isEmpty()) {
sb.append(" " + namedArguments);
}
return sb.toString();
}
@ -53,7 +107,7 @@ public class Annotation {
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + Arrays.hashCode(arguments);
result = prime * result + Arrays.hashCode(unnamedArguments);
result = prime * result + ((type == null) ? 0 : type.hashCode());
return result;
}
@ -67,7 +121,7 @@ public class Annotation {
if (getClass() != obj.getClass())
return false;
Annotation other = (Annotation) obj;
if (!Arrays.equals(arguments, other.arguments))
if (!Arrays.equals(unnamedArguments, other.unnamedArguments))
return false;
if (type == null) {
if (other.type != null)
@ -77,8 +131,12 @@ public class Annotation {
return true;
}
public Pair<TypeReference, Object>[] getArguments() {
return arguments;
public Pair<TypeReference, Object>[] getUnnamedArguments() {
return unnamedArguments;
}
public Map<String,ElementValue> getNamedArguments() {
return namedArguments;
}
public TypeReference getType() {

View File

@ -18,7 +18,6 @@ import com.ibm.wala.classLoader.IField;
import com.ibm.wala.classLoader.IMethod;
import com.ibm.wala.classLoader.ShrikeCTMethod;
import com.ibm.wala.classLoader.ShrikeClass;
import com.ibm.wala.shrikeCT.AnnotationsReader.UnimplementedException;
import com.ibm.wala.shrikeCT.InvalidClassFileException;
import com.ibm.wala.types.TypeName;
import com.ibm.wala.util.debug.Assertions;
@ -39,10 +38,7 @@ public class Annotations {
} catch (InvalidClassFileException e) {
e.printStackTrace();
Assertions.UNREACHABLE();
} catch (UnimplementedException e) {
e.printStackTrace();
Assertions.UNREACHABLE();
}
}
for (Annotation a : annotations) {
if (a.getType().getName().equals(type)) {
return true;
@ -63,9 +59,6 @@ public class Annotations {
} catch (InvalidClassFileException e) {
e.printStackTrace();
Assertions.UNREACHABLE();
} catch (UnimplementedException e) {
e.printStackTrace();
Assertions.UNREACHABLE();
}
for (Annotation a : annotations) {
if (a.getType().getName().equals(type)) {

View File

@ -16,3 +16,4 @@ Export-Package: com.ibm.wala.shrike.bench,
com.ibm.wala.shrikeBT.tools,
com.ibm.wala.shrikeCT
Bundle-RequiredExecutionEnvironment: J2SE-1.5
Require-Bundle: com.ibm.wala.util

View File

@ -23,6 +23,7 @@ import com.ibm.wala.shrikeBT.shrikeCT.CTDecoder;
import com.ibm.wala.shrikeBT.shrikeCT.ClassInstrumenter;
import com.ibm.wala.shrikeBT.shrikeCT.OfflineInstrumenter;
import com.ibm.wala.shrikeCT.AnnotationsReader;
import com.ibm.wala.shrikeCT.AnnotationsReader.AnnotationAttribute;
import com.ibm.wala.shrikeCT.ClassConstants;
import com.ibm.wala.shrikeCT.ClassReader;
import com.ibm.wala.shrikeCT.CodeReader;
@ -286,20 +287,8 @@ public class ClassPrinter {
private void printAnnotations(ClassReader cr, ClassReader.AttrIterator attrs, AnnotationsReader r)
throws InvalidClassFileException {
try {
int[] annotations = r.getAnnotationOffsets();
for (int j : annotations) {
w.write(" Annotation type: " + r.getAnnotationType(j) + "\n");
}
} catch (AnnotationsReader.UnimplementedException e) {
int len = attrs.getDataSize();
int pos = attrs.getDataOffset();
while (len > 0) {
int amount = Math.min(16, len);
w.write(" " + makeHex(cr.getBytes(), pos, amount, 32) + " " + makeChars(cr.getBytes(), pos, amount) + "\n");
len -= amount;
pos += amount;
}
for (AnnotationAttribute annot : r.getAllAnnotations()) {
w.write(" Annotation type: " + annot.type + "\n");
}
}

View File

@ -10,10 +10,14 @@
*******************************************************************************/
package com.ibm.wala.shrikeCT;
import java.util.HashMap;
import java.util.Arrays;
import java.util.Map;
import com.ibm.wala.util.collections.HashMapFactory;
import com.ibm.wala.util.collections.Pair;
/**
* This class reads Annotations attributes.
* This class reads Annotations attributes, e.g., RuntimeInvisibleAnnotations.
*
* @author sjfink
*/
@ -40,7 +44,8 @@ public class AnnotationsReader extends AttributeReader {
}
/**
* @return total length of this attribute in bytes, <bf>including</bf> the first 6 bytes
* @return total length of this attribute in bytes, <bf>including</bf> the
* first 6 bytes
* @throws InvalidClassFileException
*/
public int getAttributeSize() throws InvalidClassFileException {
@ -50,118 +55,238 @@ public class AnnotationsReader extends AttributeReader {
}
/**
* @return the offsets into the class file of the annotations of this attribute
* @throws InvalidClassFileException
* @throws UnimplementedException
*/
public int[] getAnnotationOffsets() throws InvalidClassFileException, UnimplementedException {
int[] result = new int[getAnnotationCount()];
int offset = beginOffset + 8;
for (int i = 0; i < result.length; i++) {
result[i] = offset;
offset += getAnnotationSize(offset);
}
return result;
}
/**
* @param begin offset in the constant pool
* @return the size, in bytes, of the annotation structure starting at a given offset
* @throws InvalidClassFileException
* @throws UnimplementedException
*/
private int getAnnotationSize(int begin) throws InvalidClassFileException, UnimplementedException {
int offset = begin + 2;
checkSize(offset, 2);
int numElementValuePairs = cr.getUShort(offset);
offset += 2;
for (int i = 0; i < numElementValuePairs; i++) {
offset += 2;
offset += getElementValueSize(offset);
}
return offset - begin;
}
/**
* temporary migration aid until I've implemented everything.
*
* @author sjfink
* 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
*/
public static class UnimplementedException extends Exception {
}
/**
* @return the type of the annotation stating at a given offset
* @throws InvalidClassFileException
*/
public String getAnnotationType(int offset) throws InvalidClassFileException {
private String getUtf8ConstantPoolValue(int offset) throws InvalidClassFileException {
checkSize(offset, 2);
int cpOffset = cr.getUShort(offset);
return cr.getCP().getCPUtf8(cpOffset);
}
public static final int INT_TYPE = 3;
public static final int STRING_TYPE = 1;
/*
* This method maps the internal type representation of annotation types to java types and stringifies all annotations.
/**
* Marker interface for possible element values in an annotation attribute.
*
* @see AnnotationsReader#readElementValueAndSize(int)
*
*/
private String getFromConstantPool(int offset) {
byte type = cr.getCP().getItemType(cr.getByte(offset));
if (type == INT_TYPE) {
String res = "";
try {
res = "" + cr.getCP().getCPInt(cr.getByte(offset));
} catch (InvalidClassFileException e) {
}
return res;
}
if (type == STRING_TYPE) {
String res = "";
try {
res = cr.getCP().getCPUtf8(cr.getByte(offset));
} catch (Exception e) {
}
return res;
}
return "";
public static interface ElementValue {
}
/**
* This method returns all the annotations as map key->stringified value starting at the index begin in the class file.
* @see AnnotationsReader#readElementValueAndSize(int)
*
*
* @param begin
* @return HashMap<String, String>
*/
public HashMap<String, String> getAnnotationValues(int begin) {
HashMap<String, String> res = new HashMap<String, String>();
int offset = begin + 2;
public static class ConstantElementValue implements ElementValue {
int numElementValuePairs = cr.getUShort(offset);
public final Object val;
offset += 3;
public ConstantElementValue(Object val) {
this.val = val;
}
@Override
public String toString() {
return val.toString();
}
}
/**
* @see AnnotationsReader#readElementValueAndSize(int)
*/
public static class EnumElementValue implements ElementValue {
public final String enumType;
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 + "]";
}
}
/**
* @see AnnotationsReader#readElementValueAndSize(int)
*/
public static class ArrayElementValue implements ElementValue {
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>
* 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 res1 = getFromConstantPool(offset);
offset += 3;
String res2 = getFromConstantPool(offset);
String elementName = getUtf8ConstantPoolValue(offset);
offset += 2;
res.put(res1, res2);
Pair<ElementValue, Integer> elementValAndSize = readElementValueAndSize(offset);
offset += elementValAndSize.snd;
size += elementValAndSize.snd + 2;
elementName2Val.put(elementName, elementValAndSize.fst);
}
return res;
return Pair.make(new AnnotationAttribute(type, elementName2Val), size);
}
/**
* @return the size, in bytes, of the element-value structure starting at a given offset
* 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 spec section 4.7.16 for details.
*/
private int getElementValueSize(int begin) {
return 3; // this is correct for any primitive type annotations.
// TODO: Integrate array annotations
public static class AnnotationAttribute implements ElementValue {
public final String type;
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;
}
}
}