From de264840982ffb11b64cf7635740d455c8627d89 Mon Sep 17 00:00:00 2001 From: Julian Dolby Date: Wed, 4 Mar 2015 13:49:58 -0500 Subject: [PATCH] java annotations support for dalvik --- .../wala/core/tests/ir/AnnotationTest.java | 108 ++++++----- .../core/tests/ir/JVMLAnnotationTest.java | 34 ++++ .../ibm/wala/classLoader/BytecodeClass.java | 3 + .../ibm/wala/classLoader/IBytecodeMethod.java | 5 + .../src/com/ibm/wala/classLoader/IClass.java | 2 +- .../ibm/wala/classLoader/ShrikeCTMethod.java | 1 + .../com/ibm/wala/classLoader/ShrikeClass.java | 1 + .../wala/types/annotations/Annotation.java | 3 +- .../ibm/wala/dalvik/test/DalvikTestBase.java | 120 +++++++++++++ .../callGraph/DalvikCallGraphTestBase.java | 103 +---------- .../dalvik/test/ir/DalvikAnnotationsTest.java | 40 +++++ .../dalvik/classLoader/DexFileModule.java | 6 +- .../wala/dalvik/classLoader/DexIClass.java | 90 +++++++++- .../wala/dalvik/classLoader/DexIField.java | 19 +- .../wala/dalvik/classLoader/DexIMethod.java | 25 ++- .../ibm/wala/dalvik/classLoader/DexUtil.java | 169 ++++++++++++++++++ 16 files changed, 547 insertions(+), 182 deletions(-) create mode 100644 com.ibm.wala.core.tests/src/com/ibm/wala/core/tests/ir/JVMLAnnotationTest.java create mode 100644 com.ibm.wala.dalvik.test/source/com/ibm/wala/dalvik/test/DalvikTestBase.java create mode 100644 com.ibm.wala.dalvik.test/source/com/ibm/wala/dalvik/test/ir/DalvikAnnotationsTest.java create mode 100644 com.ibm.wala.dalvik/src/com/ibm/wala/dalvik/classLoader/DexUtil.java diff --git a/com.ibm.wala.core.tests/src/com/ibm/wala/core/tests/ir/AnnotationTest.java b/com.ibm.wala.core.tests/src/com/ibm/wala/core/tests/ir/AnnotationTest.java index e259cfa49..1c8ef8d37 100644 --- a/com.ibm.wala.core.tests/src/com/ibm/wala/core/tests/ir/AnnotationTest.java +++ b/com.ibm.wala.core.tests/src/com/ibm/wala/core/tests/ir/AnnotationTest.java @@ -13,22 +13,17 @@ package com.ibm.wala.core.tests.ir; import java.io.IOException; import java.util.Collection; import java.util.Collections; +import java.util.Set; -import org.junit.AfterClass; import org.junit.Assert; -import org.junit.BeforeClass; import org.junit.Test; +import com.ibm.wala.classLoader.BytecodeClass; +import com.ibm.wala.classLoader.IBytecodeMethod; import com.ibm.wala.classLoader.IClass; 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.core.tests.callGraph.CallGraphTestUtil; -import com.ibm.wala.core.tests.util.TestConstants; 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; @@ -39,30 +34,13 @@ 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; -import com.ibm.wala.util.config.AnalysisScopeReader; -import com.ibm.wala.util.io.FileProvider; +import com.ibm.wala.util.collections.Pair; import com.ibm.wala.util.strings.Atom; -public class AnnotationTest extends WalaTestCase { +public abstract class AnnotationTest extends WalaTestCase { - public static void main(String[] args) { - justThisTest(AnnotationTest.class); - } - - private static IClassHierarchy cha; + protected static IClassHierarchy cha; - @BeforeClass - public static void before() throws IOException, ClassHierarchyException { - AnalysisScope scope = AnalysisScopeReader.readJavaScope(TestConstants.WALA_TESTDATA, - (new 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"); @@ -104,13 +82,13 @@ public class AnnotationTest extends WalaTestCase { InvalidClassFileException { IClass classUnderTest = cha.lookupClass(typeUnderTest); Assert.assertNotNull(typeUnderTest.toString() + " not found", classUnderTest); - Assert.assertTrue(classUnderTest instanceof ShrikeClass); - ShrikeClass shrikeClassUnderTest = (ShrikeClass) classUnderTest; + Assert.assertTrue(classUnderTest instanceof BytecodeClass); + BytecodeClass bcClassUnderTest = (BytecodeClass) classUnderTest; - Collection runtimeInvisibleAnnotations = shrikeClassUnderTest.getRuntimeInvisibleAnnotations(); + Collection runtimeInvisibleAnnotations = bcClassUnderTest.getAnnotations(true); assertEqualCollections(expectedRuntimeInvisibleAnnotations, runtimeInvisibleAnnotations); - Collection runtimeVisibleAnnotations = shrikeClassUnderTest.getRuntimeVisibleAnnotations(); + Collection runtimeVisibleAnnotations = bcClassUnderTest.getAnnotations(false); assertEqualCollections(expectedRuntimeVisibleAnnotations, runtimeVisibleAnnotations); } @@ -130,13 +108,14 @@ public class AnnotationTest extends WalaTestCase { } } + @SuppressWarnings("unchecked") @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; + BytecodeClass shrikeClass = (BytecodeClass) klass; Collection classAnnotations = shrikeClass.getAnnotations(true); Assert.assertEquals("[Annotation type {strParam=classStrParam}]", classAnnotations.toString()); @@ -145,16 +124,23 @@ public class AnnotationTest extends WalaTestCase { IMethod methodUnderTest = cha.resolveMethod(methodRefUnderTest); Assert.assertNotNull(methodRefUnderTest.toString() + " not found", methodUnderTest); - Assert.assertTrue(methodUnderTest instanceof ShrikeCTMethod); - ShrikeCTMethod shrikeCTMethodUnderTest = (ShrikeCTMethod) methodUnderTest; + Assert.assertTrue(methodUnderTest instanceof IBytecodeMethod); + IBytecodeMethod bcMethodUnderTest = (IBytecodeMethod) methodUnderTest; - Collection runtimeInvisibleAnnotations = shrikeCTMethodUnderTest.getAnnotations(true); - Assert - .assertEquals( - "[Annotation type {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()); - - } + Collection runtimeInvisibleAnnotations = bcMethodUnderTest.getAnnotations(true); + Assert.assertEquals(1, runtimeInvisibleAnnotations.size()); + + Annotation x = runtimeInvisibleAnnotations.iterator().next(); + Assert.assertEquals(TypeReference.findOrCreate(ClassLoaderReference.Application, "Lannotations/AnnotationWithParams"), x.getType()); + for(Pair n : new Pair[]{Pair.make("enumParam", "EnumElementValue [type=Lannotations/AnnotationEnum;, val=VAL1]"), + Pair.make("strArrParam", "ArrayElementValue [vals=[biz, boz]]"), + Pair.make("annotParam", "AnnotationElementValue [type=Lannotations/AnnotationWithSingleParam;, elementValues={value=sdfevs}]"), + Pair.make("strParam", "sdfsevs"), + Pair.make("intParam", "25"), + Pair.make("klassParam", "Ljava/lang/Integer;")}) { + Assert.assertEquals(n.snd, x.getNamedArguments().get(n.fst).toString()); + } + } @Test public void testClassAnnotations4() throws Exception { @@ -179,35 +165,47 @@ public class AnnotationTest extends WalaTestCase { TypeReference typeRef = TypeReference.findOrCreate(ClassLoaderReference.Application, "Lannotations/ParameterAnnotations1"); checkParameterAnnots(typeRef, "foo(Ljava/lang/String;)V", - "[Annotation type ]"); + new String[]{"Annotation type "}); checkParameterAnnots( typeRef, "bar(Ljava/lang/Integer;)V", - "[Annotation type {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;}]"); + new String[]{"Annotation type {annotParam=AnnotationElementValue [type=Lannotations/AnnotationWithSingleParam;, elementValues={value=sdfevs}], enumParam=EnumElementValue [type=Lannotations/AnnotationEnum;, val=VAL1], intParam=25, klassParam=Ljava/lang/Integer;, strArrParam=ArrayElementValue [vals=[biz, boz]], strParam=sdfsevs}"}); checkParameterAnnots(typeRef, "foo2(Ljava/lang/String;Ljava/lang/Integer;)V", - "[Annotation type ]", - "[Annotation type ]"); + new String[]{"Annotation type "}, + new String[]{"Annotation type "}); checkParameterAnnots(typeRef, "foo3(Ljava/lang/String;Ljava/lang/Integer;)V", - "[Annotation type ]", - "[Annotation type ]"); + new String[]{"Annotation type "}, + new String[]{"Annotation type "}); checkParameterAnnots(typeRef, "foo4(Ljava/lang/String;Ljava/lang/Integer;)V", - "[Annotation type , Annotation type ]", - "[]"); + new String[]{"Annotation type ", "Annotation type "}, + new String[0]); } - protected void checkParameterAnnots(TypeReference typeRef, String selector, String... expected) { + protected void checkParameterAnnots(TypeReference typeRef, String selector, String[]... expected) { MethodReference methodRefUnderTest = MethodReference.findOrCreate(typeRef, Selector.make(selector)); IMethod methodUnderTest = cha.resolveMethod(methodRefUnderTest); Assert.assertNotNull(methodRefUnderTest.toString() + " not found", methodUnderTest); - Assert.assertTrue(methodUnderTest instanceof ShrikeCTMethod); - ShrikeCTMethod shrikeCTMethodUnderTest = (ShrikeCTMethod) methodUnderTest; + Assert.assertTrue(methodUnderTest instanceof IBytecodeMethod); + IBytecodeMethod IBytecodeMethodUnderTest = (IBytecodeMethod) methodUnderTest; - Collection[] parameterAnnotations = shrikeCTMethodUnderTest.getParameterAnnotations(); + Collection[] parameterAnnotations = IBytecodeMethodUnderTest.getParameterAnnotations(); Assert.assertEquals(expected.length, parameterAnnotations.length); for (int i = 0; i < expected.length; i++) { - Assert.assertEquals(expected[i], parameterAnnotations[i].toString()); + Set e = HashSetFactory.make(); + for(String s : expected[i]) { + e.add(s); + } + + Set a = HashSetFactory.make(); + if (parameterAnnotations[i] != null) { + for(Annotation x : parameterAnnotations[i]) { + a.add(x.toString()); + } + } + + Assert.assertEquals(e, a); } } diff --git a/com.ibm.wala.core.tests/src/com/ibm/wala/core/tests/ir/JVMLAnnotationTest.java b/com.ibm.wala.core.tests/src/com/ibm/wala/core/tests/ir/JVMLAnnotationTest.java new file mode 100644 index 000000000..42bb4e7b9 --- /dev/null +++ b/com.ibm.wala.core.tests/src/com/ibm/wala/core/tests/ir/JVMLAnnotationTest.java @@ -0,0 +1,34 @@ +package com.ibm.wala.core.tests.ir; + +import java.io.IOException; + +import org.junit.AfterClass; +import org.junit.BeforeClass; + +import com.ibm.wala.core.tests.callGraph.CallGraphTestUtil; +import com.ibm.wala.core.tests.util.TestConstants; +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.util.config.AnalysisScopeReader; +import com.ibm.wala.util.io.FileProvider; + +public class JVMLAnnotationTest extends AnnotationTest { + + public static void main(String[] args) { + justThisTest(JVMLAnnotationTest.class); + } + + @BeforeClass + public static void before() throws IOException, ClassHierarchyException { + AnalysisScope scope = AnalysisScopeReader.readJavaScope(TestConstants.WALA_TESTDATA, + (new FileProvider()).getFile(CallGraphTestUtil.REGRESSION_EXCLUSIONS), AnnotationTest.class.getClassLoader()); + cha = ClassHierarchy.make(scope); + } + + @AfterClass + public static void after() { + cha = null; + } + +} diff --git a/com.ibm.wala.core/src/com/ibm/wala/classLoader/BytecodeClass.java b/com.ibm.wala.core/src/com/ibm/wala/classLoader/BytecodeClass.java index 1df680a09..89b2098dd 100644 --- a/com.ibm.wala.core/src/com/ibm/wala/classLoader/BytecodeClass.java +++ b/com.ibm.wala.core/src/com/ibm/wala/classLoader/BytecodeClass.java @@ -590,4 +590,7 @@ public abstract class BytecodeClass implements IClass { } } } + + public abstract Collection getAnnotations(boolean runtimeVisible) throws InvalidClassFileException; + } diff --git a/com.ibm.wala.core/src/com/ibm/wala/classLoader/IBytecodeMethod.java b/com.ibm.wala.core/src/com/ibm/wala/classLoader/IBytecodeMethod.java index e719208c8..d30f7d725 100644 --- a/com.ibm.wala.core/src/com/ibm/wala/classLoader/IBytecodeMethod.java +++ b/com.ibm.wala.core/src/com/ibm/wala/classLoader/IBytecodeMethod.java @@ -16,6 +16,7 @@ import com.ibm.wala.shrikeBT.ExceptionHandler; import com.ibm.wala.shrikeBT.IInstruction; import com.ibm.wala.shrikeBT.IndirectionData; import com.ibm.wala.shrikeCT.InvalidClassFileException; +import com.ibm.wala.types.annotations.Annotation; /** * A method which originated in bytecode, decoded by Shrike @@ -47,4 +48,8 @@ public interface IBytecodeMethod extends IMethod { */ IndirectionData getIndirectionData(); + Collection[] getParameterAnnotations(); + + Collection getAnnotations(boolean runtimeVisible) throws InvalidClassFileException; + } diff --git a/com.ibm.wala.core/src/com/ibm/wala/classLoader/IClass.java b/com.ibm.wala.core/src/com/ibm/wala/classLoader/IClass.java index 8f6c4d4b8..705aed968 100644 --- a/com.ibm.wala.core/src/com/ibm/wala/classLoader/IClass.java +++ b/com.ibm.wala.core/src/com/ibm/wala/classLoader/IClass.java @@ -183,5 +183,5 @@ public interface IClass extends IClassHierarchyDweller { * get annotations, if any */ Collection getAnnotations(); - + } diff --git a/com.ibm.wala.core/src/com/ibm/wala/classLoader/ShrikeCTMethod.java b/com.ibm.wala.core/src/com/ibm/wala/classLoader/ShrikeCTMethod.java index a11ff4162..d9f822b53 100644 --- a/com.ibm.wala.core/src/com/ibm/wala/classLoader/ShrikeCTMethod.java +++ b/com.ibm.wala.core/src/com/ibm/wala/classLoader/ShrikeCTMethod.java @@ -446,6 +446,7 @@ public final class ShrikeCTMethod extends ShrikeBTMethod implements IBytecodeMet return getAnnotations(false); } + @Override public Collection getAnnotations(boolean runtimeInvisible) throws InvalidClassFileException { AnnotationsReader r = getAnnotationsReader(runtimeInvisible ? AnnotationType.RuntimeInvisibleAnnotations : AnnotationType.RuntimeVisibleAnnotations); diff --git a/com.ibm.wala.core/src/com/ibm/wala/classLoader/ShrikeClass.java b/com.ibm.wala.core/src/com/ibm/wala/classLoader/ShrikeClass.java index 80e246ead..9034885f8 100644 --- a/com.ibm.wala.core/src/com/ibm/wala/classLoader/ShrikeClass.java +++ b/com.ibm.wala.core/src/com/ibm/wala/classLoader/ShrikeClass.java @@ -244,6 +244,7 @@ public final class ShrikeClass extends JVMClass { return result; } + @Override public Collection getAnnotations(boolean runtimeInvisible) throws InvalidClassFileException { AnnotationsReader r = getAnnotationsReader(runtimeInvisible); return Annotation.getAnnotationsFromReader(r, getClassLoader().getReference()); diff --git a/com.ibm.wala.core/src/com/ibm/wala/types/annotations/Annotation.java b/com.ibm.wala.core/src/com/ibm/wala/types/annotations/Annotation.java index 2080b3cf8..11c7f8b8d 100644 --- a/com.ibm.wala.core/src/com/ibm/wala/types/annotations/Annotation.java +++ b/com.ibm.wala.core/src/com/ibm/wala/types/annotations/Annotation.java @@ -14,6 +14,7 @@ import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Map; +import java.util.TreeMap; import com.ibm.wala.shrikeCT.AnnotationsReader; import com.ibm.wala.shrikeCT.AnnotationsReader.AnnotationAttribute; @@ -121,7 +122,7 @@ public class Annotation { sb.append(" ]"); } if (!namedArguments.isEmpty()) { - sb.append(" " + namedArguments); + sb.append(" " + new TreeMap(namedArguments)); } return sb.toString(); } diff --git a/com.ibm.wala.dalvik.test/source/com/ibm/wala/dalvik/test/DalvikTestBase.java b/com.ibm.wala.dalvik.test/source/com/ibm/wala/dalvik/test/DalvikTestBase.java new file mode 100644 index 000000000..85bc51273 --- /dev/null +++ b/com.ibm.wala.dalvik.test/source/com/ibm/wala/dalvik/test/DalvikTestBase.java @@ -0,0 +1,120 @@ +package com.ibm.wala.dalvik.test; + +import static com.ibm.wala.properties.WalaProperties.ANDROID_RT_JAR; + +import java.io.File; +import java.io.FilenameFilter; +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; +import java.util.ArrayList; +import java.util.List; +import java.util.Properties; + +import com.ibm.wala.classLoader.JarFileModule; +import com.ibm.wala.classLoader.Module; +import com.ibm.wala.classLoader.NestedJarFileModule; +import com.ibm.wala.core.tests.callGraph.CallGraphTestUtil; +import com.ibm.wala.core.tests.shrike.DynamicCallGraphTestBase; +import com.ibm.wala.dalvik.test.callGraph.DalvikCallGraphTestBase; +import com.ibm.wala.dalvik.util.AndroidAnalysisScope; +import com.ibm.wala.ipa.callgraph.AnalysisScope; +import com.ibm.wala.properties.WalaProperties; +import com.ibm.wala.util.WalaException; +import com.ibm.wala.util.io.TemporaryFile; + +public abstract class DalvikTestBase extends DynamicCallGraphTestBase { + + public static Properties walaProperties; + + static { + try { + walaProperties = WalaProperties.loadProperties(); + } catch (WalaException e) { + walaProperties = null; + } + } + + public static String getJavaJar(AnalysisScope javaScope) throws IOException { + Module javaJar = javaScope.getModules(javaScope.getApplicationLoader()).iterator().next(); + if (javaJar instanceof JarFileModule) { + String javaJarPath = ((JarFileModule)javaJar).getAbsolutePath(); + return javaJarPath; + } else { + assert javaJar instanceof NestedJarFileModule : javaJar; + File F = File.createTempFile("android", ".jar"); + //F.deleteOnExit(); + System.err.println(F.getAbsolutePath()); + TemporaryFile.streamToFile(F, ((NestedJarFileModule)javaJar).getNestedContents()); + return F.getAbsolutePath(); + } + } + + public static File convertJarToDex(String jarFile) throws IOException { + File f = File.createTempFile("convert", ".dex"); + //f.deleteOnExit(); + System.err.println(f); + com.android.dx.command.Main.main(new String[]{"--dex", "--output=" + f.getAbsolutePath(), jarFile}); + return f; + } + + public static URI[] androidLibs() { + if ("Dalvik".equals(System.getProperty("java.vm.name"))) { + try { + return new URI[]{ + new URL("file:///system/framework/core.jar").toURI(), + new URL("file:///system/framework/framework.jar").toURI(), + new URL("file:///system/framework/framework2.jar").toURI(), + new URL("file:///system/framework/framework3.jar").toURI() + }; + } catch (MalformedURLException e) { + assert false : e; + return null; + } catch (URISyntaxException e) { + assert false : e; + return null; + } + } else { + List libs = new ArrayList(); + try { + for(File lib : new File(walaProperties.getProperty(ANDROID_RT_JAR)).listFiles(new FilenameFilter() { + @Override + public boolean accept(File dir, String name) { + return name.endsWith("dex") || name.endsWith("jar"); + } + })) { + libs.add(lib.toURI()); + } + } catch (Exception e) { + for(String l : WalaProperties.getJ2SEJarFiles()) { + libs.add(new File(l).toURI()); + } + try { + File jarFile = TemporaryFile.urlToFile("android.jar", DalvikCallGraphTestBase.class.getClassLoader().getResource("android.jar")); + libs.add(jarFile.toURI()); + } catch (IOException e1) { + assert false : e1; + } + } + return libs.toArray(new URI[ libs.size() ]); + } + } + + public static AnalysisScope makeDalvikScope(boolean useAndroidLib, String dexFileName) throws IOException { + AnalysisScope scope = + useAndroidLib? + AndroidAnalysisScope.setUpAndroidAnalysisScope( + new File(dexFileName).toURI(), + CallGraphTestUtil.REGRESSION_EXCLUSIONS, + CallGraphTestUtil.class.getClassLoader(), + androidLibs()): + AndroidAnalysisScope.setUpAndroidAnalysisScope( + new File(dexFileName).toURI(), + CallGraphTestUtil.REGRESSION_EXCLUSIONS, + CallGraphTestUtil.class.getClassLoader()); + return scope; + } + +} \ No newline at end of file diff --git a/com.ibm.wala.dalvik.test/source/com/ibm/wala/dalvik/test/callGraph/DalvikCallGraphTestBase.java b/com.ibm.wala.dalvik.test/source/com/ibm/wala/dalvik/test/callGraph/DalvikCallGraphTestBase.java index 28ac5d067..2ae791171 100644 --- a/com.ibm.wala.dalvik.test/source/com/ibm/wala/dalvik/test/callGraph/DalvikCallGraphTestBase.java +++ b/com.ibm.wala.dalvik.test/source/com/ibm/wala/dalvik/test/callGraph/DalvikCallGraphTestBase.java @@ -10,32 +10,20 @@ *******************************************************************************/ package com.ibm.wala.dalvik.test.callGraph; -import static com.ibm.wala.properties.WalaProperties.ANDROID_RT_JAR; - import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; -import java.io.FilenameFilter; import java.io.IOException; import java.lang.reflect.InvocationTargetException; -import java.net.MalformedURLException; -import java.net.URI; -import java.net.URISyntaxException; -import java.net.URL; -import java.util.ArrayList; import java.util.Iterator; import java.util.List; -import java.util.Properties; import java.util.Set; import com.ibm.wala.classLoader.IClass; -import com.ibm.wala.classLoader.JarFileModule; -import com.ibm.wala.classLoader.Module; -import com.ibm.wala.classLoader.NestedJarFileModule; import com.ibm.wala.classLoader.NewSiteReference; import com.ibm.wala.core.tests.callGraph.CallGraphTestUtil; -import com.ibm.wala.core.tests.shrike.DynamicCallGraphTestBase; import com.ibm.wala.dalvik.classLoader.DexIRFactory; +import com.ibm.wala.dalvik.test.DalvikTestBase; import com.ibm.wala.dalvik.util.AndroidAnalysisScope; import com.ibm.wala.dalvik.util.AndroidEntryPointLocator; import com.ibm.wala.dalvik.util.AndroidEntryPointLocator.LocatorFlags; @@ -75,17 +63,7 @@ import com.ibm.wala.util.collections.Pair; import com.ibm.wala.util.functions.Function; import com.ibm.wala.util.io.TemporaryFile; -public class DalvikCallGraphTestBase extends DynamicCallGraphTestBase { - - public static Properties walaProperties; - - static { - try { - walaProperties = WalaProperties.loadProperties(); - } catch (WalaException e) { - walaProperties = null; - } - } +public class DalvikCallGraphTestBase extends DalvikTestBase { protected static Set processCG(CallGraph cg, Predicate filter, Function map) { Set result = HashSetFactory.make(); @@ -114,29 +92,6 @@ public class DalvikCallGraphTestBase extends DynamicCallGraphTestBase { } - protected static String getJavaJar(AnalysisScope javaScope) throws IOException { - Module javaJar = javaScope.getModules(javaScope.getApplicationLoader()).iterator().next(); - if (javaJar instanceof JarFileModule) { - String javaJarPath = ((JarFileModule)javaJar).getAbsolutePath(); - return javaJarPath; - } else { - assert javaJar instanceof NestedJarFileModule : javaJar; - File F = File.createTempFile("android", ".jar"); - //F.deleteOnExit(); - System.err.println(F.getAbsolutePath()); - TemporaryFile.streamToFile(F, ((NestedJarFileModule)javaJar).getNestedContents()); - return F.getAbsolutePath(); - } - } - - public static File convertJarToDex(String jarFile) throws IOException, InterruptedException { - File f = File.createTempFile("convert", ".dex"); - //f.deleteOnExit(); - System.err.println(f); - com.android.dx.command.Main.main(new String[]{"--dex", "--output=" + f.getAbsolutePath(), jarFile}); - return f; - } - public void dynamicCG(File javaJarPath, String mainClass, String... args) throws FileNotFoundException, IOException, ClassNotFoundException, InvalidClassFileException, FailureException, SecurityException, NoSuchMethodException, IllegalArgumentException, IllegalAccessException, InvocationTargetException, InterruptedException { File F = TemporaryFile.streamToFile(new File("test_jar.jar"), new FileInputStream(javaJarPath)); F.deleteOnExit(); @@ -216,60 +171,8 @@ public class DalvikCallGraphTestBase extends DynamicCallGraphTestBase { return Pair.make(callGraph, ptrAnalysis); } - public static URI[] androidLibs() { - if ("Dalvik".equals(System.getProperty("java.vm.name"))) { - try { - return new URI[]{ - new URL("file:///system/framework/core.jar").toURI(), - new URL("file:///system/framework/framework.jar").toURI(), - new URL("file:///system/framework/framework2.jar").toURI(), - new URL("file:///system/framework/framework3.jar").toURI() - }; - } catch (MalformedURLException e) { - assert false : e; - return null; - } catch (URISyntaxException e) { - assert false : e; - return null; - } - } else { - List libs = new ArrayList(); - try { - for(File lib : new File(walaProperties.getProperty(ANDROID_RT_JAR)).listFiles(new FilenameFilter() { - @Override - public boolean accept(File dir, String name) { - return name.endsWith("dex") || name.endsWith("jar"); - } - })) { - libs.add(lib.toURI()); - } - } catch (Exception e) { - for(String l : WalaProperties.getJ2SEJarFiles()) { - libs.add(new File(l).toURI()); - } - try { - File jarFile = TemporaryFile.urlToFile("android.jar", DalvikCallGraphTestBase.class.getClassLoader().getResource("android.jar")); - libs.add(jarFile.toURI()); - } catch (IOException e1) { - assert false : e1; - } - } - return libs.toArray(new URI[ libs.size() ]); - } - } - public static Pair> makeDalvikCallGraph(boolean useAndroidLib, String mainClassName, String dexFileName) throws IOException, ClassHierarchyException, IllegalArgumentException, CancelException { - AnalysisScope scope = - useAndroidLib? - AndroidAnalysisScope.setUpAndroidAnalysisScope( - new File(dexFileName).toURI(), - CallGraphTestUtil.REGRESSION_EXCLUSIONS, - CallGraphTestUtil.class.getClassLoader(), - androidLibs()): - AndroidAnalysisScope.setUpAndroidAnalysisScope( - new File(dexFileName).toURI(), - CallGraphTestUtil.REGRESSION_EXCLUSIONS, - CallGraphTestUtil.class.getClassLoader()); + AnalysisScope scope = makeDalvikScope(useAndroidLib, dexFileName); final IClassHierarchy cha = ClassHierarchy.make(scope); diff --git a/com.ibm.wala.dalvik.test/source/com/ibm/wala/dalvik/test/ir/DalvikAnnotationsTest.java b/com.ibm.wala.dalvik.test/source/com/ibm/wala/dalvik/test/ir/DalvikAnnotationsTest.java new file mode 100644 index 000000000..08f6b19fe --- /dev/null +++ b/com.ibm.wala.dalvik.test/source/com/ibm/wala/dalvik/test/ir/DalvikAnnotationsTest.java @@ -0,0 +1,40 @@ +package com.ibm.wala.dalvik.test.ir; + +import static com.ibm.wala.dalvik.test.DalvikTestBase.convertJarToDex; +import static com.ibm.wala.dalvik.test.DalvikTestBase.makeDalvikScope; + +import java.io.File; +import java.io.IOException; + +import org.junit.AfterClass; +import org.junit.BeforeClass; + +import com.ibm.wala.core.tests.ir.AnnotationTest; +import com.ibm.wala.core.tests.ir.JVMLAnnotationTest; +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.util.io.TemporaryFile; + +public class DalvikAnnotationsTest extends AnnotationTest { + + public static void main(String[] args) { + justThisTest(JVMLAnnotationTest.class); + } + + @BeforeClass + public static void before() throws IOException, ClassHierarchyException { + File F = File.createTempFile("waladata", ".jar"); + F.deleteOnExit(); + TemporaryFile.streamToFile(F, DalvikAnnotationsTest.class.getClassLoader().getResourceAsStream("com.ibm.wala.core.testdata_1.0.0a.jar")); + File androidDex = convertJarToDex(F.getAbsolutePath()); + AnalysisScope dalvikScope = makeDalvikScope(true, androidDex.getAbsolutePath()); + cha = ClassHierarchy.make(dalvikScope); + } + + @AfterClass + public static void after() { + cha = null; + } + +} diff --git a/com.ibm.wala.dalvik/src/com/ibm/wala/dalvik/classLoader/DexFileModule.java b/com.ibm.wala.dalvik/src/com/ibm/wala/dalvik/classLoader/DexFileModule.java index fc4985749..1392a1734 100644 --- a/com.ibm.wala.dalvik/src/com/ibm/wala/dalvik/classLoader/DexFileModule.java +++ b/com.ibm.wala.dalvik/src/com/ibm/wala/dalvik/classLoader/DexFileModule.java @@ -47,8 +47,6 @@ package com.ibm.wala.dalvik.classLoader; -import static org.jf.dexlib.ItemType.TYPE_CLASS_DEF_ITEM; - import java.io.File; import java.io.IOException; import java.util.Collection; @@ -90,9 +88,7 @@ public class DexFileModule implements Module { // create ModuleEntries from ClassDefItem entries = new HashSet(); - @SuppressWarnings("unchecked") - Section cldeff = dexfile.getSectionForType(TYPE_CLASS_DEF_ITEM); - + Section cldeff = dexfile.ClassDefsSection; for (ClassDefItem cdefitems : cldeff.getItems()) { logger.debug("DexFileModule adding class: " + cdefitems.getConciseIdentity()); entries.add(new DexModuleEntry(cdefitems)); diff --git a/com.ibm.wala.dalvik/src/com/ibm/wala/dalvik/classLoader/DexIClass.java b/com.ibm.wala.dalvik/src/com/ibm/wala/dalvik/classLoader/DexIClass.java index f0d60e0b9..32fccccb7 100644 --- a/com.ibm.wala.dalvik/src/com/ibm/wala/dalvik/classLoader/DexIClass.java +++ b/com.ibm.wala.dalvik/src/com/ibm/wala/dalvik/classLoader/DexIClass.java @@ -54,12 +54,21 @@ import static org.jf.dexlib.Util.AccessFlags.PUBLIC; import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.List; +import java.util.Map; +import java.util.Set; +import org.jf.dexlib.AnnotationDirectoryItem; +import org.jf.dexlib.AnnotationItem; +import org.jf.dexlib.AnnotationSetItem; +import org.jf.dexlib.AnnotationVisibility; import org.jf.dexlib.ClassDataItem; import org.jf.dexlib.ClassDataItem.EncodedField; import org.jf.dexlib.ClassDataItem.EncodedMethod; import org.jf.dexlib.ClassDefItem; +import org.jf.dexlib.FieldIdItem; +import org.jf.dexlib.MethodIdItem; import org.jf.dexlib.TypeIdItem; import org.jf.dexlib.TypeListItem; import org.slf4j.Logger; @@ -71,9 +80,12 @@ import com.ibm.wala.classLoader.IField; import com.ibm.wala.classLoader.IMethod; import com.ibm.wala.classLoader.Module; import com.ibm.wala.ipa.cha.IClassHierarchy; +import com.ibm.wala.shrikeCT.AnnotationsReader.AnnotationType; import com.ibm.wala.shrikeCT.InvalidClassFileException; import com.ibm.wala.types.TypeReference; import com.ibm.wala.types.annotations.Annotation; +import com.ibm.wala.util.collections.HashMapFactory; +import com.ibm.wala.util.collections.HashSetFactory; import com.ibm.wala.util.strings.ImmutableByteArray; public class DexIClass extends BytecodeClass { @@ -258,6 +270,79 @@ public class DexIClass extends BytecodeClass { return hashCode; } + Collection getAnnotations(Set types) { + Set result = HashSetFactory.make(); + AnnotationDirectoryItem d = dexModuleEntry.getClassDefItem().getAnnotations(); + if (d.getClassAnnotations() != null) { + for(AnnotationItem a : d.getClassAnnotations().getAnnotations()) { + if (types == null || types.contains(a.getVisibility())) { + result.add(DexUtil.getAnnotation(a, getClassLoader().getReference())); + } + } + } + return result; + } + + public Collection getAnnotations() { + return getAnnotations((Set)null); + } + + public Collection getAnnotations(boolean runtimeInvisible) throws InvalidClassFileException { + return getAnnotations(getTypes(runtimeInvisible)); + } + + static Set getTypes(boolean runtimeInvisible) { + Set types = HashSetFactory.make(); + types.add(AnnotationVisibility.SYSTEM); + if (runtimeInvisible) { + types.add(AnnotationVisibility.BUILD); + } else { + types.add(AnnotationVisibility.RUNTIME); + } + return types; + } + + List getAnnotations(MethodIdItem m, Set types) { + List result = new ArrayList(); + AnnotationDirectoryItem d = dexModuleEntry.getClassDefItem().getAnnotations(); + if (d != null) { + for(AnnotationItem a : d.getMethodAnnotations(m).getAnnotations()) { + if (types == null || types.contains(a.getVisibility())) { + result.add(a); + } + } + } + return result; + } + + List getAnnotations(FieldIdItem m) { + List result = new ArrayList(); + AnnotationDirectoryItem d = dexModuleEntry.getClassDefItem().getAnnotations(); + if (d != null) { + for(AnnotationItem a : d.getFieldAnnotations(m).getAnnotations()) { + result.add(a); + } + } + return result; + } + + Map> getParameterAnnotations(MethodIdItem m) { + Map> result = HashMapFactory.make(); + AnnotationDirectoryItem d = dexModuleEntry.getClassDefItem().getAnnotations(); + if (d != null) { + int i = 0; + for(AnnotationSetItem as : d.getParameterAnnotations(m).getAnnotationSets()) { + for(AnnotationItem a : as.getAnnotations()) { + if (! result.containsKey(i)) { + result.put(i, new ArrayList()); + } + result.get(i).add(a); + } + i++; + } + } + return result; + } /* * (non-Javadoc) @@ -339,11 +424,6 @@ public class DexIClass extends BytecodeClass { return clinitId!=-1?methods[clinitId]:null; } - @Override - public Collection getAnnotations() { - throw new UnsupportedOperationException(); - } - @Override public Module getContainer() { return dexModuleEntry.asModule(); diff --git a/com.ibm.wala.dalvik/src/com/ibm/wala/dalvik/classLoader/DexIField.java b/com.ibm.wala.dalvik/src/com/ibm/wala/dalvik/classLoader/DexIField.java index cb5363575..51074c061 100644 --- a/com.ibm.wala.dalvik/src/com/ibm/wala/dalvik/classLoader/DexIField.java +++ b/com.ibm.wala.dalvik/src/com/ibm/wala/dalvik/classLoader/DexIField.java @@ -58,6 +58,8 @@ import static org.jf.dexlib.Util.AccessFlags.VOLATILE; import java.util.Collection; import org.jf.dexlib.ClassDataItem.EncodedField; +import org.jf.dexlib.StringIdItem; +import org.jf.dexlib.TypeIdItem; import com.ibm.wala.classLoader.IClass; import com.ibm.wala.classLoader.IField; @@ -67,7 +69,6 @@ import com.ibm.wala.types.TypeName; import com.ibm.wala.types.TypeReference; import com.ibm.wala.types.annotations.Annotation; import com.ibm.wala.util.strings.Atom; -import com.ibm.wala.util.strings.ImmutableByteArray; public class DexIField implements IField { @@ -100,20 +101,16 @@ public class DexIField implements IField { //public DexIField(EncodedField encodedField) { eField = encodedField; myClass = klass; - name = Atom.findOrCreateUnicodeAtom(eField.field.getFieldName().getStringValue()); + StringIdItem fieldName = eField.field.getFieldName(); + name = Atom.findOrCreateUnicodeAtom(fieldName.getStringValue()); - ImmutableByteArray fieldType = ImmutableByteArray.make(eField.field.getFieldType().getTypeDescriptor()); - TypeName T = null; - if (fieldType.get(fieldType.length() - 1) == ';') { - T = TypeName.findOrCreate(fieldType, 0, fieldType.length() - 1); - } else { - T = TypeName.findOrCreate(fieldType); - } + TypeIdItem fieldType = eField.field.getFieldType(); + TypeName T = DexUtil.getTypeName(fieldType); TypeReference type = TypeReference.findOrCreate(myClass.getClassLoader().getReference(), T); myFieldRef = FieldReference.findOrCreate(myClass.getReference(), name, type); } - public TypeReference getFieldTypeReference() { + public TypeReference getFieldTypeReference() { //compute the typeReference from the EncodedField // if (typeReference == null) { @@ -176,7 +173,7 @@ public class DexIField implements IField { @Override public Collection getAnnotations() { - throw new UnsupportedOperationException(); + return DexUtil.getAnnotations(myClass.getAnnotations(eField.field), myClass.getClassLoader().getReference()); } } diff --git a/com.ibm.wala.dalvik/src/com/ibm/wala/dalvik/classLoader/DexIMethod.java b/com.ibm.wala.dalvik/src/com/ibm/wala/dalvik/classLoader/DexIMethod.java index fa1110c6f..059ad955a 100644 --- a/com.ibm.wala.dalvik/src/com/ibm/wala/dalvik/classLoader/DexIMethod.java +++ b/com.ibm.wala.dalvik/src/com/ibm/wala/dalvik/classLoader/DexIMethod.java @@ -61,6 +61,9 @@ import static org.jf.dexlib.Util.AccessFlags.VOLATILE; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Set; import org.jf.dexlib.AnnotationItem; import org.jf.dexlib.AnnotationSetItem; @@ -191,9 +194,6 @@ public class DexIMethod implements IBytecodeMethod { public DexIMethod(EncodedMethod encodedMethod, DexIClass klass) { eMethod = encodedMethod; myClass = klass; - //XXX TEST - //myClass.iterateMethodAnnotations(this); - } public static int getTotalInsts() { @@ -3357,7 +3357,24 @@ public class DexIMethod implements IBytecodeMethod { @Override public Collection getAnnotations() { - throw new UnsupportedOperationException(); + return DexUtil.getAnnotations(myClass.getAnnotations(eMethod.method, null), myClass.getClassLoader().getReference()); } + @Override + public Collection getAnnotations(boolean runtimeInvisible) { + return DexUtil.getAnnotations(myClass.getAnnotations(eMethod.method, DexIClass.getTypes(runtimeInvisible)), myClass.getClassLoader().getReference()); + } + + @Override + public Collection[] getParameterAnnotations() { + Map> raw = myClass.getParameterAnnotations(eMethod.method); + @SuppressWarnings("unchecked") + Collection[] result = new Collection[ getReference().getNumberOfParameters() ]; + for(Map.Entry> x : raw.entrySet()) { + result[x.getKey()] = DexUtil.getAnnotations(x.getValue(), myClass.getClassLoader().getReference()); + } + return result; + } + + } diff --git a/com.ibm.wala.dalvik/src/com/ibm/wala/dalvik/classLoader/DexUtil.java b/com.ibm.wala.dalvik/src/com/ibm/wala/dalvik/classLoader/DexUtil.java new file mode 100644 index 000000000..197cab0a2 --- /dev/null +++ b/com.ibm.wala.dalvik/src/com/ibm/wala/dalvik/classLoader/DexUtil.java @@ -0,0 +1,169 @@ +package com.ibm.wala.dalvik.classLoader; + +import java.util.Collection; +import java.util.Map; + +import org.jf.dexlib.AnnotationItem; +import org.jf.dexlib.FieldIdItem; +import org.jf.dexlib.MethodIdItem; +import org.jf.dexlib.TypeIdItem; +import org.jf.dexlib.EncodedValue.AnnotationEncodedSubValue; +import org.jf.dexlib.EncodedValue.ArrayEncodedValue; +import org.jf.dexlib.EncodedValue.BooleanEncodedValue; +import org.jf.dexlib.EncodedValue.ByteEncodedValue; +import org.jf.dexlib.EncodedValue.CharEncodedValue; +import org.jf.dexlib.EncodedValue.DoubleEncodedValue; +import org.jf.dexlib.EncodedValue.EncodedValue; +import org.jf.dexlib.EncodedValue.EnumEncodedValue; +import org.jf.dexlib.EncodedValue.FieldEncodedValue; +import org.jf.dexlib.EncodedValue.FloatEncodedValue; +import org.jf.dexlib.EncodedValue.IntEncodedValue; +import org.jf.dexlib.EncodedValue.LongEncodedValue; +import org.jf.dexlib.EncodedValue.MethodEncodedValue; +import org.jf.dexlib.EncodedValue.ShortEncodedValue; +import org.jf.dexlib.EncodedValue.StringEncodedValue; +import org.jf.dexlib.EncodedValue.TypeEncodedValue; +import org.jf.dexlib.EncodedValue.ValueType; + +import com.ibm.wala.shrikeCT.AnnotationsReader.AnnotationAttribute; +import com.ibm.wala.shrikeCT.AnnotationsReader.ArrayElementValue; +import com.ibm.wala.shrikeCT.AnnotationsReader.ConstantElementValue; +import com.ibm.wala.shrikeCT.AnnotationsReader.ElementValue; +import com.ibm.wala.shrikeCT.AnnotationsReader.EnumElementValue; +import com.ibm.wala.types.ClassLoaderReference; +import com.ibm.wala.types.Descriptor; +import com.ibm.wala.types.FieldReference; +import com.ibm.wala.types.MethodReference; +import com.ibm.wala.types.TypeName; +import com.ibm.wala.types.TypeReference; +import com.ibm.wala.types.annotations.Annotation; +import com.ibm.wala.util.collections.HashMapFactory; +import com.ibm.wala.util.collections.HashSetFactory; +import com.ibm.wala.util.strings.Atom; +import com.ibm.wala.util.strings.ImmutableByteArray; + +public class DexUtil { + + static Collection getAnnotations(Collection as, ClassLoaderReference clr) { + Collection result = HashSetFactory.make(); + for(AnnotationItem a : as) { + result.add(getAnnotation(a, clr)); + } + return result; + } + + static Annotation getAnnotation(AnnotationItem a, ClassLoaderReference clr) { + return getAnnotation(a.getEncodedAnnotation(), clr); + } + + static Annotation getAnnotation(AnnotationEncodedSubValue ea, ClassLoaderReference clr) { + Map values = HashMapFactory.make(); + TypeReference at = getTypeRef(ea.annotationType, clr); + + for(int i = 0; i < ea.names.length; i++) { + String name = ea.names[i].getStringValue(); + EncodedValue v = ea.values[i]; + ElementValue value = getValue(clr, v); + values.put(name, value); + } + + return Annotation.makeWithNamed(at, values); + } + + static ElementValue getValue(ClassLoaderReference clr, EncodedValue v) { + switch (v.getValueType()) { + case VALUE_ANNOTATION: + Annotation a = getAnnotation((AnnotationEncodedSubValue)v, clr); + return new AnnotationAttribute(a.getType().getName().toString() +";", a.getNamedArguments()); + + case VALUE_ARRAY: + EncodedValue[] vs = ((ArrayEncodedValue)v).values; + ElementValue rs[] = new ElementValue[ vs.length ]; + for(int idx = 0; idx < vs.length; idx++) { + rs[idx] = getValue(clr, vs[idx]); + } + return new ArrayElementValue(rs); + + case VALUE_BOOLEAN: + Boolean bl = ((BooleanEncodedValue)v).value; + return new ConstantElementValue(bl); + + case VALUE_BYTE: + Byte bt = ((ByteEncodedValue)v).value; + return new ConstantElementValue(bt); + + case VALUE_CHAR: + Character c = ((CharEncodedValue)v).value; + return new ConstantElementValue(c); + + case VALUE_DOUBLE: + Double d = ((DoubleEncodedValue)v).value; + return new ConstantElementValue(d); + + case VALUE_ENUM: + FieldIdItem o = ((EnumEncodedValue)v).value; + return new EnumElementValue(o.getFieldType().getTypeDescriptor(), o.getFieldName().getStringValue()); + + case VALUE_FIELD: + o = v.getValueType()==ValueType.VALUE_ENUM? ((EnumEncodedValue)v).value: ((FieldEncodedValue)v).value; + String fieldName = o.getFieldName().getStringValue(); + TypeReference ft = getTypeRef(o.getFieldType(), clr); + TypeReference ct = getTypeRef(o.getContainingClass(), clr); + return new ConstantElementValue(FieldReference.findOrCreate(ct, Atom.findOrCreateUnicodeAtom(fieldName), ft)); + + case VALUE_FLOAT: + Float f = ((FloatEncodedValue)v).value; + return new ConstantElementValue(f); + + case VALUE_INT: + Integer iv = ((IntEncodedValue)v).value; + return new ConstantElementValue(iv); + + case VALUE_LONG: + Long l = ((LongEncodedValue)v).value; + return new ConstantElementValue(l); + + case VALUE_METHOD: + MethodIdItem m = ((MethodEncodedValue)v).value; + ct = getTypeRef(m.getContainingClass(), clr); + String methodName = m.getMethodName().getStringValue(); + String methodSig = m.getPrototype().getPrototypeString(); + return new ConstantElementValue(MethodReference.findOrCreate(ct, Atom.findOrCreateUnicodeAtom(methodName), Descriptor.findOrCreateUTF8(methodSig))); + + case VALUE_NULL: + return new ConstantElementValue(null); + + case VALUE_SHORT: + Short s = ((ShortEncodedValue)v).value; + return new ConstantElementValue(s); + + case VALUE_STRING: + String str = ((StringEncodedValue)v).value.getStringValue(); + return new ConstantElementValue(str); + + case VALUE_TYPE: + TypeIdItem t = ((TypeEncodedValue)v).value; + return new ConstantElementValue(getTypeName(t) + ";"); + + default: + assert false : v; + return null; + } + } + + static TypeReference getTypeRef(TypeIdItem type, ClassLoaderReference clr) { + return TypeReference.findOrCreate(clr, getTypeName(type)); + } + + static TypeName getTypeName(TypeIdItem fieldType) { + ImmutableByteArray fieldTypeArray = ImmutableByteArray.make(fieldType.getTypeDescriptor()); + TypeName T = null; + if (fieldTypeArray.get(fieldTypeArray.length() - 1) == ';') { + T = TypeName.findOrCreate(fieldTypeArray, 0, fieldTypeArray.length() - 1); + } else { + T = TypeName.findOrCreate(fieldTypeArray); + } + return T; + } + +}