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:
parent
c40917c3da
commit
7630cd79c9
|
@ -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() {}
|
||||||
|
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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");
|
||||||
|
}
|
|
@ -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 "";
|
||||||
|
}
|
|
@ -4,10 +4,14 @@ import java.io.IOException;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
|
||||||
|
import org.junit.AfterClass;
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
|
import org.junit.BeforeClass;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import com.ibm.wala.classLoader.IClass;
|
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.classLoader.ShrikeClass;
|
||||||
import com.ibm.wala.core.tests.callGraph.CallGraphTestUtil;
|
import com.ibm.wala.core.tests.callGraph.CallGraphTestUtil;
|
||||||
import com.ibm.wala.core.tests.util.TestConstants;
|
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.callgraph.AnalysisScope;
|
||||||
import com.ibm.wala.ipa.cha.ClassHierarchy;
|
import com.ibm.wala.ipa.cha.ClassHierarchy;
|
||||||
import com.ibm.wala.ipa.cha.ClassHierarchyException;
|
import com.ibm.wala.ipa.cha.ClassHierarchyException;
|
||||||
|
import com.ibm.wala.ipa.cha.IClassHierarchy;
|
||||||
import com.ibm.wala.shrikeCT.InvalidClassFileException;
|
import com.ibm.wala.shrikeCT.InvalidClassFileException;
|
||||||
import com.ibm.wala.types.ClassLoaderReference;
|
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.TypeReference;
|
||||||
import com.ibm.wala.types.annotations.Annotation;
|
import com.ibm.wala.types.annotations.Annotation;
|
||||||
import com.ibm.wala.util.collections.HashSetFactory;
|
import com.ibm.wala.util.collections.HashSetFactory;
|
||||||
|
@ -29,39 +36,59 @@ public class AnnotationTest extends WalaTestCase {
|
||||||
justThisTest(AnnotationTest.class);
|
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");
|
TypeReference typeUnderTest = TypeReference.findOrCreate(ClassLoaderReference.Application, "Lannotations/AnnotatedClass1");
|
||||||
|
|
||||||
Collection<Annotation> expectedRuntimeInvisibleAnnotations = HashSetFactory.make();
|
Collection<Annotation> expectedRuntimeInvisibleAnnotations = HashSetFactory.make();
|
||||||
expectedRuntimeInvisibleAnnotations.add(Annotation.make(TypeReference.findOrCreate(ClassLoaderReference.Application, "Lannotations/RuntimeInvisableAnnotation")));
|
expectedRuntimeInvisibleAnnotations.add(Annotation.make(TypeReference.findOrCreate(ClassLoaderReference.Application,
|
||||||
expectedRuntimeInvisibleAnnotations.add(Annotation.make(TypeReference.findOrCreate(ClassLoaderReference.Application, "Lannotations/DefaultVisableAnnotation")));
|
"Lannotations/RuntimeInvisableAnnotation")));
|
||||||
|
expectedRuntimeInvisibleAnnotations.add(Annotation.make(TypeReference.findOrCreate(ClassLoaderReference.Application,
|
||||||
|
"Lannotations/DefaultVisableAnnotation")));
|
||||||
|
|
||||||
Collection<Annotation> expectedRuntimeVisibleAnnotations = HashSetFactory.make();
|
Collection<Annotation> expectedRuntimeVisibleAnnotations = HashSetFactory.make();
|
||||||
expectedRuntimeVisibleAnnotations.add(Annotation.make(TypeReference.findOrCreate(ClassLoaderReference.Application, "Lannotations/RuntimeVisableAnnotation")));
|
expectedRuntimeVisibleAnnotations.add(Annotation.make(TypeReference.findOrCreate(ClassLoaderReference.Application,
|
||||||
|
"Lannotations/RuntimeVisableAnnotation")));
|
||||||
|
|
||||||
testClassAnnontations(typeUnderTest, expectedRuntimeInvisibleAnnotations, expectedRuntimeVisibleAnnotations);
|
testClassAnnotations(typeUnderTest, expectedRuntimeInvisibleAnnotations, expectedRuntimeVisibleAnnotations);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test public void testClassAnnotations2() throws Exception {
|
@Test
|
||||||
|
public void testClassAnnotations2() throws Exception {
|
||||||
TypeReference typeUnderTest = TypeReference.findOrCreate(ClassLoaderReference.Application, "Lannotations/AnnotatedClass2");
|
TypeReference typeUnderTest = TypeReference.findOrCreate(ClassLoaderReference.Application, "Lannotations/AnnotatedClass2");
|
||||||
|
|
||||||
Collection<Annotation> expectedRuntimeInvisibleAnnotations = HashSetFactory.make();
|
Collection<Annotation> expectedRuntimeInvisibleAnnotations = HashSetFactory.make();
|
||||||
expectedRuntimeInvisibleAnnotations.add(Annotation.make(TypeReference.findOrCreate(ClassLoaderReference.Application, "Lannotations/RuntimeInvisableAnnotation")));
|
expectedRuntimeInvisibleAnnotations.add(Annotation.make(TypeReference.findOrCreate(ClassLoaderReference.Application,
|
||||||
expectedRuntimeInvisibleAnnotations.add(Annotation.make(TypeReference.findOrCreate(ClassLoaderReference.Application, "Lannotations/RuntimeInvisableAnnotation2")));
|
"Lannotations/RuntimeInvisableAnnotation")));
|
||||||
|
expectedRuntimeInvisibleAnnotations.add(Annotation.make(TypeReference.findOrCreate(ClassLoaderReference.Application,
|
||||||
|
"Lannotations/RuntimeInvisableAnnotation2")));
|
||||||
|
|
||||||
Collection<Annotation> expectedRuntimeVisibleAnnotations = HashSetFactory.make();
|
Collection<Annotation> expectedRuntimeVisibleAnnotations = HashSetFactory.make();
|
||||||
expectedRuntimeVisibleAnnotations.add(Annotation.make(TypeReference.findOrCreate(ClassLoaderReference.Application, "Lannotations/RuntimeVisableAnnotation")));
|
expectedRuntimeVisibleAnnotations.add(Annotation.make(TypeReference.findOrCreate(ClassLoaderReference.Application,
|
||||||
expectedRuntimeVisibleAnnotations.add(Annotation.make(TypeReference.findOrCreate(ClassLoaderReference.Application, "Lannotations/RuntimeVisableAnnotation2")));
|
"Lannotations/RuntimeVisableAnnotation")));
|
||||||
|
expectedRuntimeVisibleAnnotations.add(Annotation.make(TypeReference.findOrCreate(ClassLoaderReference.Application,
|
||||||
|
"Lannotations/RuntimeVisableAnnotation2")));
|
||||||
|
|
||||||
testClassAnnontations(typeUnderTest, expectedRuntimeInvisibleAnnotations, expectedRuntimeVisibleAnnotations);
|
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,
|
Collection<Annotation> expectedRuntimeVisibleAnnotations) throws IOException, ClassHierarchyException,
|
||||||
InvalidClassFileException, com.ibm.wala.shrikeCT.AnnotationsReader.UnimplementedException {
|
InvalidClassFileException {
|
||||||
AnalysisScope scope = AnalysisScopeReader.readJavaScope(TestConstants.WALA_TESTDATA, FileProvider.getFile(CallGraphTestUtil.REGRESSION_EXCLUSIONS), getClass().getClassLoader());
|
|
||||||
ClassHierarchy cha = ClassHierarchy.make(scope);
|
|
||||||
|
|
||||||
IClass classUnderTest = cha.lookupClass(typeUnderTest);
|
IClass classUnderTest = cha.lookupClass(typeUnderTest);
|
||||||
Assert.assertNotNull(typeUnderTest.toString() + " not found", classUnderTest);
|
Assert.assertNotNull(typeUnderTest.toString() + " not found", classUnderTest);
|
||||||
Assert.assertTrue(classUnderTest instanceof ShrikeClass);
|
Assert.assertTrue(classUnderTest instanceof ShrikeClass);
|
||||||
|
@ -74,23 +101,45 @@ public class AnnotationTest extends WalaTestCase {
|
||||||
assertEqualCollections(expectedRuntimeVisibleAnnotations, runtimeVisibleAnnotations);
|
assertEqualCollections(expectedRuntimeVisibleAnnotations, runtimeVisibleAnnotations);
|
||||||
}
|
}
|
||||||
|
|
||||||
private <T> void assertEqualCollections(Collection<T> expected,
|
private <T> void assertEqualCollections(Collection<T> expected, Collection<T> actual) {
|
||||||
Collection<T> actual) {
|
if (expected == null) {
|
||||||
if (expected == null){
|
|
||||||
expected = Collections.emptySet();
|
expected = Collections.emptySet();
|
||||||
}
|
}
|
||||||
if (actual == null){
|
if (actual == null) {
|
||||||
actual = Collections.emptySet();
|
actual = Collections.emptySet();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (expected.size() != actual.size()){
|
if (expected.size() != actual.size()) {
|
||||||
Assert.assertTrue("expected=" + expected + " actual=" + actual, false);
|
Assert.assertTrue("expected=" + expected + " actual=" + actual, false);
|
||||||
}
|
}
|
||||||
for (T a : expected){
|
for (T a : expected) {
|
||||||
Assert.assertTrue ("missing " + a.toString(), actual.contains(a));
|
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());
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,15 +11,12 @@
|
||||||
package com.ibm.wala.classLoader;
|
package com.ibm.wala.classLoader;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.HashMap;
|
|
||||||
|
|
||||||
import com.ibm.wala.ipa.cha.IClassHierarchy;
|
import com.ibm.wala.ipa.cha.IClassHierarchy;
|
||||||
import com.ibm.wala.shrikeBT.Decoder;
|
import com.ibm.wala.shrikeBT.Decoder;
|
||||||
import com.ibm.wala.shrikeBT.IndirectionData;
|
import com.ibm.wala.shrikeBT.IndirectionData;
|
||||||
import com.ibm.wala.shrikeBT.shrikeCT.CTDecoder;
|
import com.ibm.wala.shrikeBT.shrikeCT.CTDecoder;
|
||||||
import com.ibm.wala.shrikeCT.AnnotationsReader;
|
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;
|
||||||
import com.ibm.wala.shrikeCT.ClassReader.AttrIterator;
|
import com.ibm.wala.shrikeCT.ClassReader.AttrIterator;
|
||||||
import com.ibm.wala.shrikeCT.CodeReader;
|
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.TypeReference;
|
||||||
import com.ibm.wala.types.annotations.Annotation;
|
import com.ibm.wala.types.annotations.Annotation;
|
||||||
import com.ibm.wala.types.generics.MethodTypeSignature;
|
import com.ibm.wala.types.generics.MethodTypeSignature;
|
||||||
import com.ibm.wala.util.collections.HashSetFactory;
|
|
||||||
import com.ibm.wala.util.debug.Assertions;
|
import com.ibm.wala.util.debug.Assertions;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -276,7 +272,7 @@ public final class ShrikeCTMethod extends ShrikeBTMethod implements IBytecodeMet
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private AnnotationsReader getAnnotationsReader(boolean runtimeInvisable) {
|
private AnnotationsReader getAnnotationsReader(boolean runtimeInvisible) {
|
||||||
ClassReader.AttrIterator iter = new AttrIterator();
|
ClassReader.AttrIterator iter = new AttrIterator();
|
||||||
getClassReader().initMethodAttributeIterator(shrikeMethodIndex, iter);
|
getClassReader().initMethodAttributeIterator(shrikeMethodIndex, iter);
|
||||||
|
|
||||||
|
@ -284,7 +280,7 @@ public final class ShrikeCTMethod extends ShrikeBTMethod implements IBytecodeMet
|
||||||
AnnotationsReader result = null;
|
AnnotationsReader result = null;
|
||||||
try {
|
try {
|
||||||
for (; iter.isValid(); iter.advance()) {
|
for (; iter.isValid(); iter.advance()) {
|
||||||
if (runtimeInvisable) {
|
if (runtimeInvisible) {
|
||||||
if (iter.getName().equals(RuntimeInvisibleAnnotationsReader.attrName)) {
|
if (iter.getName().equals(RuntimeInvisibleAnnotationsReader.attrName)) {
|
||||||
result = new RuntimeInvisibleAnnotationsReader(iter);
|
result = new RuntimeInvisibleAnnotationsReader(iter);
|
||||||
break;
|
break;
|
||||||
|
@ -342,56 +338,22 @@ public final class ShrikeCTMethod extends ShrikeBTMethod implements IBytecodeMet
|
||||||
/**
|
/**
|
||||||
* read the runtime-invisible annotations from the class file
|
* 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);
|
return getAnnotations(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* read the runtime-visible annotations from the class file
|
* 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);
|
return getAnnotations(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Collection<Annotation> getAnnotations(boolean runtimeInvisable) throws InvalidClassFileException, UnimplementedException {
|
public Collection<Annotation> getAnnotations(boolean runtimeInvisible) throws InvalidClassFileException {
|
||||||
AnnotationsReader r = getAnnotationsReader(runtimeInvisable);
|
AnnotationsReader r = getAnnotationsReader(runtimeInvisible);
|
||||||
if (r != null) {
|
return Annotation.getAnnotationsFromReader(r, getDeclaringClass().getClassLoader().getReference());
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 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() {
|
private static final IndirectionData NO_INDIRECTIONS = new IndirectionData() {
|
||||||
|
|
||||||
|
|
|
@ -12,14 +12,12 @@ package com.ibm.wala.classLoader;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import com.ibm.wala.ipa.cha.IClassHierarchy;
|
import com.ibm.wala.ipa.cha.IClassHierarchy;
|
||||||
import com.ibm.wala.shrikeBT.Constants;
|
import com.ibm.wala.shrikeBT.Constants;
|
||||||
import com.ibm.wala.shrikeCT.AnnotationsReader;
|
import com.ibm.wala.shrikeCT.AnnotationsReader;
|
||||||
import com.ibm.wala.shrikeCT.AnnotationsReader.UnimplementedException;
|
|
||||||
import com.ibm.wala.shrikeCT.ClassConstants;
|
import com.ibm.wala.shrikeCT.ClassConstants;
|
||||||
import com.ibm.wala.shrikeCT.ClassReader;
|
import com.ibm.wala.shrikeCT.ClassReader;
|
||||||
import com.ibm.wala.shrikeCT.ClassReader.AttrIterator;
|
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.TypeReference;
|
||||||
import com.ibm.wala.types.annotations.Annotation;
|
import com.ibm.wala.types.annotations.Annotation;
|
||||||
import com.ibm.wala.types.generics.ClassSignature;
|
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.debug.Assertions;
|
||||||
import com.ibm.wala.util.shrike.ShrikeClassReaderHandle;
|
import com.ibm.wala.util.shrike.ShrikeClassReaderHandle;
|
||||||
import com.ibm.wala.util.strings.Atom;
|
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));
|
Atom name = Atom.findOrCreateUnicodeAtom(cr.getFieldName(i));
|
||||||
ImmutableByteArray b = ImmutableByteArray.make(cr.getFieldType(i));
|
ImmutableByteArray b = ImmutableByteArray.make(cr.getFieldType(i));
|
||||||
Collection<Annotation> annotations = null;
|
Collection<Annotation> annotations = null;
|
||||||
try {
|
|
||||||
annotations = getRuntimeInvisibleAnnotations(i);
|
annotations = getRuntimeInvisibleAnnotations(i);
|
||||||
annotations = annotations.isEmpty() ? null : annotations;
|
annotations = annotations.isEmpty() ? null : annotations;
|
||||||
} catch (UnimplementedException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
// keep going
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((accessFlags & ClassConstants.ACC_STATIC) == 0) {
|
if ((accessFlags & ClassConstants.ACC_STATIC) == 0) {
|
||||||
addFieldToList(instanceList, name, b, accessFlags, annotations);
|
addFieldToList(instanceList, name, b, accessFlags, annotations);
|
||||||
|
@ -226,29 +218,17 @@ public final class ShrikeClass extends JVMClass<IClassLoader> {
|
||||||
reader.clear();
|
reader.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Collection<Annotation> getRuntimeInvisibleAnnotations() throws InvalidClassFileException, UnimplementedException {
|
public Collection<Annotation> getRuntimeInvisibleAnnotations() throws InvalidClassFileException {
|
||||||
return getAnnotations(true);
|
return getAnnotations(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Collection<Annotation> getRuntimeVisibleAnnotations() throws InvalidClassFileException, UnimplementedException {
|
public Collection<Annotation> getRuntimeVisibleAnnotations() throws InvalidClassFileException {
|
||||||
return getAnnotations(false);
|
return getAnnotations(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Collection<Annotation> getAnnotations(boolean runtimeInvisable) throws InvalidClassFileException, UnimplementedException {
|
public Collection<Annotation> getAnnotations(boolean runtimeInvisible) throws InvalidClassFileException {
|
||||||
AnnotationsReader r = getAnnotationsReader(runtimeInvisable);
|
AnnotationsReader r = getAnnotationsReader(runtimeInvisible);
|
||||||
if (r != null) {
|
return Annotation.getAnnotationsFromReader(r, getClassLoader().getReference());
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private AnnotationsReader getAnnotationsReader(boolean runtimeInvisable) throws InvalidClassFileException {
|
private AnnotationsReader getAnnotationsReader(boolean runtimeInvisable) throws InvalidClassFileException {
|
||||||
|
@ -278,6 +258,7 @@ public final class ShrikeClass extends JVMClass<IClassLoader> {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private InnerClassesReader getInnerClassesReader() throws InvalidClassFileException {
|
private InnerClassesReader getInnerClassesReader() throws InvalidClassFileException {
|
||||||
ClassReader r = reader.get();
|
ClassReader r = reader.get();
|
||||||
ClassReader.AttrIterator attrs = new ClassReader.AttrIterator();
|
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
|
* read the runtime-invisible annotations from the class file
|
||||||
*/
|
*/
|
||||||
public Collection<Annotation> getRuntimeInvisibleAnnotations(int fieldIndex) throws InvalidClassFileException,
|
public Collection<Annotation> getRuntimeInvisibleAnnotations(int fieldIndex) throws InvalidClassFileException {
|
||||||
UnimplementedException {
|
|
||||||
RuntimeInvisibleAnnotationsReader r = getRuntimeInvisibleAnnotationsReader(fieldIndex);
|
RuntimeInvisibleAnnotationsReader r = getRuntimeInvisibleAnnotationsReader(fieldIndex);
|
||||||
if (r != null) {
|
return Annotation.getAnnotationsFromReader(r, getClassLoader().getReference());
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private SignatureReader getSignatureReader() throws InvalidClassFileException {
|
private SignatureReader getSignatureReader() throws InvalidClassFileException {
|
||||||
|
|
|
@ -11,41 +11,95 @@
|
||||||
package com.ibm.wala.types.annotations;
|
package com.ibm.wala.types.annotations;
|
||||||
|
|
||||||
import java.util.Arrays;
|
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.types.TypeReference;
|
||||||
|
import com.ibm.wala.util.collections.HashSetFactory;
|
||||||
import com.ibm.wala.util.collections.Pair;
|
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 {
|
public class Annotation {
|
||||||
|
|
||||||
private final TypeReference type;
|
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.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) {
|
public static Annotation makeUnnamedAndNamed(TypeReference t, Map<String, ElementValue> namedArguments, Pair<TypeReference,Object>[] unnamedArguments) {
|
||||||
return new Annotation(t, arguments);
|
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) {
|
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
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
StringBuffer sb = new StringBuffer("Annotation type " + type);
|
StringBuffer sb = new StringBuffer("Annotation type " + type);
|
||||||
if (arguments != null) {
|
if (unnamedArguments != null) {
|
||||||
sb.append("[");
|
sb.append("[");
|
||||||
for(Pair<TypeReference, Object> arg : arguments) {
|
for (Pair<TypeReference, Object> arg : unnamedArguments) {
|
||||||
sb.append(" " + arg.fst.getName().getClassName() + ":" + arg.snd);
|
sb.append(" " + arg.fst.getName().getClassName() + ":" + arg.snd);
|
||||||
}
|
}
|
||||||
sb.append(" ]");
|
sb.append(" ]");
|
||||||
}
|
}
|
||||||
|
if (!namedArguments.isEmpty()) {
|
||||||
|
sb.append(" " + namedArguments);
|
||||||
|
}
|
||||||
return sb.toString();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -53,7 +107,7 @@ public class Annotation {
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
final int prime = 31;
|
final int prime = 31;
|
||||||
int result = 1;
|
int result = 1;
|
||||||
result = prime * result + Arrays.hashCode(arguments);
|
result = prime * result + Arrays.hashCode(unnamedArguments);
|
||||||
result = prime * result + ((type == null) ? 0 : type.hashCode());
|
result = prime * result + ((type == null) ? 0 : type.hashCode());
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -67,7 +121,7 @@ public class Annotation {
|
||||||
if (getClass() != obj.getClass())
|
if (getClass() != obj.getClass())
|
||||||
return false;
|
return false;
|
||||||
Annotation other = (Annotation) obj;
|
Annotation other = (Annotation) obj;
|
||||||
if (!Arrays.equals(arguments, other.arguments))
|
if (!Arrays.equals(unnamedArguments, other.unnamedArguments))
|
||||||
return false;
|
return false;
|
||||||
if (type == null) {
|
if (type == null) {
|
||||||
if (other.type != null)
|
if (other.type != null)
|
||||||
|
@ -77,8 +131,12 @@ public class Annotation {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Pair<TypeReference, Object>[] getArguments() {
|
public Pair<TypeReference, Object>[] getUnnamedArguments() {
|
||||||
return arguments;
|
return unnamedArguments;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String,ElementValue> getNamedArguments() {
|
||||||
|
return namedArguments;
|
||||||
}
|
}
|
||||||
|
|
||||||
public TypeReference getType() {
|
public TypeReference getType() {
|
||||||
|
|
|
@ -18,7 +18,6 @@ import com.ibm.wala.classLoader.IField;
|
||||||
import com.ibm.wala.classLoader.IMethod;
|
import com.ibm.wala.classLoader.IMethod;
|
||||||
import com.ibm.wala.classLoader.ShrikeCTMethod;
|
import com.ibm.wala.classLoader.ShrikeCTMethod;
|
||||||
import com.ibm.wala.classLoader.ShrikeClass;
|
import com.ibm.wala.classLoader.ShrikeClass;
|
||||||
import com.ibm.wala.shrikeCT.AnnotationsReader.UnimplementedException;
|
|
||||||
import com.ibm.wala.shrikeCT.InvalidClassFileException;
|
import com.ibm.wala.shrikeCT.InvalidClassFileException;
|
||||||
import com.ibm.wala.types.TypeName;
|
import com.ibm.wala.types.TypeName;
|
||||||
import com.ibm.wala.util.debug.Assertions;
|
import com.ibm.wala.util.debug.Assertions;
|
||||||
|
@ -39,9 +38,6 @@ public class Annotations {
|
||||||
} catch (InvalidClassFileException e) {
|
} catch (InvalidClassFileException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
Assertions.UNREACHABLE();
|
Assertions.UNREACHABLE();
|
||||||
} catch (UnimplementedException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
Assertions.UNREACHABLE();
|
|
||||||
}
|
}
|
||||||
for (Annotation a : annotations) {
|
for (Annotation a : annotations) {
|
||||||
if (a.getType().getName().equals(type)) {
|
if (a.getType().getName().equals(type)) {
|
||||||
|
@ -63,9 +59,6 @@ public class Annotations {
|
||||||
} catch (InvalidClassFileException e) {
|
} catch (InvalidClassFileException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
Assertions.UNREACHABLE();
|
Assertions.UNREACHABLE();
|
||||||
} catch (UnimplementedException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
Assertions.UNREACHABLE();
|
|
||||||
}
|
}
|
||||||
for (Annotation a : annotations) {
|
for (Annotation a : annotations) {
|
||||||
if (a.getType().getName().equals(type)) {
|
if (a.getType().getName().equals(type)) {
|
||||||
|
|
|
@ -16,3 +16,4 @@ Export-Package: com.ibm.wala.shrike.bench,
|
||||||
com.ibm.wala.shrikeBT.tools,
|
com.ibm.wala.shrikeBT.tools,
|
||||||
com.ibm.wala.shrikeCT
|
com.ibm.wala.shrikeCT
|
||||||
Bundle-RequiredExecutionEnvironment: J2SE-1.5
|
Bundle-RequiredExecutionEnvironment: J2SE-1.5
|
||||||
|
Require-Bundle: com.ibm.wala.util
|
||||||
|
|
|
@ -23,6 +23,7 @@ import com.ibm.wala.shrikeBT.shrikeCT.CTDecoder;
|
||||||
import com.ibm.wala.shrikeBT.shrikeCT.ClassInstrumenter;
|
import com.ibm.wala.shrikeBT.shrikeCT.ClassInstrumenter;
|
||||||
import com.ibm.wala.shrikeBT.shrikeCT.OfflineInstrumenter;
|
import com.ibm.wala.shrikeBT.shrikeCT.OfflineInstrumenter;
|
||||||
import com.ibm.wala.shrikeCT.AnnotationsReader;
|
import com.ibm.wala.shrikeCT.AnnotationsReader;
|
||||||
|
import com.ibm.wala.shrikeCT.AnnotationsReader.AnnotationAttribute;
|
||||||
import com.ibm.wala.shrikeCT.ClassConstants;
|
import com.ibm.wala.shrikeCT.ClassConstants;
|
||||||
import com.ibm.wala.shrikeCT.ClassReader;
|
import com.ibm.wala.shrikeCT.ClassReader;
|
||||||
import com.ibm.wala.shrikeCT.CodeReader;
|
import com.ibm.wala.shrikeCT.CodeReader;
|
||||||
|
@ -286,20 +287,8 @@ public class ClassPrinter {
|
||||||
|
|
||||||
private void printAnnotations(ClassReader cr, ClassReader.AttrIterator attrs, AnnotationsReader r)
|
private void printAnnotations(ClassReader cr, ClassReader.AttrIterator attrs, AnnotationsReader r)
|
||||||
throws InvalidClassFileException {
|
throws InvalidClassFileException {
|
||||||
try {
|
for (AnnotationAttribute annot : r.getAllAnnotations()) {
|
||||||
int[] annotations = r.getAnnotationOffsets();
|
w.write(" Annotation type: " + annot.type + "\n");
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,10 +10,14 @@
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
package com.ibm.wala.shrikeCT;
|
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
|
* @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
|
* @throws InvalidClassFileException
|
||||||
*/
|
*/
|
||||||
public int getAttributeSize() 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
|
* get the Utf8 constant pool value, where the constant pool offset is given
|
||||||
* @throws InvalidClassFileException
|
* in the class
|
||||||
* @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
|
|
||||||
*
|
*
|
||||||
|
* @param offset
|
||||||
|
* offset in the class file at which the constant pool offset is
|
||||||
|
* given
|
||||||
*/
|
*/
|
||||||
public static class UnimplementedException extends Exception {
|
private String getUtf8ConstantPoolValue(int offset) throws InvalidClassFileException {
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the type of the annotation stating at a given offset
|
|
||||||
* @throws InvalidClassFileException
|
|
||||||
*/
|
|
||||||
public String getAnnotationType(int offset) throws InvalidClassFileException {
|
|
||||||
checkSize(offset, 2);
|
checkSize(offset, 2);
|
||||||
int cpOffset = cr.getUShort(offset);
|
int cpOffset = cr.getUShort(offset);
|
||||||
return cr.getCP().getCPUtf8(cpOffset);
|
return cr.getCP().getCPUtf8(cpOffset);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final int INT_TYPE = 3;
|
/**
|
||||||
|
* Marker interface for possible element values in an annotation attribute.
|
||||||
public static final int STRING_TYPE = 1;
|
*
|
||||||
|
* @see AnnotationsReader#readElementValueAndSize(int)
|
||||||
/*
|
*
|
||||||
* This method maps the internal type representation of annotation types to java types and stringifies all annotations.
|
|
||||||
*/
|
*/
|
||||||
private String getFromConstantPool(int offset) {
|
public static interface ElementValue {
|
||||||
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 "";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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) {
|
public static class ConstantElementValue implements ElementValue {
|
||||||
HashMap<String, String> res = new HashMap<String, String>();
|
|
||||||
int offset = begin + 2;
|
|
||||||
|
|
||||||
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++) {
|
for (int i = 0; i < numElementValuePairs; i++) {
|
||||||
String res1 = getFromConstantPool(offset);
|
String elementName = getUtf8ConstantPoolValue(offset);
|
||||||
offset += 3;
|
|
||||||
String res2 = getFromConstantPool(offset);
|
|
||||||
offset += 2;
|
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) {
|
public static class AnnotationAttribute implements ElementValue {
|
||||||
return 3; // this is correct for any primitive type annotations.
|
|
||||||
// TODO: Integrate array annotations
|
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
Loading…
Reference in New Issue