diff options
author | Andy Clement <aclement@pivotal.io> | 2019-01-24 12:41:06 -0800 |
---|---|---|
committer | Andy Clement <aclement@pivotal.io> | 2019-01-24 12:41:06 -0800 |
commit | 9659cfe976a424a20e7b840152a13d266e794226 (patch) | |
tree | 0ae99f276260be65a84660bc37bd7ebbed908e63 /bcel-builder/src/test | |
parent | 52c4cbfa1b8bcc0d2c7ac50c77203e516b335205 (diff) | |
download | aspectj-9659cfe976a424a20e7b840152a13d266e794226.tar.gz aspectj-9659cfe976a424a20e7b840152a13d266e794226.zip |
mavenizing bcel-builder - complete
Diffstat (limited to 'bcel-builder/src/test')
29 files changed, 4998 insertions, 0 deletions
diff --git a/bcel-builder/src/test/java/org/aspectj/apache/bcel/classfile/tests/AnnotationAccessFlagTest.java b/bcel-builder/src/test/java/org/aspectj/apache/bcel/classfile/tests/AnnotationAccessFlagTest.java new file mode 100644 index 000000000..96dd9e765 --- /dev/null +++ b/bcel-builder/src/test/java/org/aspectj/apache/bcel/classfile/tests/AnnotationAccessFlagTest.java @@ -0,0 +1,55 @@ +/* ******************************************************************* + * Copyright (c) 2004 IBM + * 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: + * Andy Clement - initial implementation + * ******************************************************************/ + +package org.aspectj.apache.bcel.classfile.tests; + +import java.io.File; + +import junit.framework.TestCase; + +import org.aspectj.apache.bcel.classfile.ConstantPool; +import org.aspectj.apache.bcel.classfile.JavaClass; +import org.aspectj.apache.bcel.util.ClassPath; +import org.aspectj.apache.bcel.util.SyntheticRepository; + +public class AnnotationAccessFlagTest extends TestCase { + + private boolean verbose = false; + + protected void setUp() throws Exception { + super.setUp(); + } + + /** + * If you write an annotation and compile it, the class file generated should be + * marked as an annotation type - which is detectable through BCEL. + */ + public void testAnnotationClassSaysItIs() throws ClassNotFoundException { + ClassPath cp = + new ClassPath("testdata"+File.separator+"testcode.jar"+File.pathSeparator+System.getProperty("java.class.path")); + SyntheticRepository repos = SyntheticRepository.getInstance(cp); + JavaClass clazz = repos.loadClass("SimpleAnnotation"); + ConstantPool pool = clazz.getConstantPool(); + assertTrue("Expected SimpleAnnotation class to say it was an annotation - but it didn't !", + clazz.isAnnotation()); + clazz = repos.loadClass("SimpleClass"); + assertTrue("Expected SimpleClass class to say it was not an annotation - but it didn't !", + !clazz.isAnnotation()); + } + + + protected void tearDown() throws Exception { + super.tearDown(); + } + + +} diff --git a/bcel-builder/src/test/java/org/aspectj/apache/bcel/classfile/tests/AnnotationDefaultAttributeTest.java b/bcel-builder/src/test/java/org/aspectj/apache/bcel/classfile/tests/AnnotationDefaultAttributeTest.java new file mode 100644 index 000000000..ced29d489 --- /dev/null +++ b/bcel-builder/src/test/java/org/aspectj/apache/bcel/classfile/tests/AnnotationDefaultAttributeTest.java @@ -0,0 +1,49 @@ +/* ******************************************************************* + * Copyright (c) 2004 IBM + * 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: + * Andy Clement - initial implementation + * ******************************************************************/ + +package org.aspectj.apache.bcel.classfile.tests; + +import org.aspectj.apache.bcel.classfile.AnnotationDefault; +import org.aspectj.apache.bcel.classfile.JavaClass; +import org.aspectj.apache.bcel.classfile.Method; +import org.aspectj.apache.bcel.classfile.annotation.ElementValue; +import org.aspectj.apache.bcel.classfile.annotation.SimpleElementValue; + +public class AnnotationDefaultAttributeTest extends BcelTestCase { + + protected void setUp() throws Exception { + super.setUp(); + } + + + /** + * For values in an annotation that have default values, we should be able to + * query the AnnotationDefault attribute against the method to discover the + * default value that was originally declared. + */ + public void testMethodAnnotations() throws ClassNotFoundException { + JavaClass clazz = getClassFromJar("SimpleAnnotation"); + + Method m = getMethod(clazz,"fruit"); + AnnotationDefault a = (AnnotationDefault) findAttribute("AnnotationDefault",m.getAttributes()); + SimpleElementValue val = (SimpleElementValue) a.getElementValue(); + assertTrue("Should be STRING but is "+val.getElementValueType(), + val.getElementValueType()==ElementValue.STRING); + assertTrue("Should have default of bananas but default is "+val.getValueString(), + val.getValueString().equals("bananas")); + } + + protected void tearDown() throws Exception { + super.tearDown(); + } + +} diff --git a/bcel-builder/src/test/java/org/aspectj/apache/bcel/classfile/tests/AnnotationGenTest.java b/bcel-builder/src/test/java/org/aspectj/apache/bcel/classfile/tests/AnnotationGenTest.java new file mode 100644 index 000000000..901aa2ee7 --- /dev/null +++ b/bcel-builder/src/test/java/org/aspectj/apache/bcel/classfile/tests/AnnotationGenTest.java @@ -0,0 +1,179 @@ +/******************************************************************************* + * Copyright (c) 2004 IBM 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: Andy Clement - initial implementation + ******************************************************************************/ + +package org.aspectj.apache.bcel.classfile.tests; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Vector; + +import org.aspectj.apache.bcel.Constants; +import org.aspectj.apache.bcel.classfile.Attribute; +import org.aspectj.apache.bcel.classfile.ConstantPool; +import org.aspectj.apache.bcel.classfile.Utility; +import org.aspectj.apache.bcel.classfile.annotation.AnnotationGen; +import org.aspectj.apache.bcel.classfile.annotation.NameValuePair; +import org.aspectj.apache.bcel.classfile.annotation.ElementValue; +import org.aspectj.apache.bcel.classfile.annotation.RuntimeAnnos; +import org.aspectj.apache.bcel.classfile.annotation.RuntimeInvisAnnos; +import org.aspectj.apache.bcel.classfile.annotation.RuntimeVisAnnos; +import org.aspectj.apache.bcel.classfile.annotation.SimpleElementValue; +import org.aspectj.apache.bcel.generic.ClassGen; +import org.aspectj.apache.bcel.generic.ObjectType; + +public class AnnotationGenTest extends BcelTestCase { + + @Override + protected void setUp() throws Exception { + super.setUp(); + } + + private ClassGen createClassGen(String classname) { + return new ClassGen(classname, "java.lang.Object", "<generated>", Constants.ACC_PUBLIC | Constants.ACC_SUPER, null); + } + + /** + * Programmatically construct an mutable annotation (AnnotationGen) object. + */ + public void testConstructMutableAnnotation() { + + // Create the containing class + ClassGen cg = createClassGen("HelloWorld"); + ConstantPool cp = cg.getConstantPool(); + + // Create the simple primitive value '4' of type 'int' + SimpleElementValue evg = new SimpleElementValue(ElementValue.PRIMITIVE_INT, cp, 4); + + // Give it a name, call it 'id' + NameValuePair nvGen = new NameValuePair("id", evg, cp); + + // Check it looks right + assertTrue("Should include string 'id=4' but says: " + nvGen.toString(), nvGen.toString().indexOf("id=4") != -1); + + ObjectType t = new ObjectType("SimpleAnnotation"); + + List<NameValuePair> elements = new ArrayList<NameValuePair>(); + elements.add(nvGen); + + // Build an annotation of type 'SimpleAnnotation' with 'id=4' as the only value :) + AnnotationGen a = new AnnotationGen(t, elements, true, cp); + + // Check we can save and load it ok + checkSerialize(a, cp); + } + + public void testVisibleInvisibleAnnotationGen() { + + // Create the containing class + ClassGen cg = createClassGen("HelloWorld"); + ConstantPool cp = cg.getConstantPool(); + + // Create the simple primitive value '4' of type 'int' + SimpleElementValue evg = new SimpleElementValue(ElementValue.PRIMITIVE_INT, cp, 4); + + // Give it a name, call it 'id' + NameValuePair nvGen = new NameValuePair("id", evg, cp); + + // Check it looks right + assertTrue("Should include string 'id=4' but says: " + nvGen.toString(), nvGen.toString().indexOf("id=4") != -1); + + ObjectType t = new ObjectType("SimpleAnnotation"); + + List<NameValuePair> elements = new ArrayList<NameValuePair>(); + elements.add(nvGen); + + // Build a RV annotation of type 'SimpleAnnotation' with 'id=4' as the only value :) + AnnotationGen a = new AnnotationGen(t, elements, true, cp); + + Vector<AnnotationGen> v = new Vector<AnnotationGen>(); + v.add(a); + Collection<RuntimeAnnos> attributes = Utility.getAnnotationAttributes(cp, v); + boolean foundRV = false; + for (Attribute attribute : attributes) { + if (attribute instanceof RuntimeVisAnnos) { + assertTrue(((RuntimeAnnos) attribute).areVisible()); + foundRV = true; + + } + } + assertTrue("Should have seen a RuntimeVisibleAnnotation", foundRV); + + // Build a RIV annotation of type 'SimpleAnnotation' with 'id=4' as the only value :) + AnnotationGen a2 = new AnnotationGen(t, elements, false, cp); + + Vector<AnnotationGen> v2 = new Vector<AnnotationGen>(); + v2.add(a2); + Collection<RuntimeAnnos> attributes2 = Utility.getAnnotationAttributes(cp, v2); + boolean foundRIV = false; + for (Attribute attribute : attributes2) { + // for (int i = 0; i < attributes2.length; i++) { + // Attribute attribute = attributes2[i]; + if (attribute instanceof RuntimeInvisAnnos) { + assertFalse(((RuntimeAnnos) attribute).areVisible()); + foundRIV = true; + } + } + assertTrue("Should have seen a RuntimeInvisibleAnnotation", foundRIV); + } + + // // + // Helper methods + + private void checkSerialize(AnnotationGen a, ConstantPool cpg) { + try { + String beforeName = a.getTypeName(); + List<NameValuePair> beforeValues = a.getValues(); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + DataOutputStream dos = new DataOutputStream(baos); + a.dump(dos); + dos.flush(); + dos.close(); + + byte[] bs = baos.toByteArray(); + + ByteArrayInputStream bais = new ByteArrayInputStream(bs); + DataInputStream dis = new DataInputStream(bais); + AnnotationGen annAfter = AnnotationGen.read(dis, cpg, a.isRuntimeVisible()); + + dis.close(); + + String afterName = annAfter.getTypeName(); + List<NameValuePair> afterValues = annAfter.getValues(); + + if (!beforeName.equals(afterName)) { + fail("Deserialization failed: before type='" + beforeName + "' after type='" + afterName + "'"); + } + if (a.getValues().size() != annAfter.getValues().size()) { + fail("Different numbers of element name value pairs?? " + a.getValues().size() + "!=" + annAfter.getValues().size()); + } + for (int i = 0; i < a.getValues().size(); i++) { + NameValuePair beforeElement = a.getValues().get(i); + NameValuePair afterElement = annAfter.getValues().get(i); + if (!beforeElement.getNameString().equals(afterElement.getNameString())) { + fail("Different names?? " + beforeElement.getNameString() + "!=" + afterElement.getNameString()); + } + } + + } catch (IOException ioe) { + fail("Unexpected exception whilst checking serialization: " + ioe); + } + } + + @Override + protected void tearDown() throws Exception { + super.tearDown(); + } + +}
\ No newline at end of file diff --git a/bcel-builder/src/test/java/org/aspectj/apache/bcel/classfile/tests/AnonymousClassTest.java b/bcel-builder/src/test/java/org/aspectj/apache/bcel/classfile/tests/AnonymousClassTest.java new file mode 100644 index 000000000..4a9bc0a3f --- /dev/null +++ b/bcel-builder/src/test/java/org/aspectj/apache/bcel/classfile/tests/AnonymousClassTest.java @@ -0,0 +1,60 @@ +/** + * Copyright (c) 2005 Contributors + * 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: + * Adrian Colyer - initial implementation + */ +package org.aspectj.apache.bcel.classfile.tests; + +import java.io.File; + +import org.aspectj.apache.bcel.classfile.JavaClass; +import org.aspectj.apache.bcel.util.ClassPath; +import org.aspectj.apache.bcel.util.SyntheticRepository; + +import junit.framework.TestCase; + +/** + * @author adrian colyer + * + */ +public class AnonymousClassTest extends TestCase { + + private SyntheticRepository repos; + + public void testRegularClassIsNotAnonymous() throws ClassNotFoundException { + JavaClass clazz = repos.loadClass("AnonymousClassTest"); + assertFalse("regular outer classes are not anonymous",clazz.isAnonymous()); + assertFalse("regular outer classes are not nested",clazz.isNested()); + } + + public void testNamedInnerClassIsNotAnonymous() throws ClassNotFoundException { + JavaClass clazz = repos.loadClass("AnonymousClassTest$X"); + assertFalse("regular inner classes are not anonymous",clazz.isAnonymous()); + assertTrue("regular inner classes are nested",clazz.isNested()); + } + + public void testStaticInnerClassIsNotAnonymous() throws ClassNotFoundException { + JavaClass clazz = repos.loadClass("AnonymousClassTest$Y"); + assertFalse("regular static inner classes are not anonymous",clazz.isAnonymous()); + assertTrue("regular static inner classes are nested",clazz.isNested()); + } + + public void testAnonymousInnerClassIsAnonymous() throws ClassNotFoundException { + JavaClass clazz = repos.loadClass("AnonymousClassTest$1"); + assertTrue("anonymous inner classes are anonymous",clazz.isAnonymous()); + assertTrue("anonymous inner classes are anonymous",clazz.isNested()); + } + + protected void setUp() throws Exception { + ClassPath cp = + new ClassPath("testdata"+File.separator+"testcode.jar"+File.pathSeparator+System.getProperty("java.class.path")); + repos = SyntheticRepository.getInstance(cp); + } + +} diff --git a/bcel-builder/src/test/java/org/aspectj/apache/bcel/classfile/tests/BcelTestCase.java b/bcel-builder/src/test/java/org/aspectj/apache/bcel/classfile/tests/BcelTestCase.java new file mode 100644 index 000000000..ef4b8a9a7 --- /dev/null +++ b/bcel-builder/src/test/java/org/aspectj/apache/bcel/classfile/tests/BcelTestCase.java @@ -0,0 +1,186 @@ +/* ******************************************************************* + * Copyright (c) 2004 - 2016 IBM, VMware, Contributors + * 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: + * Andy Clement - initial implementation {date} + * ******************************************************************/ + +package org.aspectj.apache.bcel.classfile.tests; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +import junit.framework.TestCase; + +import org.aspectj.apache.bcel.classfile.Attribute; +import org.aspectj.apache.bcel.classfile.ConstantPool; +import org.aspectj.apache.bcel.classfile.Field; +import org.aspectj.apache.bcel.classfile.JavaClass; +import org.aspectj.apache.bcel.classfile.Method; +import org.aspectj.apache.bcel.classfile.annotation.AnnotationGen; +import org.aspectj.apache.bcel.classfile.annotation.NameValuePair; +import org.aspectj.apache.bcel.classfile.annotation.ElementValue; +import org.aspectj.apache.bcel.classfile.annotation.SimpleElementValue; +import org.aspectj.apache.bcel.generic.ObjectType; +import org.aspectj.apache.bcel.util.ClassPath; +import org.aspectj.apache.bcel.util.SyntheticRepository; + +/** + * Super class for the Java5 tests, includes various helper methods. + */ +public abstract class BcelTestCase extends TestCase { + + private boolean verbose = false; + + protected File createTestdataFile(String name) { + return new File("testdata" + File.separator + name); + } + + protected JavaClass getClassFromJar(String clazzname) throws ClassNotFoundException { + SyntheticRepository repos = createRepos("testcode.jar"); + return repos.loadClass(clazzname); + } + + protected JavaClass getClassFromJava8Jar(String clazzname) throws ClassNotFoundException { + SyntheticRepository repos = createRepos("java8testcode.jar"); + return repos.loadClass(clazzname); + } + + protected Method getMethod(JavaClass cl, String methodname) { + Method[] methods = cl.getMethods(); + for (int i = 0; i < methods.length; i++) { + Method m = methods[i]; + if (m.getName().equals(methodname)) { + return m; + } + } + return null; + } + + protected Field getField(JavaClass cl, String fieldname) { + Field[] fields = cl.getFields(); + for (int i = 0; i < fields.length; i++) { + Field f = fields[i]; + if (f.getName().equals(fieldname)) { + return f; + } + } + return null; + } + + protected boolean wipe(String name) { + return new File("testdata" + File.separator + name).delete(); + } + + protected boolean wipe(String dir, String name) { + boolean b = wipe(dir + File.separator + name); + String[] files = new File(dir).list(); + if (files == null || files.length == 0) { + new File(dir).delete(); // Why does this not succeed? stupid thing + } + return b; + } + + public SyntheticRepository createRepos(String cpentry) { + ClassPath cp = new ClassPath("testdata" + File.separator + cpentry + File.pathSeparator + + System.getProperty("java.class.path")); + return SyntheticRepository.getInstance(cp); + } + + protected Attribute[] findAttribute(String name, JavaClass clazz) { + Attribute[] all = clazz.getAttributes(); + List<Attribute> chosenAttrsList = new ArrayList<Attribute>(); + for (int i = 0; i < all.length; i++) { + if (verbose) + System.err.println("Attribute: " + all[i].getName()); + if (all[i].getName().equals(name)) + chosenAttrsList.add(all[i]); + } + return chosenAttrsList.toArray(new Attribute[] {}); + } + + protected Attribute findAttribute(String name, Attribute[] all) { + List<Attribute> chosenAttrsList = new ArrayList<Attribute>(); + for (int i = 0; i < all.length; i++) { + if (verbose) + System.err.println("Attribute: " + all[i].getName()); + if (all[i].getName().equals(name)) + chosenAttrsList.add(all[i]); + } + assertTrue("Should be one match: " + chosenAttrsList.size(), chosenAttrsList.size() == 1); + return chosenAttrsList.get(0); + } + + protected String dumpAnnotations(AnnotationGen[] as) { + StringBuffer result = new StringBuffer(); + result.append("["); + for (int i = 0; i < as.length; i++) { + AnnotationGen annotation = as[i]; + result.append(annotation.toShortString()); + if (i + 1 < as.length) + result.append(","); + } + result.append("]"); + return result.toString(); + } + + protected String dumpAnnotations(List<AnnotationGen> as) { + StringBuffer result = new StringBuffer(); + result.append("["); + for (int i = 0; i < as.size(); i++) { + AnnotationGen annotation = as.get(i); + result.append(annotation.toShortString()); + if (i + 1 < as.size()) + result.append(","); + } + result.append("]"); + return result.toString(); + } + + protected String dumpAttributes(Attribute[] as) { + StringBuffer result = new StringBuffer(); + result.append("AttributeArray:["); + for (int i = 0; i < as.length; i++) { + Attribute attr = as[i]; + result.append(attr.toString()); + if (i + 1 < as.length) + result.append(","); + } + result.append("]"); + return result.toString(); + } + + public AnnotationGen createFruitAnnotation(ConstantPool cp, String aFruit, boolean visibility) { + SimpleElementValue evg = new SimpleElementValue(ElementValue.STRING, cp, aFruit); + NameValuePair nvGen = new NameValuePair("fruit", evg, cp); + ObjectType t = new ObjectType("SimpleStringAnnotation"); + List<NameValuePair> elements = new ArrayList<NameValuePair>(); + elements.add(nvGen); + return new AnnotationGen(t, elements, visibility, cp); + } + + public Attribute getAttribute(Attribute[] attrs, byte tag) { + for (Attribute attr: attrs) { + if (attr.getTag() == tag) { + return attr; + } + } + return null; + } + + public Attribute getAttribute(Attribute[] attrs, String name) { + for (Attribute attr: attrs) { + if (attr.getName().equals(name)) { + return attr; + } + } + return null; + } + +} diff --git a/bcel-builder/src/test/java/org/aspectj/apache/bcel/classfile/tests/ClassloaderRepositoryTest.java b/bcel-builder/src/test/java/org/aspectj/apache/bcel/classfile/tests/ClassloaderRepositoryTest.java new file mode 100644 index 000000000..7202be3fa --- /dev/null +++ b/bcel-builder/src/test/java/org/aspectj/apache/bcel/classfile/tests/ClassloaderRepositoryTest.java @@ -0,0 +1,89 @@ +package org.aspectj.apache.bcel.classfile.tests; + +import java.net.URL; +import java.net.URLClassLoader; + +import org.aspectj.apache.bcel.classfile.JavaClass; +import org.aspectj.apache.bcel.util.ClassLoaderRepository; + +import junit.framework.TestCase; + +/* + * Tests create a simple classloader repository configuration and check sharing of information. + */ +public class ClassloaderRepositoryTest extends TestCase { + + private ClassLoaderRepository rep1,rep2; + + public void setUp() throws Exception { + super.setUp(); + ClassLoader cl = Thread.currentThread().getContextClassLoader(); + ClassLoader cl1 = new URLClassLoader(new URL[]{},cl); + ClassLoader cl2 = new URLClassLoader(new URL[]{},cl); + rep1 = new ClassLoaderRepository(cl1); + rep2 = new ClassLoaderRepository(cl2); + } + + // Retrieve string 5 times from same repository, 4 hits should be from local cache + public void testLocalCacheWorks() throws ClassNotFoundException { + ClassLoaderRepository.useSharedCache=false; + JavaClass jc = rep1.loadClass("java.lang.String"); + jc = rep1.loadClass("java.lang.String"); + jc = rep1.loadClass("java.lang.String"); + jc = rep1.loadClass("java.lang.String"); + jc = rep1.loadClass("java.lang.String"); + assertTrue("Should have used local cache 4 times: "+reportLocalCacheHits(rep1),reportLocalCacheHits(rep1)==4); + } + + // Retrieve string 5 times from same repository, 4 hits should be from local cache + public void testSharedCacheWorksOnOne() throws ClassNotFoundException { + ClassLoaderRepository.useSharedCache=true; + JavaClass jc = rep1.loadClass("java.lang.String"); + jc = rep1.loadClass("java.lang.String"); + jc = rep1.loadClass("java.lang.String"); + jc = rep1.loadClass("java.lang.String"); + jc = rep1.loadClass("java.lang.String"); + assertTrue("Should have used local cache 4 times: "+reportSharedCacheHits(rep1),reportSharedCacheHits(rep1)==4); + } + + // Retrieve String through one repository then load again through another, should be shared cache hit + public void testSharedCacheWorks() throws ClassNotFoundException { + ClassLoaderRepository.useSharedCache=true; + JavaClass jc = rep1.loadClass("java.lang.String"); + jc = rep2.loadClass("java.lang.String"); + assertTrue("Should have retrieved String from shared cache: "+reportSharedCacheHits(rep1), + reportSharedCacheHits(rep1)==1); + } + + // Shared cache OFF, shouldn't get a shared cache hit + public void testSharedCacheCanBeDeactivated() throws ClassNotFoundException { + try { + ClassLoaderRepository.useSharedCache=false; + JavaClass jc = rep1.loadClass("java.lang.String"); + jc = rep2.loadClass("java.lang.String"); + assertTrue("Should not have retrieved String from shared cache: "+ + reportSharedCacheHits(rep1), + reportSharedCacheHits(rep1)==0); + } finally { + ClassLoaderRepository.useSharedCache=true; + } + } + + public void tearDown() throws Exception { + super.tearDown(); + System.err.println("Rep1: "+rep1.reportStats()); + System.err.println("Rep2: "+rep2.reportStats()); + rep1.reset(); + rep2.reset(); + } + + private long reportLocalCacheHits(ClassLoaderRepository rep) { + return rep.reportStats()[5]; + } + + private long reportSharedCacheHits(ClassLoaderRepository rep) { + return rep.reportStats()[3]; + } + +} + diff --git a/bcel-builder/src/test/java/org/aspectj/apache/bcel/classfile/tests/ConstantPoolToStringTest.java b/bcel-builder/src/test/java/org/aspectj/apache/bcel/classfile/tests/ConstantPoolToStringTest.java new file mode 100644 index 000000000..3a41c6c70 --- /dev/null +++ b/bcel-builder/src/test/java/org/aspectj/apache/bcel/classfile/tests/ConstantPoolToStringTest.java @@ -0,0 +1,54 @@ +/* ******************************************************************* + * Copyright (c) 2018 Contributors + * 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: + * Andy Clement - initial implementation + * ******************************************************************/ + +package org.aspectj.apache.bcel.classfile.tests; + +import org.aspectj.apache.bcel.classfile.ConstantPool; +import org.aspectj.apache.bcel.classfile.JavaClass; +import org.aspectj.apache.bcel.classfile.Method; +import org.aspectj.apache.bcel.util.SyntheticRepository; + +public class ConstantPoolToStringTest extends BcelTestCase { + + @Override + protected void setUp() throws Exception { + super.setUp(); + } + + public void testToStringLambdaElements() throws ClassNotFoundException { + SyntheticRepository repos = createRepos("lambda.jar"); + JavaClass clazz = repos.loadClass("Code"); + ConstantPool pool = clazz.getConstantPool(); + Method[] methods = clazz.getMethods(); + String codeString = methods[1].getCode().getCodeString(); + assertEquals("Code(max_stack = 1, max_locals = 2, code_length = 13)\n" + + "0: invokedynamic #0.run ()Ljava/lang/Runnable; (2)\n" + + "5: astore_1\n" + + "6: aload_1\n" + + "7: invokeinterface java.lang.Runnable.run ()V (3) 1 0\n" + + "12: return\n",codeString); + + // #20 = MethodHandle 6:#32 // REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite; + String cts = pool.constantToString(pool.getConstant(20)); + assertEquals("6:java.lang.invoke.LambdaMetafactory.metafactory (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;",cts); + + // #21 = MethodType #10 // ()V + cts = pool.constantToString(pool.getConstant(21)); + assertEquals("()V",cts); + } + + @Override + protected void tearDown() throws Exception { + super.tearDown(); + } + +} diff --git a/bcel-builder/src/test/java/org/aspectj/apache/bcel/classfile/tests/ElementValueGenTest.java b/bcel-builder/src/test/java/org/aspectj/apache/bcel/classfile/tests/ElementValueGenTest.java new file mode 100644 index 000000000..7d91bd7d3 --- /dev/null +++ b/bcel-builder/src/test/java/org/aspectj/apache/bcel/classfile/tests/ElementValueGenTest.java @@ -0,0 +1,229 @@ +/******************************************************************************* + * Copyright (c) 2004 IBM 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: Andy Clement - initial implementation + ******************************************************************************/ + +package org.aspectj.apache.bcel.classfile.tests; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.util.Collections; + +import org.aspectj.apache.bcel.Constants; +import org.aspectj.apache.bcel.classfile.ConstantPool; +import org.aspectj.apache.bcel.classfile.annotation.AnnotationElementValue; +import org.aspectj.apache.bcel.classfile.annotation.AnnotationGen; +import org.aspectj.apache.bcel.classfile.annotation.ClassElementValue; +import org.aspectj.apache.bcel.classfile.annotation.ElementValue; +import org.aspectj.apache.bcel.classfile.annotation.EnumElementValue; +import org.aspectj.apache.bcel.classfile.annotation.NameValuePair; +import org.aspectj.apache.bcel.classfile.annotation.SimpleElementValue; +import org.aspectj.apache.bcel.generic.ClassGen; +import org.aspectj.apache.bcel.generic.ObjectType; + +public class ElementValueGenTest extends BcelTestCase { + + protected void setUp() throws Exception { + super.setUp(); + } + + private ClassGen createClassGen(String classname) { + return new ClassGen(classname, "java.lang.Object", "<generated>", Constants.ACC_PUBLIC | Constants.ACC_SUPER, null); + } + + // // + // Create primitive element values + + public void testCreateIntegerElementValue() { + ClassGen cg = createClassGen("HelloWorld"); + ConstantPool cp = cg.getConstantPool(); + + SimpleElementValue evg = new SimpleElementValue(ElementValue.PRIMITIVE_INT, cp, 555); + // Creation of an element like that should leave a new entry in the cpool + assertTrue("Should have the same index in the constantpool but " + evg.getIndex() + "!=" + cp.lookupInteger(555), + evg.getIndex() == cp.lookupInteger(555)); + checkSerialize(evg, cp); + } + + public void testCreateFloatElementValue() { + ClassGen cg = createClassGen("HelloWorld"); + ConstantPool cp = cg.getConstantPool(); + + SimpleElementValue evg = new SimpleElementValue(ElementValue.PRIMITIVE_FLOAT, cp, 111.222f); + // Creation of an element like that should leave a new entry in the cpool + assertTrue("Should have the same index in the constantpool but " + evg.getIndex() + "!=" + cp.lookupFloat(111.222f), + evg.getIndex() == cp.lookupFloat(111.222f)); + checkSerialize(evg, cp); + } + + public void testCreateDoubleElementValue() { + ClassGen cg = createClassGen("HelloWorld"); + ConstantPool cp = cg.getConstantPool(); + + SimpleElementValue evg = new SimpleElementValue(ElementValue.PRIMITIVE_DOUBLE, cp, 333.44); + // Creation of an element like that should leave a new entry in the cpool + int idx = cp.lookupDouble(333.44); + assertTrue("Should have the same index in the constantpool but " + evg.getIndex() + "!=" + idx, evg.getIndex() == idx); + checkSerialize(evg, cp); + } + + public void testCreateLongElementValue() { + ClassGen cg = createClassGen("HelloWorld"); + ConstantPool cp = cg.getConstantPool(); + + SimpleElementValue evg = new SimpleElementValue(ElementValue.PRIMITIVE_LONG, cp, 3334455L); + // Creation of an element like that should leave a new entry in the cpool + int idx = cp.lookupLong(3334455L); + assertTrue("Should have the same index in the constantpool but " + evg.getIndex() + "!=" + idx, evg.getIndex() == idx); + checkSerialize(evg, cp); + } + + public void testCreateCharElementValue() { + ClassGen cg = createClassGen("HelloWorld"); + ConstantPool cp = cg.getConstantPool(); + + SimpleElementValue evg = new SimpleElementValue(ElementValue.PRIMITIVE_CHAR, cp, (char) 't'); + // Creation of an element like that should leave a new entry in the cpool + int idx = cp.lookupInteger((char) 't'); + assertTrue("Should have the same index in the constantpool but " + evg.getIndex() + "!=" + idx, evg.getIndex() == idx); + checkSerialize(evg, cp); + } + + public void testCreateByteElementValue() { + ClassGen cg = createClassGen("HelloWorld"); + ConstantPool cp = cg.getConstantPool(); + + SimpleElementValue evg = new SimpleElementValue(ElementValue.PRIMITIVE_CHAR, cp, (byte) 'z'); + // Creation of an element like that should leave a new entry in the cpool + int idx = cp.lookupInteger((byte) 'z'); + assertTrue("Should have the same index in the constantpool but " + evg.getIndex() + "!=" + idx, evg.getIndex() == idx); + checkSerialize(evg, cp); + } + + public void testCreateBooleanElementValue() { + ClassGen cg = createClassGen("HelloWorld"); + ConstantPool cp = cg.getConstantPool(); + + SimpleElementValue evg = new SimpleElementValue(ElementValue.PRIMITIVE_BOOLEAN, cp, true); + // Creation of an element like that should leave a new entry in the cpool + int idx = cp.lookupInteger(1); // 1 == true + assertTrue("Should have the same index in the constantpool but " + evg.getIndex() + "!=" + idx, evg.getIndex() == idx); + checkSerialize(evg, cp); + } + + public void testCreateShortElementValue() { + ClassGen cg = createClassGen("HelloWorld"); + ConstantPool cp = cg.getConstantPool(); + + SimpleElementValue evg = new SimpleElementValue(ElementValue.PRIMITIVE_SHORT, cp, (short) 42); + // Creation of an element like that should leave a new entry in the cpool + int idx = cp.lookupInteger(42); + assertTrue("Should have the same index in the constantpool but " + evg.getIndex() + "!=" + idx, evg.getIndex() == idx); + checkSerialize(evg, cp); + } + + // // + // Create string element values + + public void testCreateStringElementValue() { + + // Create HelloWorld + ClassGen cg = createClassGen("HelloWorld"); + ConstantPool cp = cg.getConstantPool(); + + SimpleElementValue evg = new SimpleElementValue(ElementValue.STRING, cp, "hello"); + // Creation of an element like that should leave a new entry in the cpool + assertTrue("Should have the same index in the constantpool but " + evg.getIndex() + "!=" + cp.lookupUtf8("hello"), + evg.getIndex() == cp.lookupUtf8("hello")); + checkSerialize(evg, cp); + } + + // // + // Create enum element value + + public void testCreateEnumElementValue() { + ClassGen cg = createClassGen("HelloWorld"); + ConstantPool cp = cg.getConstantPool(); + + ObjectType enumType = new ObjectType("SimpleEnum"); // Supports rainbow :) + + EnumElementValue evg = new EnumElementValue(enumType, "Red", cp); + // Creation of an element like that should leave a new entry in the cpool + assertTrue("The new ElementValue value index should match the contents of the constantpool but " + evg.getValueIndex() + + "!=" + cp.lookupUtf8("Red"), evg.getValueIndex() == cp.lookupUtf8("Red")); + // BCELBUG: Should the class signature or class name be in the constant pool? (see note in ConstantPool) + // assertTrue("The new ElementValue type index should match the contents of the constantpool but "+ + // evg.getTypeIndex()+"!="+cp.lookupClass(enumType.getSignature()), + // evg.getTypeIndex()==cp.lookupClass(enumType.getSignature())); + + checkSerialize(evg, cp); + } + + public void testCreateMarkerAnnotationElementValue() { + ClassGen cg = createClassGen("HelloWorld"); + ConstantPool cp = cg.getConstantPool(); + ObjectType annoType = new ObjectType("SimpleMarkerAnnotation"); + AnnotationGen annoGen = new AnnotationGen(annoType, Collections.<NameValuePair> emptyList(), true, cp); + AnnotationElementValue evg = new AnnotationElementValue(annoGen, cp); + checkSerialize(evg, cp); + } + + // // + // Create class element value + + public void testCreateClassElementValue() { + ClassGen cg = createClassGen("HelloWorld"); + ConstantPool cp = cg.getConstantPool(); + + ObjectType classType = new ObjectType("java.lang.Integer"); + + ClassElementValue evg = new ClassElementValue(classType, cp); + + assertTrue("Unexpected value for contained class: '" + evg.getClassString() + "'", + evg.getClassString().indexOf("Integer") != -1); + + checkSerialize(evg, cp); + } + + // // + // Helper methods + + private void checkSerialize(ElementValue evgBefore, ConstantPool cpg) { + try { + String beforeValue = evgBefore.stringifyValue(); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + DataOutputStream dos = new DataOutputStream(baos); + evgBefore.dump(dos); + dos.flush(); + dos.close(); + + byte[] bs = baos.toByteArray(); + + ByteArrayInputStream bais = new ByteArrayInputStream(bs); + DataInputStream dis = new DataInputStream(bais); + ElementValue evgAfter = ElementValue.readElementValue(dis, cpg); + + dis.close(); + String afterValue = evgAfter.stringifyValue(); + + if (!beforeValue.equals(afterValue)) { + fail("Deserialization failed: before='" + beforeValue + "' after='" + afterValue + "'"); + } + + } catch (IOException ioe) { + fail("Unexpected exception whilst checking serialization: " + ioe); + } + } + + protected void tearDown() throws Exception { + super.tearDown(); + } + +}
\ No newline at end of file diff --git a/bcel-builder/src/test/java/org/aspectj/apache/bcel/classfile/tests/EnclosingMethodAttributeTest.java b/bcel-builder/src/test/java/org/aspectj/apache/bcel/classfile/tests/EnclosingMethodAttributeTest.java new file mode 100644 index 000000000..a03c9b386 --- /dev/null +++ b/bcel-builder/src/test/java/org/aspectj/apache/bcel/classfile/tests/EnclosingMethodAttributeTest.java @@ -0,0 +1,98 @@ +/* ******************************************************************* + * Copyright (c) 2004 IBM + * 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: + * Andy Clement - initial implementation + * ******************************************************************/ + +package org.aspectj.apache.bcel.classfile.tests; + +import java.io.File; +import java.io.IOException; + +import org.aspectj.apache.bcel.classfile.Attribute; +import org.aspectj.apache.bcel.classfile.ConstantPool; +import org.aspectj.apache.bcel.classfile.EnclosingMethod; +import org.aspectj.apache.bcel.classfile.JavaClass; +import org.aspectj.apache.bcel.util.SyntheticRepository; + +public class EnclosingMethodAttributeTest extends BcelTestCase { + + @Override + protected void setUp() throws Exception { + super.setUp(); + } + + /** + * Verify for an inner class declared inside the 'main' method that the enclosing method attribute is set correctly. + */ + public void testCheckMethodLevelNamedInnerClass() throws ClassNotFoundException { + SyntheticRepository repos = createRepos("testcode.jar"); + JavaClass clazz = repos.loadClass("AttributeTestClassEM01$1S"); + ConstantPool pool = clazz.getConstantPool(); + Attribute[] encMethodAttrs = findAttribute("EnclosingMethod", clazz); + assertTrue("Expected 1 EnclosingMethod attribute but found " + encMethodAttrs.length, encMethodAttrs.length == 1); + EnclosingMethod em = (EnclosingMethod) encMethodAttrs[0]; + String enclosingClassName = em.getEnclosingClass().getClassname(pool); + String enclosingMethodName = em.getEnclosingMethod().getName(pool); + assertTrue("Expected class name to be 'AttributeTestClassEM01' but was " + enclosingClassName, enclosingClassName + .equals("AttributeTestClassEM01")); + assertTrue("Expected method name to be 'main' but was " + enclosingMethodName, enclosingMethodName.equals("main")); + } + + /** + * Verify for an inner class declared at the type level that the EnclosingMethod attribute is set correctly (i.e. to a null + * value) + */ + public void testCheckClassLevelNamedInnerClass() throws ClassNotFoundException { + SyntheticRepository repos = createRepos("testcode.jar"); + JavaClass clazz = repos.loadClass("AttributeTestClassEM02$1"); + ConstantPool pool = clazz.getConstantPool(); + Attribute[] encMethodAttrs = findAttribute("EnclosingMethod", clazz); + assertTrue("Expected 1 EnclosingMethod attribute but found " + encMethodAttrs.length, encMethodAttrs.length == 1); + EnclosingMethod em = (EnclosingMethod) encMethodAttrs[0]; + String enclosingClassName = em.getEnclosingClass().getClassname(pool); + assertTrue("The class is not within a method, so method_index should be null, but it is " + em.getEnclosingMethodIndex(), + em.getEnclosingMethodIndex() == 0); + assertTrue("Expected class name to be 'AttributeTestClassEM02' but was " + enclosingClassName, enclosingClassName + .equals("AttributeTestClassEM02")); + } + + /** + * Check that we can save and load the attribute correctly. + */ + public void testAttributeSerializtion() throws ClassNotFoundException, IOException { + // Read in the class + SyntheticRepository repos = createRepos("testcode.jar"); + JavaClass clazz = repos.loadClass("AttributeTestClassEM02$1"); + ConstantPool pool = clazz.getConstantPool(); + Attribute[] encMethodAttrs = findAttribute("EnclosingMethod", clazz); + assertTrue("Expected 1 EnclosingMethod attribute but found " + encMethodAttrs.length, encMethodAttrs.length == 1); + + // Write it out + File tfile = createTestdataFile("AttributeTestClassEM02$1.class"); + clazz.dump(tfile); + + // Read in the new version and check it is OK + SyntheticRepository repos2 = createRepos("."); + JavaClass clazz2 = repos2.loadClass("AttributeTestClassEM02$1"); + EnclosingMethod em = (EnclosingMethod) encMethodAttrs[0]; + String enclosingClassName = em.getEnclosingClass().getClassname(pool); + assertTrue("The class is not within a method, so method_index should be null, but it is " + em.getEnclosingMethodIndex(), + em.getEnclosingMethodIndex() == 0); + assertTrue("Expected class name to be 'AttributeTestClassEM02' but was " + enclosingClassName, enclosingClassName + .equals("AttributeTestClassEM02")); + assertTrue(tfile.delete()); + } + + @Override + protected void tearDown() throws Exception { + super.tearDown(); + } + +} diff --git a/bcel-builder/src/test/java/org/aspectj/apache/bcel/classfile/tests/EnumAccessFlagTest.java b/bcel-builder/src/test/java/org/aspectj/apache/bcel/classfile/tests/EnumAccessFlagTest.java new file mode 100644 index 000000000..a91947549 --- /dev/null +++ b/bcel-builder/src/test/java/org/aspectj/apache/bcel/classfile/tests/EnumAccessFlagTest.java @@ -0,0 +1,56 @@ +/* ******************************************************************* + * Copyright (c) 2004 IBM + * 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: + * Andy Clement - initial implementation + * ******************************************************************/ + +package org.aspectj.apache.bcel.classfile.tests; + +import java.io.File; + +import junit.framework.TestCase; + +import org.aspectj.apache.bcel.classfile.ConstantPool; +import org.aspectj.apache.bcel.classfile.JavaClass; +import org.aspectj.apache.bcel.util.ClassPath; +import org.aspectj.apache.bcel.util.SyntheticRepository; + +public class EnumAccessFlagTest extends TestCase { + + private boolean verbose = false; + + protected void setUp() throws Exception { + super.setUp(); + } + + /** + * An enumerated type, once compiled, should result in a class file that + * is marked such that we can determine from the access flags (through BCEL) that + * it was originally an enum type declaration. + */ + public void testEnumClassSaysItIs() throws ClassNotFoundException { + ClassPath cp = + new ClassPath("testdata"+File.separator+"testcode.jar"+File.pathSeparator+System.getProperty("java.class.path")); + SyntheticRepository repos = SyntheticRepository.getInstance(cp); + JavaClass clazz = repos.loadClass("SimpleEnum"); + ConstantPool pool = clazz.getConstantPool(); + assertTrue("Expected SimpleEnum class to say it was an enum - but it didn't !", + clazz.isEnum()); + clazz = repos.loadClass("SimpleClass"); + assertTrue("Expected SimpleClass class to say it was not an enum - but it didn't !", + !clazz.isEnum()); + } + + + protected void tearDown() throws Exception { + super.tearDown(); + } + + +} diff --git a/bcel-builder/src/test/java/org/aspectj/apache/bcel/classfile/tests/FieldAnnotationsTest.java b/bcel-builder/src/test/java/org/aspectj/apache/bcel/classfile/tests/FieldAnnotationsTest.java new file mode 100644 index 000000000..6f5a4f8f3 --- /dev/null +++ b/bcel-builder/src/test/java/org/aspectj/apache/bcel/classfile/tests/FieldAnnotationsTest.java @@ -0,0 +1,146 @@ +/* ******************************************************************* + * Copyright (c) 2004 IBM + * 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: + * Andy Clement - initial implementation + * ******************************************************************/ + +package org.aspectj.apache.bcel.classfile.tests; + +import java.io.File; +import java.io.IOException; +import java.util.Iterator; + +import org.aspectj.apache.bcel.classfile.Field; +import org.aspectj.apache.bcel.classfile.JavaClass; +import org.aspectj.apache.bcel.classfile.annotation.AnnotationGen; +import org.aspectj.apache.bcel.classfile.annotation.NameValuePair; +import org.aspectj.apache.bcel.generic.ClassGen; +import org.aspectj.apache.bcel.generic.FieldGen; +import org.aspectj.apache.bcel.util.SyntheticRepository; + + +public class FieldAnnotationsTest extends BcelTestCase { + + + protected void setUp() throws Exception { + super.setUp(); + } + + + /** + * Check field annotations are retrievable. + */ + public void testFieldAnnotations() throws ClassNotFoundException { + JavaClass clazz = getClassFromJar("AnnotatedFields"); + + checkAnnotatedField(clazz,"i","SimpleAnnotation","id","1"); + checkAnnotatedField(clazz,"s","SimpleAnnotation","id","2"); + + } + + /** + * Check field annotations (de)serialize ok. + */ + public void testFieldAnnotationsReadWrite() throws ClassNotFoundException,IOException { + JavaClass clazz = getClassFromJar("AnnotatedFields"); + + checkAnnotatedField(clazz,"i","SimpleAnnotation","id","1"); + checkAnnotatedField(clazz,"s","SimpleAnnotation","id","2"); + + // Write it out + File tfile = createTestdataFile("AnnotatedFields.class"); + clazz.dump(tfile); + + SyntheticRepository repos2 = createRepos("."); + JavaClass clazz2 = repos2.loadClass("AnnotatedFields"); + + checkAnnotatedField(clazz,"i","SimpleAnnotation","id","1"); + checkAnnotatedField(clazz,"s","SimpleAnnotation","id","2"); + + assertTrue(tfile.delete()); + } + + /** + * Check we can load in a class, modify its field annotations, save it, reload it and + * everything is correct. + */ + public void testFieldAnnotationsModification() throws ClassNotFoundException, IOException { + boolean dbg = false; + JavaClass clazz = getClassFromJar("AnnotatedFields"); + + ClassGen clg = new ClassGen(clazz); + Field f = clg.getFields()[0]; + if (dbg) System.err.println("Field in freshly constructed class is: "+f); + if (dbg) System.err.println("Annotations on field are: "+dumpAnnotations(f.getAnnotations())); + AnnotationGen fruitBasedAnnotation = createFruitAnnotation(clg.getConstantPool(),"Tomato",false); + FieldGen fg = new FieldGen(f,clg.getConstantPool()); + if (dbg) System.err.println("Adding annotation to the field"); + fg.addAnnotation(fruitBasedAnnotation); + if (dbg) System.err.println("FieldGen (mutable field) is "+fg); + if (dbg) System.err.println("with annotations: "+dumpAnnotations(fg.getAnnotations())); + + if (dbg) System.err.println("Replacing original field with new field that has extra annotation"); + clg.removeField(f); + clg.addField(fg.getField()); + + f = clg.getFields()[1]; // there are two fields in the class, removing and readding has changed the order + // so this time index [1] is the 'int i' field + if (dbg) System.err.println("Field now looks like this: "+f); + if (dbg) System.err.println("With annotations: "+dumpAnnotations(f.getAnnotations())); + assertTrue("Should be 2 annotations on this field, but there are "+f.getAnnotations().length,f.getAnnotations().length==2); + } + + // helper methods + + public void checkAnnotatedField(JavaClass clazz,String fieldname, + String annotationName,String annotationElementName,String annotationElementValue) { + Field[] fields = clazz.getFields(); + + for (int i = 0; i < fields.length; i++) { + Field f = fields[i]; + AnnotationGen[] fieldAnnotations = f.getAnnotations(); + if (f.getName().equals(fieldname)) { + checkAnnotation(fieldAnnotations[0],annotationName,annotationElementName,annotationElementValue); + + } + } + } + + private void checkAnnotation(AnnotationGen a,String name,String elementname,String elementvalue) { + assertTrue("Expected annotation to have name "+name+" but it had name "+a.getTypeName(), + a.getTypeName().equals(name)); + assertTrue("Expected annotation to have one element but it had "+a.getValues().size(),a.getValues().size()==1); + NameValuePair envp = a.getValues().get(0); + assertTrue("Expected element name "+elementname+" but was "+envp.getNameString(), + elementname.equals(envp.getNameString())); + assertTrue("Expected element value "+elementvalue+" but was "+envp.getValue().stringifyValue(), + elementvalue.equals(envp.getValue().stringifyValue())); + } + + + // helper methods + + public void checkValue(AnnotationGen a,String name,String tostring) { + for (Iterator<NameValuePair> i = a.getValues().iterator(); i.hasNext();) { + NameValuePair element = i.next(); + if (element.getNameString().equals(name)) { + if (!element.getValue().stringifyValue().equals(tostring)) { + fail("Expected element "+name+" to have value "+tostring+" but it had value "+element.getValue().stringifyValue()); + } + return; + } + } + fail("Didnt find named element "+name); + } + + protected void tearDown() throws Exception { + super.tearDown(); + } + +} diff --git a/bcel-builder/src/test/java/org/aspectj/apache/bcel/classfile/tests/Fundamentals.java b/bcel-builder/src/test/java/org/aspectj/apache/bcel/classfile/tests/Fundamentals.java new file mode 100644 index 000000000..c54beb5b3 --- /dev/null +++ b/bcel-builder/src/test/java/org/aspectj/apache/bcel/classfile/tests/Fundamentals.java @@ -0,0 +1,319 @@ +package org.aspectj.apache.bcel.classfile.tests; + +import org.aspectj.apache.bcel.Constants; +import org.aspectj.apache.bcel.generic.FieldInstruction; +import org.aspectj.apache.bcel.generic.IINC; +import org.aspectj.apache.bcel.generic.INVOKEINTERFACE; +import org.aspectj.apache.bcel.generic.Instruction; +import org.aspectj.apache.bcel.generic.InstructionBranch; +import org.aspectj.apache.bcel.generic.InstructionByte; +import org.aspectj.apache.bcel.generic.InstructionCP; +import org.aspectj.apache.bcel.generic.InstructionConstants; +import org.aspectj.apache.bcel.generic.InstructionHandle; +import org.aspectj.apache.bcel.generic.InstructionLV; +import org.aspectj.apache.bcel.generic.InstructionShort; +import org.aspectj.apache.bcel.generic.InvokeInstruction; +import org.aspectj.apache.bcel.generic.LOOKUPSWITCH; +import org.aspectj.apache.bcel.generic.MULTIANEWARRAY; +import org.aspectj.apache.bcel.generic.RET; +import org.aspectj.apache.bcel.generic.TABLESWITCH; + +import junit.framework.TestCase; + +// Check things that have to be true based on the specification +public class Fundamentals extends TestCase { + + // Checking: opcode, length, consumed stack entries, produced stack entries + public void testInstructions() { + + // Instructions 000-009 + checkInstruction(InstructionConstants.NOP,0,1,0,0); + checkInstruction(InstructionConstants.ACONST_NULL,1,1,0,1); + checkInstruction(InstructionConstants.ICONST_M1,2,1,0,1); + checkInstruction(InstructionConstants.ICONST_0,3,1,0,1); + checkInstruction(InstructionConstants.ICONST_1,4,1,0,1); + checkInstruction(InstructionConstants.ICONST_2,5,1,0,1); + checkInstruction(InstructionConstants.ICONST_3,6,1,0,1); + checkInstruction(InstructionConstants.ICONST_4,7,1,0,1); + checkInstruction(InstructionConstants.ICONST_5,8,1,0,1); + checkInstruction(InstructionConstants.LCONST_0,9,1,0,2); + + // Instructions 010-019 + checkInstruction(InstructionConstants.LCONST_1,10,1,0,2); + checkInstruction(InstructionConstants.FCONST_0,11,1,0,1); + checkInstruction(InstructionConstants.FCONST_1,12,1,0,1); + checkInstruction(InstructionConstants.FCONST_2,13,1,0,1); + checkInstruction(InstructionConstants.DCONST_0,14,1,0,2); + checkInstruction(InstructionConstants.DCONST_1,15,1,0,2); + checkInstruction(new InstructionByte(Constants.BIPUSH,b0),16,2,0,1); + checkInstruction(new InstructionShort(Constants.SIPUSH,s0),17,3,0,1); + checkInstruction(new InstructionCP(Constants.LDC,b0),18,2,0,1); + checkInstruction(new InstructionCP(Constants.LDC_W,s0),19,2,0,1); + + // Instructions 020-029 + checkInstruction(new InstructionCP(Constants.LDC2_W,s0),20,3,0,2); + checkInstruction(new InstructionLV(Constants.ILOAD,s20),21,2,0,1); + checkInstruction(new InstructionLV(Constants.LLOAD,s20),22,2,0,2); + checkInstruction(new InstructionLV(Constants.FLOAD,s20),23,2,0,1); + checkInstruction(new InstructionLV(Constants.DLOAD,s20),24,2,0,2); + checkInstruction(new InstructionLV(Constants.ALOAD,s20),25,2,0,1); + checkInstruction(InstructionConstants.ILOAD_0,26,1,0,1); + checkInstruction(InstructionConstants.ILOAD_1,27,1,0,1); + checkInstruction(InstructionConstants.ILOAD_2,28,1,0,1); + checkInstruction(InstructionConstants.ILOAD_3,29,1,0,1); + + // Instructions 030-039 + checkInstruction(InstructionConstants.LLOAD_0,30,1,0,2); + checkInstruction(InstructionConstants.LLOAD_1,31,1,0,2); + checkInstruction(InstructionConstants.LLOAD_2,32,1,0,2); + checkInstruction(InstructionConstants.LLOAD_3,33,1,0,2); + checkInstruction(InstructionConstants.FLOAD_0,34,1,0,1); + checkInstruction(InstructionConstants.FLOAD_1,35,1,0,1); + checkInstruction(InstructionConstants.FLOAD_2,36,1,0,1); + checkInstruction(InstructionConstants.FLOAD_3,37,1,0,1); + checkInstruction(InstructionConstants.DLOAD_0,38,1,0,2); + checkInstruction(InstructionConstants.DLOAD_1,39,1,0,2); + + // Instructions 040-049 + checkInstruction(InstructionConstants.DLOAD_2,40,1,0,2); + checkInstruction(InstructionConstants.DLOAD_3,41,1,0,2); + checkInstruction(InstructionConstants.ALOAD_0,42,1,0,1); + checkInstruction(InstructionConstants.ALOAD_1,43,1,0,1); + checkInstruction(InstructionConstants.ALOAD_2,44,1,0,1); + checkInstruction(InstructionConstants.ALOAD_3,45,1,0,1); + checkInstruction(InstructionConstants.IALOAD,46,1,2,1); + checkInstruction(InstructionConstants.LALOAD,47,1,2,2); + checkInstruction(InstructionConstants.FALOAD,48,1,2,1); + checkInstruction(InstructionConstants.DALOAD,49,1,2,2); + + // Instructions 050-059 + checkInstruction(InstructionConstants.AALOAD,50,1,2,1); + checkInstruction(InstructionConstants.BALOAD,51,1,2,1); + checkInstruction(InstructionConstants.CALOAD,52,1,2,1); + checkInstruction(InstructionConstants.SALOAD,53,1,2,1); + checkInstruction(new InstructionLV(Constants.ISTORE,s20),54,2,1,0); + checkInstruction(new InstructionLV(Constants.LSTORE,s20),55,2,2,0); + checkInstruction(new InstructionLV(Constants.FSTORE,s20),56,2,1,0); + checkInstruction(new InstructionLV(Constants.DSTORE,s20),57,2,2,0); + checkInstruction(new InstructionLV(Constants.ASTORE,s20),58,2,1,0); + checkInstruction(InstructionConstants.ISTORE_0,59,1,1,0); + + // Instructions 060-069 + checkInstruction(InstructionConstants.ISTORE_1,60,1,1,0); + checkInstruction(InstructionConstants.ISTORE_2,61,1,1,0); + checkInstruction(InstructionConstants.ISTORE_3,62,1,1,0); + checkInstruction(InstructionConstants.LSTORE_0,63,1,2,0); + checkInstruction(InstructionConstants.LSTORE_1,64,1,2,0); + checkInstruction(InstructionConstants.LSTORE_2,65,1,2,0); + checkInstruction(InstructionConstants.LSTORE_3,66,1,2,0); + checkInstruction(InstructionConstants.FSTORE_0,67,1,1,0); + checkInstruction(InstructionConstants.FSTORE_1,68,1,1,0); + checkInstruction(InstructionConstants.FSTORE_2,69,1,1,0); + + // Instructions 070-079 + checkInstruction(InstructionConstants.FSTORE_3,70,1,1,0); + checkInstruction(InstructionConstants.DSTORE_0,71,1,2,0); + checkInstruction(InstructionConstants.DSTORE_1,72,1,2,0); + checkInstruction(InstructionConstants.DSTORE_2,73,1,2,0); + checkInstruction(InstructionConstants.DSTORE_3,74,1,2,0); + checkInstruction(InstructionConstants.ASTORE_0,75,1,1,0); + checkInstruction(InstructionConstants.ASTORE_1,76,1,1,0); + checkInstruction(InstructionConstants.ASTORE_2,77,1,1,0); + checkInstruction(InstructionConstants.ASTORE_3,78,1,1,0); + checkInstruction(InstructionConstants.IASTORE,79,1,3,0); + + // Instructions 080-089 + checkInstruction(InstructionConstants.LASTORE,80,1,4,0); + checkInstruction(InstructionConstants.FASTORE,81,1,3,0); + checkInstruction(InstructionConstants.DASTORE,82,1,4,0); + checkInstruction(InstructionConstants.AASTORE,83,1,3,0); + checkInstruction(InstructionConstants.BASTORE,84,1,3,0); + checkInstruction(InstructionConstants.CASTORE,85,1,3,0); + checkInstruction(InstructionConstants.SASTORE,86,1,3,0); + checkInstruction(InstructionConstants.POP,87,1,1,0); + checkInstruction(InstructionConstants.POP2,88,1,2,0); + checkInstruction(InstructionConstants.DUP,89,1,1,2); + + // Instructions 090-099 + checkInstruction(InstructionConstants.DUP_X1,90,1,2,3); + checkInstruction(InstructionConstants.DUP_X2,91,1,3,4); + checkInstruction(InstructionConstants.DUP2,92,1,2,4); + checkInstruction(InstructionConstants.DUP2_X1,93,1,3,5); + checkInstruction(InstructionConstants.DUP2_X2,94,1,4,6); + checkInstruction(InstructionConstants.SWAP,95,1,2,2); + checkInstruction(InstructionConstants.IADD,96,1,2,1); + checkInstruction(InstructionConstants.LADD,97,1,4,2); + checkInstruction(InstructionConstants.FADD,98,1,2,1); + checkInstruction(InstructionConstants.DADD,99,1,4,2); + + // Instructions 100-109 + checkInstruction(InstructionConstants.ISUB,100,1,2,1); + checkInstruction(InstructionConstants.LSUB,101,1,4,2); + checkInstruction(InstructionConstants.FSUB,102,1,2,1); + checkInstruction(InstructionConstants.DSUB,103,1,4,2); + checkInstruction(InstructionConstants.IMUL,104,1,2,1); + checkInstruction(InstructionConstants.LMUL,105,1,4,2); + checkInstruction(InstructionConstants.FMUL,106,1,2,1); + checkInstruction(InstructionConstants.DMUL,107,1,4,2); + checkInstruction(InstructionConstants.IDIV,108,1,2,1); + checkInstruction(InstructionConstants.LDIV,109,1,4,2); + + // Instructions 110-119 + checkInstruction(InstructionConstants.FDIV,110,1,2,1); + checkInstruction(InstructionConstants.DDIV,111,1,4,2); + checkInstruction(InstructionConstants.IREM,112,1,2,1); + checkInstruction(InstructionConstants.LREM,113,1,4,2); + checkInstruction(InstructionConstants.FREM,114,1,2,1); + checkInstruction(InstructionConstants.DREM,115,1,4,2); + checkInstruction(InstructionConstants.INEG,116,1,1,1); + checkInstruction(InstructionConstants.LNEG,117,1,2,2); + checkInstruction(InstructionConstants.FNEG,118,1,1,1); + checkInstruction(InstructionConstants.DNEG,119,1,2,2); + + // Instructions 120-129 + checkInstruction(InstructionConstants.ISHL,120,1,2,1); + checkInstruction(InstructionConstants.LSHL,121,1,3,2); + checkInstruction(InstructionConstants.ISHR,122,1,2,1); + checkInstruction(InstructionConstants.LSHR,123,1,3,2); + checkInstruction(InstructionConstants.IUSHR,124,1,2,1); + checkInstruction(InstructionConstants.LUSHR,125,1,3,2); + checkInstruction(InstructionConstants.IAND,126,1,2,1); + checkInstruction(InstructionConstants.LAND,127,1,4,2); + checkInstruction(InstructionConstants.IOR,128,1,2,1); + checkInstruction(InstructionConstants.LOR,129,1,4,2); + + // Instructions 130-139 + checkInstruction(InstructionConstants.IXOR,130,1,2,1); + checkInstruction(InstructionConstants.LXOR,131,1,4,2); + checkInstruction(new IINC(0,0,false),132,3,0,0); + checkInstruction(InstructionConstants.I2L,133,1,1,2); + checkInstruction(InstructionConstants.I2F,134,1,1,1); + checkInstruction(InstructionConstants.I2D,135,1,1,2); + checkInstruction(InstructionConstants.L2I,136,1,2,1); + checkInstruction(InstructionConstants.L2F,137,1,2,1); + checkInstruction(InstructionConstants.L2D,138,1,2,2); + checkInstruction(InstructionConstants.F2I,139,1,1,1); + + // Instructions 140-149 + checkInstruction(InstructionConstants.F2L,140,1,1,2); + checkInstruction(InstructionConstants.F2D,141,1,1,2); + checkInstruction(InstructionConstants.D2I,142,1,2,1); + checkInstruction(InstructionConstants.D2L,143,1,2,2); + checkInstruction(InstructionConstants.D2F,144,1,2,1); + checkInstruction(InstructionConstants.I2B,145,1,1,1); + checkInstruction(InstructionConstants.I2C,146,1,1,1); + checkInstruction(InstructionConstants.I2S,147,1,1,1); + checkInstruction(InstructionConstants.LCMP,148,1,4,1); + checkInstruction(InstructionConstants.FCMPL,149,1,2,1); + + // Instructions 150-159 + checkInstruction(InstructionConstants.FCMPG,150,1,2,1); + checkInstruction(InstructionConstants.DCMPL,151,1,4,1); + checkInstruction(InstructionConstants.DCMPG,152,1,4,1); + checkInstruction(new InstructionBranch(Constants.IFEQ,s0),153,3,1,0); + checkInstruction(new InstructionBranch(Constants.IFNE,s0),154,3,1,0); + checkInstruction(new InstructionBranch(Constants.IFLT,s0),155,3,1,0); + checkInstruction(new InstructionBranch(Constants.IFGE,s0),156,3,1,0); + checkInstruction(new InstructionBranch(Constants.IFGT,s0),157,3,1,0); + checkInstruction(new InstructionBranch(Constants.IFLE,s0),158,3,1,0); + checkInstruction(new InstructionBranch(Constants.IF_ICMPEQ,s0),159,3,2,0); + + // Instructions 160-169 + checkInstruction(new InstructionBranch(Constants.IF_ICMPNE,s0),160,3,2,0); + checkInstruction(new InstructionBranch(Constants.IF_ICMPLT,s0),161,3,2,0); + checkInstruction(new InstructionBranch(Constants.IF_ICMPGE,s0),162,3,2,0); + checkInstruction(new InstructionBranch(Constants.IF_ICMPGT,s0),163,3,2,0); + checkInstruction(new InstructionBranch(Constants.IF_ICMPLE,s0),164,3,2,0); + checkInstruction(new InstructionBranch(Constants.IF_ACMPEQ,s0),165,3,2,0); + checkInstruction(new InstructionBranch(Constants.IF_ACMPNE,s0),166,3,2,0); + checkInstruction(new InstructionBranch(Constants.GOTO,s0),167,3,0,0); + checkInstruction(new InstructionBranch(Constants.JSR,s0),168,3,0,1); + checkInstruction(new RET(0,false),169,2,0,0); + + // Instructions 170-179 + checkInstruction(new TABLESWITCH(new int[]{},new InstructionHandle[]{},null),170,VARIES,1,0); + checkInstruction(new LOOKUPSWITCH(new int[]{},new InstructionHandle[]{},null),171,VARIES,1,0); + checkInstruction(InstructionConstants.IRETURN,172,1,1,0); + checkInstruction(InstructionConstants.LRETURN,173,1,2,0); + checkInstruction(InstructionConstants.FRETURN,174,1,1,0); + checkInstruction(InstructionConstants.DRETURN,175,1,2,0); + checkInstruction(InstructionConstants.ARETURN,176,1,1,0); + checkInstruction(InstructionConstants.RETURN,177,1,0,0); + checkInstruction(new FieldInstruction(Constants.GETSTATIC,0),178,3,0,VARIES); + checkInstruction(new FieldInstruction(Constants.PUTSTATIC,0),179,3,VARIES,0); + + // Instructions 180-189 + checkInstruction(new FieldInstruction(Constants.GETFIELD,0),180,3,1,VARIES); + checkInstruction(new FieldInstruction(Constants.PUTFIELD,0),181,3,VARIES,0); + checkInstruction(new InvokeInstruction(Constants.INVOKEVIRTUAL,0),182,3,VARIES,VARIES); // PRODUCE STACK VARIES OK HERE? (AND NEXT COUPLE) + checkInstruction(new InvokeInstruction(Constants.INVOKESPECIAL,0),183,3,VARIES,VARIES); + checkInstruction(new InvokeInstruction(Constants.INVOKESTATIC,0),184,3,VARIES,VARIES); + checkInstruction(new INVOKEINTERFACE(0,1,0),185,5,VARIES,VARIES); + // 186 does not exist + checkInstruction(new InstructionCP(Constants.NEW,b0),187,3,0,1); + checkInstruction(new InstructionByte(Constants.NEWARRAY,b0),188,2,1,1); + checkInstruction(new InstructionCP(Constants.ANEWARRAY,0),189,3,1,1); + + // Instructions 190-199 + checkInstruction(InstructionConstants.ARRAYLENGTH,190,1,1,1); + checkInstruction(InstructionConstants.ATHROW,191,1,1,1); + checkInstruction(new InstructionCP(Constants.CHECKCAST,s0),192,3,1,1); + checkInstruction(new InstructionCP(Constants.INSTANCEOF,s0),193,3,1,1); + checkInstruction(InstructionConstants.MONITORENTER,194,1,1,0); + checkInstruction(InstructionConstants.MONITOREXIT,195,1,1,0); + // 196 is 'wide' tag + checkInstruction(new MULTIANEWARRAY(s0,b0),197,4,VARIES,1); + checkInstruction(new InstructionBranch(Constants.IFNULL,s0),198,3,1,0); + checkInstruction(new InstructionBranch(Constants.IFNONNULL,s0),199,3,1,0); + + // Instructions 200-209 + checkInstruction(new InstructionBranch(Constants.GOTO_W,0),200,5,0,0); + checkInstruction(new InstructionBranch(Constants.JSR_W,0),201,5,0,1); + + // Internally used instructions skipped + } + + public void checkInstruction(Instruction i,int opcode, int length, int stackConsumed, int stackProduced) { + String header = new String("Checking instruction '"+i+"' "); + if (i.opcode!=opcode) + fail(header+" expected opcode "+opcode+" but it is "+i.opcode); + + if (length!=VARIES && i.getLength()!=length) + fail(header+" expected length "+length+" but it is "+i.getLength()); +// if (stackConsumed>0) { +// if ((Constants.instFlags[opcode]&Constants.STACK_CONSUMER)==0) +// fail(header+" expected it to be a STACK_CONSUMER but it is not"); +// } else { +// if ((Constants.instFlags[opcode]&Constants.STACK_CONSUMER)!=0) +// fail(header+" expected it not to be a STACK_CONSUMER but it is"); +// } + if (stackConsumed==VARIES) { + if (Constants.CONSUME_STACK[opcode]!=Constants.UNPREDICTABLE) + fail("Instruction '"+i+"' should be consuming some unpredictable number of stack entries but it says it will consume "+Constants.CONSUME_STACK[opcode]); + + } else { + if (Constants.CONSUME_STACK[opcode]!=stackConsumed) + fail("Instruction '"+i+"' should be consuming "+stackConsumed+" stack entries but it says it will consume "+Constants.CONSUME_STACK[opcode]); + } +// if (stackProduced>0) { +// if ((Constants.instFlags[opcode]&Constants.STACK_PRODUCER)==0) +// fail(header+" expected it to be a STACK_PRODUCER but it is not"); +// } else { +// if ((Constants.instFlags[opcode]&Constants.STACK_PRODUCER)!=0) +// fail(header+" expected it not to be a STACK_PRODUCER but it is"); +// } + if (stackProduced==VARIES) { + if (Constants.stackEntriesProduced[opcode]!=Constants.UNPREDICTABLE) + fail(header+" should be producing some unpredictable number of stack entries but it says it will produce "+Constants.stackEntriesProduced[opcode]); + + } else { + if (Constants.stackEntriesProduced[opcode]!=stackProduced) + fail(header+" should be producing "+stackProduced+" stack entries but it says it will produce "+Constants.stackEntriesProduced[opcode]); + } + } + + private final static byte b0 = 0; + private final static short s0 = 0; + private final static short s20 = 20; + private final static int VARIES = -1; +} diff --git a/bcel-builder/src/test/java/org/aspectj/apache/bcel/classfile/tests/GeneratingAnnotatedClassesTest.java b/bcel-builder/src/test/java/org/aspectj/apache/bcel/classfile/tests/GeneratingAnnotatedClassesTest.java new file mode 100644 index 000000000..94e40d491 --- /dev/null +++ b/bcel-builder/src/test/java/org/aspectj/apache/bcel/classfile/tests/GeneratingAnnotatedClassesTest.java @@ -0,0 +1,572 @@ +/******************************************************************************* + * Copyright (c) 2004 IBM 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: Andy Clement - initial implementation + ******************************************************************************/ + +package org.aspectj.apache.bcel.classfile.tests; + +import java.io.File; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import org.aspectj.apache.bcel.Constants; +import org.aspectj.apache.bcel.classfile.ConstantPool; +import org.aspectj.apache.bcel.classfile.JavaClass; +import org.aspectj.apache.bcel.classfile.Method; +import org.aspectj.apache.bcel.classfile.annotation.AnnotationElementValue; +import org.aspectj.apache.bcel.classfile.annotation.AnnotationGen; +import org.aspectj.apache.bcel.classfile.annotation.ArrayElementValue; +import org.aspectj.apache.bcel.classfile.annotation.NameValuePair; +import org.aspectj.apache.bcel.classfile.annotation.ElementValue; +import org.aspectj.apache.bcel.classfile.annotation.SimpleElementValue; +import org.aspectj.apache.bcel.generic.ArrayType; +import org.aspectj.apache.bcel.generic.ClassGen; +import org.aspectj.apache.bcel.generic.InstructionBranch; +import org.aspectj.apache.bcel.generic.InstructionConstants; +import org.aspectj.apache.bcel.generic.InstructionFactory; +import org.aspectj.apache.bcel.generic.InstructionHandle; +import org.aspectj.apache.bcel.generic.InstructionList; +import org.aspectj.apache.bcel.generic.LocalVariableGen; +import org.aspectj.apache.bcel.generic.MethodGen; +import org.aspectj.apache.bcel.generic.ObjectType; +import org.aspectj.apache.bcel.generic.Type; +import org.aspectj.apache.bcel.util.SyntheticRepository; + +/** + * The program that some of the tests generate looks like this: public class HelloWorld { public static void main(String[] argv) { + * BufferedReader in = new BufferedReader(new InputStreamReader(System.in)); String name = null; try { name = "Andy"; } + * catch(IOException e) { return; } System.out.println("Hello, " + name); } } + * + */ +public class GeneratingAnnotatedClassesTest extends BcelTestCase { + + @Override + protected void setUp() throws Exception { + super.setUp(); + } + + /* + * Steps in the test: 1) Programmatically construct the HelloWorld program 2) Add two simple annotations at the class level 3) + * Save the class to disk 4) Reload the class using the 'static' variant of the BCEL classes 5) Check the attributes are OK + */ + public void testGenerateClassLevelAnnotations() throws ClassNotFoundException { + + // Create HelloWorld + ClassGen cg = createClassGen("HelloWorld"); + ConstantPool cp = cg.getConstantPool(); + InstructionList il = new InstructionList(); + + cg.addAnnotation(createSimpleVisibleAnnotation(cp)); + cg.addAnnotation(createSimpleInvisibleAnnotation(cp)); + + buildClassContents(cg, cp, il); + + dumpClass(cg, "HelloWorld.class"); + + JavaClass jc = getClassFrom(".", "HelloWorld"); + + AnnotationGen[] as = jc.getAnnotations(); + assertTrue("Should be two annotations but found " + as.length, as.length == 2); + AnnotationGen one = as[0]; + AnnotationGen two = as[1]; + assertTrue("Name of annotation 1 should be SimpleAnnotation but it is " + as[0].getTypeName(), as[0].getTypeName().equals( + "SimpleAnnotation")); + assertTrue("Name of annotation 2 should be SimpleAnnotation but it is " + as[1].getTypeName(), as[1].getTypeName().equals( + "SimpleAnnotation")); + List<NameValuePair> vals = as[0].getValues(); + NameValuePair nvp = vals.get(0); + assertTrue("Name of element in SimpleAnnotation should be 'id' but it is " + nvp.getNameString(), nvp.getNameString() + .equals("id")); + ElementValue ev = nvp.getValue(); + assertTrue("Type of element value should be int but it is " + ev.getElementValueType(), + ev.getElementValueType() == ElementValue.PRIMITIVE_INT); + assertTrue("Value of element should be 4 but it is " + ev.stringifyValue(), ev.stringifyValue().equals("4")); + assertTrue(createTestdataFile("HelloWorld.class").delete()); + } + + /** + * Just check that we can dump a class that has a method annotation on it and it is still there when we read it back in + */ + public void testGenerateMethodLevelAnnotations1() throws ClassNotFoundException { + // Create HelloWorld + ClassGen cg = createClassGen("HelloWorld"); + ConstantPool cp = cg.getConstantPool(); + InstructionList il = new InstructionList(); + + buildClassContentsWithAnnotatedMethods(cg, cp, il); + + // Check annotation is OK + int i = cg.getMethods()[0].getAnnotations().length; + assertTrue("Prior to dumping, main method should have 1 annotation but has " + i, i == 1); + + dumpClass(cg, "temp1" + File.separator + "HelloWorld.class"); + + JavaClass jc2 = getClassFrom("temp1", "HelloWorld"); + + // Check annotation is OK + i = jc2.getMethods()[0].getAnnotations().length; + assertTrue("JavaClass should say 1 annotation on main method but says " + i, i == 1); + + ClassGen cg2 = new ClassGen(jc2); + + // Check it now it is a ClassGen + Method[] m = cg2.getMethods(); + i = m[0].getAnnotations().length; + assertTrue("The main 'Method' should have one annotation but has " + i, i == 1); + MethodGen mg = new MethodGen(m[0], cg2.getClassName(), cg2.getConstantPool()); + + // Check it finally when the Method is changed to a MethodGen + i = mg.getAnnotations().size(); + assertTrue("The main 'MethodGen' should have one annotation but has " + i, i == 1); + + assertTrue(wipe("temp1" + File.separator + "HelloWorld.class")); + + } + + /** + * Going further than the last test - when we reload the method back in, let's change it (adding a new annotation) and then + * store that, read it back in and verify both annotations are there ! + */ + public void testGenerateMethodLevelAnnotations2() throws ClassNotFoundException { + // Create HelloWorld + ClassGen cg = createClassGen("HelloWorld"); + ConstantPool cp = cg.getConstantPool(); + InstructionList il = new InstructionList(); + + buildClassContentsWithAnnotatedMethods(cg, cp, il); + + dumpClass(cg, "temp2", "HelloWorld.class"); + + JavaClass jc2 = getClassFrom("temp2", "HelloWorld"); + + ClassGen cg2 = new ClassGen(jc2); + + // Main method after reading the class back in + Method mainMethod1 = jc2.getMethods()[0]; + assertTrue("The 'Method' should have one annotations but has " + mainMethod1.getAnnotations().length, mainMethod1 + .getAnnotations().length == 1); + + MethodGen mainMethod2 = new MethodGen(mainMethod1, cg2.getClassName(), cg2.getConstantPool()); + + assertTrue("The 'MethodGen' should have one annotations but has " + mainMethod2.getAnnotations().size(), mainMethod2 + .getAnnotations().size() == 1); + + mainMethod2.addAnnotation(createFruitAnnotation(cg2.getConstantPool(), "Pear")); + + cg2.removeMethod(mainMethod1); + cg2.addMethod(mainMethod2.getMethod()); + + dumpClass(cg2, "temp3", "HelloWorld.class"); + + JavaClass jc3 = getClassFrom("temp3", "HelloWorld"); + + ClassGen cg3 = new ClassGen(jc3); + + Method mainMethod3 = cg3.getMethods()[1]; + int i = mainMethod3.getAnnotations().length; + assertTrue("The 'Method' should now have two annotations but has " + i, i == 2); + + assertTrue(wipe("temp2", "HelloWorld.class")); + assertTrue(wipe("temp3", "HelloWorld.class")); + } + + // J5TODO: Need to add deleteFile calls to many of these tests + + /** + * Transform simple class from an immutable to a mutable object. + */ + public void testTransformClassToClassGen_SimpleTypes() throws ClassNotFoundException { + JavaClass jc = getClassFrom("testcode.jar", "SimpleAnnotatedClass"); + ClassGen cgen = new ClassGen(jc); + + // Check annotations are correctly preserved + AnnotationGen[] annotations = cgen.getAnnotations(); + assertTrue("Expected one annotation but found " + annotations.length, annotations.length == 1); + } + + /** + * Transform simple class from an immutable to a mutable object. The class is annotated with an annotation that uses an enum. + */ + public void testTransformClassToClassGen_EnumType() throws ClassNotFoundException { + JavaClass jc = getClassFrom("testcode.jar", "AnnotatedWithEnumClass"); + ClassGen cgen = new ClassGen(jc); + + // Check annotations are correctly preserved + AnnotationGen[] annotations = cgen.getAnnotations(); + assertTrue("Expected one annotation but found " + annotations.length, annotations.length == 1); + } + + /** + * Transform simple class from an immutable to a mutable object. The class is annotated with an annotation that uses an array of + * SimpleAnnotations. + */ + public void testTransformClassToClassGen_ArrayAndAnnotationTypes() throws ClassNotFoundException { + JavaClass jc = getClassFrom("testcode.jar", "AnnotatedWithCombinedAnnotation"); + ClassGen cgen = new ClassGen(jc); + + // Check annotations are correctly preserved + AnnotationGen[] annotations = cgen.getAnnotations(); + assertTrue("Expected one annotation but found " + annotations.length, annotations.length == 1); + AnnotationGen a = annotations[0]; + assertTrue("That annotation should only have one value but has " + a.getValues().size(), a.getValues().size() == 1); + NameValuePair nvp = a.getValues().get(0); + ElementValue value = nvp.getValue(); + assertTrue("Value should be ArrayElementValueGen but is " + value, value instanceof ArrayElementValue); + ArrayElementValue arrayValue = (ArrayElementValue) value; + assertTrue("Array value should be size one but is " + arrayValue.getElementValuesArraySize(), arrayValue + .getElementValuesArraySize() == 1); + ElementValue innerValue = arrayValue.getElementValuesArray()[0]; + assertTrue("Value in the array should be AnnotationElementValueGen but is " + innerValue, + innerValue instanceof AnnotationElementValue); + AnnotationElementValue innerAnnotationValue = (AnnotationElementValue) innerValue; + assertTrue("Should be called LSimpleAnnotation; but is called: " + innerAnnotationValue.getAnnotation().getTypeName(), + innerAnnotationValue.getAnnotation().getTypeSignature().equals("LSimpleAnnotation;")); + } + + /** + * Transform complex class from an immutable to a mutable object. + */ + public void testTransformComplexClassToClassGen() throws ClassNotFoundException { + JavaClass jc = getClassFrom("testcode.jar", "ComplexAnnotatedClass"); + ClassGen cgen = new ClassGen(jc); + + // Check annotations are correctly preserved + AnnotationGen[] annotations = cgen.getAnnotations(); + assertTrue("Expected one annotation but found " + annotations.length, annotations.length == 1); + List<NameValuePair> l = annotations[0].getValues(); + boolean found = false; + for (Iterator<NameValuePair> iter = l.iterator(); iter.hasNext();) { + NameValuePair element = iter.next(); + if (element.getNameString().equals("dval")) { + if (((SimpleElementValue) element.getValue()).stringifyValue().equals("33.4")) + found = true; + } + } + assertTrue("Did not find double annotation value with value 33.4", found); + } + + /** + * Load a class in and modify it with a new attribute - A SimpleAnnotation annotation + */ + public void testModifyingClasses1() throws ClassNotFoundException { + JavaClass jc = getClassFrom("testcode.jar", "SimpleAnnotatedClass"); + ClassGen cgen = new ClassGen(jc); + ConstantPool cp = cgen.getConstantPool(); + cgen.addAnnotation(createFruitAnnotation(cp, "Pineapple")); + assertTrue("Should now have two annotations but has " + cgen.getAnnotations().length, cgen.getAnnotations().length == 2); + dumpClass(cgen, "SimpleAnnotatedClass.class"); + assertTrue(wipe("SimpleAnnotatedClass.class")); + } + + /** + * Load a class in and modify it with a new attribute - A ComplexAnnotation annotation + */ + public void testModifyingClasses2() throws ClassNotFoundException { + JavaClass jc = getClassFrom("testcode.jar", "SimpleAnnotatedClass"); + ClassGen cgen = new ClassGen(jc); + ConstantPool cp = cgen.getConstantPool(); + cgen.addAnnotation(createCombinedAnnotation(cp)); + assertTrue("Should now have two annotations but has " + cgen.getAnnotations().length, cgen.getAnnotations().length == 2); + dumpClass(cgen, "SimpleAnnotatedClass.class"); + JavaClass jc2 = getClassFrom(".", "SimpleAnnotatedClass"); + jc2.getAnnotations(); + assertTrue(wipe("SimpleAnnotatedClass.class")); + // System.err.println(jc2.toString()); + } + + private void dumpClass(ClassGen cg, String fname) { + try { + File f = createTestdataFile(fname); + cg.getJavaClass().dump(f); + } catch (java.io.IOException e) { + System.err.println(e); + } + } + + private void dumpClass(ClassGen cg, String dir, String fname) { + dumpClass(cg, dir + File.separator + fname); + } + + private void buildClassContentsWithAnnotatedMethods(ClassGen cg, ConstantPool cp, InstructionList il) { + // Create method 'public static void main(String[]argv)' + MethodGen mg = createMethodGen("main", il, cp); + InstructionFactory factory = new InstructionFactory(cg); + mg.addAnnotation(createSimpleVisibleAnnotation(mg.getConstantPool())); + // We now define some often used types: + + ObjectType i_stream = new ObjectType("java.io.InputStream"); + ObjectType p_stream = new ObjectType("java.io.PrintStream"); + + // Create variables in and name : We call the constructors, i.e., + // execute BufferedReader(InputStreamReader(System.in)) . The reference + // to the BufferedReader object stays on top of the stack and is stored + // in the newly allocated in variable. + + il.append(factory.createNew("java.io.BufferedReader")); + il.append(InstructionConstants.DUP); // Use predefined constant + il.append(factory.createNew("java.io.InputStreamReader")); + il.append(InstructionConstants.DUP); + il.append(factory.createFieldAccess("java.lang.System", "in", i_stream, Constants.GETSTATIC)); + il.append(factory.createInvoke("java.io.InputStreamReader", "<init>", Type.VOID, new Type[] { i_stream }, + Constants.INVOKESPECIAL)); + il.append(factory.createInvoke("java.io.BufferedReader", "<init>", Type.VOID, + new Type[] { new ObjectType("java.io.Reader") }, Constants.INVOKESPECIAL)); + + LocalVariableGen lg = mg.addLocalVariable("in", new ObjectType("java.io.BufferedReader"), null, null); + int in = lg.getIndex(); + lg.setStart(il.append(InstructionFactory.createASTORE(in))); // "in" valid from here + + // Create local variable name and initialize it to null + + lg = mg.addLocalVariable("name", Type.STRING, null, null); + int name = lg.getIndex(); + il.append(InstructionConstants.ACONST_NULL); + lg.setStart(il.append(InstructionFactory.createASTORE(name))); // "name" valid from here + + // Create try-catch block: We remember the start of the block, read a + // line from the standard input and store it into the variable name . + + // InstructionHandle try_start = il.append(factory.createFieldAccess( + // "java.lang.System", "out", p_stream, Constants.GETSTATIC)); + + // il.append(new PUSH(cp, "Please enter your name> ")); + // il.append(factory.createInvoke("java.io.PrintStream", "print", + // Type.VOID, new Type[] { Type.STRING }, + // Constants.INVOKEVIRTUAL)); + // il.append(new ALOAD(in)); + // il.append(factory.createInvoke("java.io.BufferedReader", "readLine", + // Type.STRING, Type.NO_ARGS, Constants.INVOKEVIRTUAL)); + InstructionHandle try_start = il.append(InstructionFactory.PUSH(cp, "Andy")); + il.append(InstructionFactory.createASTORE(name)); + + // Upon normal execution we jump behind exception handler, the target + // address is not known yet. + + InstructionBranch g = new InstructionBranch(Constants.GOTO); + InstructionHandle try_end = il.append(g); + + // We add the exception handler which simply returns from the method. + + LocalVariableGen var_ex = mg.addLocalVariable("ex", Type.getType("Ljava.io.IOException;"), null, null); + int var_ex_slot = var_ex.getIndex(); + + InstructionHandle handler = il.append(InstructionFactory.createASTORE(var_ex_slot)); + var_ex.setStart(handler); + var_ex.setEnd(il.append(InstructionConstants.RETURN)); + + mg.addExceptionHandler(try_start, try_end, handler, new ObjectType("java.io.IOException")); + + // "Normal" code continues, now we can set the branch target of the GOTO + // . + + InstructionHandle ih = il.append(factory.createFieldAccess("java.lang.System", "out", p_stream, Constants.GETSTATIC)); + g.setTarget(ih); + + // Printing "Hello": String concatenation compiles to StringBuffer + // operations. + + il.append(factory.createNew(Type.STRINGBUFFER)); + il.append(InstructionConstants.DUP); + il.append(InstructionFactory.PUSH(cp, "Hello, ")); + il.append(factory.createInvoke("java.lang.StringBuffer", "<init>", Type.VOID, new Type[] { Type.STRING }, + Constants.INVOKESPECIAL)); + il.append(InstructionFactory.createALOAD(name)); + il.append(factory.createInvoke("java.lang.StringBuffer", "append", Type.STRINGBUFFER, new Type[] { Type.STRING }, + Constants.INVOKEVIRTUAL)); + il.append(factory.createInvoke("java.lang.StringBuffer", "toString", Type.STRING, Type.NO_ARGS, Constants.INVOKEVIRTUAL)); + + il.append(factory.createInvoke("java.io.PrintStream", "println", Type.VOID, new Type[] { Type.STRING }, + Constants.INVOKEVIRTUAL)); + il.append(InstructionConstants.RETURN); + + // Finalization: Finally, we have to set the stack size, which normally + // would have to be computed on the fly and add a default constructor + // method to the class, which is empty in this case. + + mg.setMaxStack(); + mg.setMaxLocals(); + cg.addMethod(mg.getMethod()); + il.dispose(); // Allow instruction handles to be reused + cg.addEmptyConstructor(Constants.ACC_PUBLIC); + } + + private void buildClassContents(ClassGen cg, ConstantPool cp, InstructionList il) { + // Create method 'public static void main(String[]argv)' + MethodGen mg = createMethodGen("main", il, cp); + InstructionFactory factory = new InstructionFactory(cg); + // We now define some often used types: + + ObjectType i_stream = new ObjectType("java.io.InputStream"); + ObjectType p_stream = new ObjectType("java.io.PrintStream"); + + // Create variables in and name : We call the constructors, i.e., + // execute BufferedReader(InputStreamReader(System.in)) . The reference + // to the BufferedReader object stays on top of the stack and is stored + // in the newly allocated in variable. + + il.append(factory.createNew("java.io.BufferedReader")); + il.append(InstructionConstants.DUP); // Use predefined constant + il.append(factory.createNew("java.io.InputStreamReader")); + il.append(InstructionConstants.DUP); + il.append(factory.createFieldAccess("java.lang.System", "in", i_stream, Constants.GETSTATIC)); + il.append(factory.createInvoke("java.io.InputStreamReader", "<init>", Type.VOID, new Type[] { i_stream }, + Constants.INVOKESPECIAL)); + il.append(factory.createInvoke("java.io.BufferedReader", "<init>", Type.VOID, + new Type[] { new ObjectType("java.io.Reader") }, Constants.INVOKESPECIAL)); + + LocalVariableGen lg = mg.addLocalVariable("in", new ObjectType("java.io.BufferedReader"), null, null); + int in = lg.getIndex(); + lg.setStart(il.append(InstructionFactory.createASTORE(in))); // "in" valid from here + + // Create local variable name and initialize it to null + + lg = mg.addLocalVariable("name", Type.STRING, null, null); + int name = lg.getIndex(); + il.append(InstructionConstants.ACONST_NULL); + lg.setStart(il.append(InstructionFactory.createASTORE(name))); // "name" valid from here + + // Create try-catch block: We remember the start of the block, read a + // line from the standard input and store it into the variable name . + + // InstructionHandle try_start = il.append(factory.createFieldAccess( + // "java.lang.System", "out", p_stream, Constants.GETSTATIC)); + + // il.append(new PUSH(cp, "Please enter your name> ")); + // il.append(factory.createInvoke("java.io.PrintStream", "print", + // Type.VOID, new Type[] { Type.STRING }, + // Constants.INVOKEVIRTUAL)); + // il.append(new ALOAD(in)); + // il.append(factory.createInvoke("java.io.BufferedReader", "readLine", + // Type.STRING, Type.NO_ARGS, Constants.INVOKEVIRTUAL)); + InstructionHandle try_start = il.append(InstructionFactory.PUSH(cp, "Andy")); + il.append(InstructionFactory.createASTORE(name)); + + // Upon normal execution we jump behind exception handler, the target + // address is not known yet. + + InstructionBranch g = new InstructionBranch(Constants.GOTO); + InstructionHandle try_end = il.append(g); + + // We add the exception handler which simply returns from the method. + + LocalVariableGen var_ex = mg.addLocalVariable("ex", Type.getType("Ljava.io.IOException;"), null, null); + int var_ex_slot = var_ex.getIndex(); + + InstructionHandle handler = il.append(InstructionFactory.createASTORE(var_ex_slot)); + var_ex.setStart(handler); + var_ex.setEnd(il.append(InstructionConstants.RETURN)); + + mg.addExceptionHandler(try_start, try_end, handler, new ObjectType("java.io.IOException")); + + // "Normal" code continues, now we can set the branch target of the GOTO + // . + + InstructionHandle ih = il.append(factory.createFieldAccess("java.lang.System", "out", p_stream, Constants.GETSTATIC)); + g.setTarget(ih); + + // Printing "Hello": String concatenation compiles to StringBuffer + // operations. + + il.append(factory.createNew(Type.STRINGBUFFER)); + il.append(InstructionConstants.DUP); + il.append(InstructionFactory.PUSH(cp, "Hello, ")); + il.append(factory.createInvoke("java.lang.StringBuffer", "<init>", Type.VOID, new Type[] { Type.STRING }, + Constants.INVOKESPECIAL)); + il.append(InstructionFactory.createALOAD(name)); + il.append(factory.createInvoke("java.lang.StringBuffer", "append", Type.STRINGBUFFER, new Type[] { Type.STRING }, + Constants.INVOKEVIRTUAL)); + il.append(factory.createInvoke("java.lang.StringBuffer", "toString", Type.STRING, Type.NO_ARGS, Constants.INVOKEVIRTUAL)); + + il.append(factory.createInvoke("java.io.PrintStream", "println", Type.VOID, new Type[] { Type.STRING }, + Constants.INVOKEVIRTUAL)); + il.append(InstructionConstants.RETURN); + + // Finalization: Finally, we have to set the stack size, which normally + // would have to be computed on the fly and add a default constructor + // method to the class, which is empty in this case. + + mg.setMaxStack(); + mg.setMaxLocals(); + cg.addMethod(mg.getMethod()); + il.dispose(); // Allow instruction handles to be reused + cg.addEmptyConstructor(Constants.ACC_PUBLIC); + } + + private JavaClass getClassFrom(String where, String clazzname) throws ClassNotFoundException { + SyntheticRepository repos = createRepos(where); + return repos.loadClass(clazzname); + } + + // helper methods + + private ClassGen createClassGen(String classname) { + return new ClassGen(classname, "java.lang.Object", "<generated>", Constants.ACC_PUBLIC | Constants.ACC_SUPER, null); + } + + private MethodGen createMethodGen(String methodname, InstructionList il, ConstantPool cp) { + return new MethodGen(Constants.ACC_STATIC | Constants.ACC_PUBLIC, // access flags + Type.VOID, // return type + new Type[] { new ArrayType(Type.STRING, 1) }, // argument types + new String[] { "argv" }, // arg names + methodname, "HelloWorld", // method, class + il, cp); + } + + public AnnotationGen createSimpleVisibleAnnotation(ConstantPool cp) { + SimpleElementValue evg = new SimpleElementValue(ElementValue.PRIMITIVE_INT, cp, 4); + + NameValuePair nvGen = new NameValuePair("id", evg, cp); + + ObjectType t = new ObjectType("SimpleAnnotation"); + + List<NameValuePair> elements = new ArrayList<NameValuePair>(); + elements.add(nvGen); + + AnnotationGen a = new AnnotationGen(t, elements, true, cp); + return a; + } + + public AnnotationGen createFruitAnnotation(ConstantPool cp, String aFruit) { + SimpleElementValue evg = new SimpleElementValue(ElementValue.STRING, cp, aFruit); + NameValuePair nvGen = new NameValuePair("fruit", evg, cp); + ObjectType t = new ObjectType("SimpleStringAnnotation"); + List<NameValuePair> elements = new ArrayList<NameValuePair>(); + elements.add(nvGen); + return new AnnotationGen(t, elements, true, cp); + } + + public AnnotationGen createCombinedAnnotation(ConstantPool cp) { + // Create an annotation instance + AnnotationGen a = createSimpleVisibleAnnotation(cp); + ArrayElementValue array = new ArrayElementValue(cp); + array.addElement(new AnnotationElementValue(a, cp)); + NameValuePair nvp = new NameValuePair("value", array, cp); + List<NameValuePair> elements = new ArrayList<NameValuePair>(); + elements.add(nvp); + return new AnnotationGen(new ObjectType("CombinedAnnotation"), elements, true, cp); + } + + public AnnotationGen createSimpleInvisibleAnnotation(ConstantPool cp) { + SimpleElementValue evg = new SimpleElementValue(ElementValue.PRIMITIVE_INT, cp, 4); + + NameValuePair nvGen = new NameValuePair("id", evg, cp); + + ObjectType t = new ObjectType("SimpleAnnotation"); + + List<NameValuePair> elements = new ArrayList<NameValuePair>(); + elements.add(nvGen); + + AnnotationGen a = new AnnotationGen(t, elements, false, cp); + return a; + } + + protected void tearDown() throws Exception { + super.tearDown(); + } + +}
\ No newline at end of file diff --git a/bcel-builder/src/test/java/org/aspectj/apache/bcel/classfile/tests/GenericSignatureParsingTest.java b/bcel-builder/src/test/java/org/aspectj/apache/bcel/classfile/tests/GenericSignatureParsingTest.java new file mode 100644 index 000000000..f64e4440f --- /dev/null +++ b/bcel-builder/src/test/java/org/aspectj/apache/bcel/classfile/tests/GenericSignatureParsingTest.java @@ -0,0 +1,472 @@ +/* ******************************************************************* + * Copyright (c) 2005 Contributors + * 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: + * Andy Clement (IBM) initial implementation + * ******************************************************************/ +package org.aspectj.apache.bcel.classfile.tests; + +import java.util.ArrayList; + +import org.aspectj.apache.bcel.Constants; +import org.aspectj.apache.bcel.classfile.Attribute; +import org.aspectj.apache.bcel.classfile.ClassFormatException; +import org.aspectj.apache.bcel.classfile.JavaClass; +import org.aspectj.apache.bcel.classfile.Method; +import org.aspectj.apache.bcel.classfile.Signature; +import org.aspectj.apache.bcel.classfile.Utility; + +/** + * Generics introduces more complex signature possibilities, they are no longer just + * made up of primitives or big 'L' types. The addition of 'anglies' due to + * parameterization and the ability to specify wildcards (possibly bounded) + * when talking about parameterized types means we need to be much more sophisticated. + * + * + * Notes: + * Signatures are used to encode Java programming language type informaiton + * that is not part of the JVM type system, such as generic type and method + * declarations and parameterized types. This kind of information is + * needed to support reflection and debugging, and by the Java compiler. + * + * ============================================= + * + * ClassTypeSignature = LPackageSpecifier* SimpleClassTypeSignature ClassTypeSignatureSuffix*; + * + * PackageSpecifier = Identifier/PackageSpecifier* + * SimpleClassTypeSignature= Identifier TypeArguments(opt) + * ClassTypeSignatureSuffix= .SimpleClassTypeSignature + * TypeVariableSignature = TIdentifier; + * TypeArguments = <TypeArgument+> + * TypeArgument = WildcardIndiciator(opt) FieldTypeSignature + * * + * WildcardIndicator = + + * - + * ArrayTypeSignature = [TypeSignature + * TypeSignature = [FieldTypeSignature + * [BaseType + * + * <not sure those [ should be prefixing fts and bt> + * Examples: + * Ljava/util/List; == java.util.List + * Ljava/util/List<Ljava/lang/String;>; == java.util.List<java.lang.String> + * Ljava/util/List<Ljava/lang/Double;>; == java.util.List<java.lang.Double> + * Ljava/util/List<+Ljava/lang/Number;>; == java.util.List<? extends java.lang.Number> + * Ljava/util/List<-Ljava/lang/Number;>; == java.util.List<? super java.lang.Number> + * Ljava/util/List<*>; == java.util.List<?> + * Ljava/util/Map<*-Ljava/lang/Number;>; == java.util.Map<?,? super java.lang.Number> + * + * ============================================= + * + * ClassSignature = FormalTypeParameters(opt) SuperclassSignature SuperinterfaceSignatures* + * + * optional formal type parameters then a superclass signature then a superinterface signature + * + * FormalTypeParameters = <FormalTypeParameter+> + * FormalTypeParameter = Identifier ClassBound InterfaceBound* + * ClassBound = :FieldTypeSignature(opt) + * InterfaceBound = :FieldTypeSignature + * + * If it exists, a set of formal type parameters are contained in anglies and consist of an identifier a classbound (assumed to be + * object if not specified) and then an optional list of InterfaceBounds + * + * SuperclassSignature = ClassTypeSignature + * SuperinterfaceSignature = ClassTypeSignature + * FieldTypeSignature = ClassTypeSignature + * ArrayTypeSignature + * TypeVariableSignature + * + * + * MethodTypeSignature = FormalTypeParameters(opt) ( TypeSignature* ) ReturnType ThrowsSignature* + * ReturnType = TypeSignature + * VoidDescriptor + * ThrowsSignature = ^ClassTypeSignature + * ^TypeVariableSignature + * + * Examples: + * + * <T::Ljava/lang/Comparable<-Ljava/lang/Number;>;> + * + * ClassBound not supplied, Object assumed. Interface bound is Comparable<? super Number> + * + * "T:Ljava/lang/Object;:Ljava/lang/Comparable<-TT;>;","T extends java.lang.Object & java.lang.Comparable<? super T>" + * + */ +public class GenericSignatureParsingTest extends BcelTestCase { + + + /** + * Throw some generic format signatures at the BCEL signature + * parsing code and see what it does. + */ + public void testParsingGenericSignatures_ClassTypeSignature() { + // trivial + checkClassTypeSignature("Ljava/util/List;","java.util.List"); + + // basics + checkClassTypeSignature("Ljava/util/List<Ljava/lang/String;>;","java.util.List<java.lang.String>"); + checkClassTypeSignature("Ljava/util/List<Ljava/lang/Double;>;","java.util.List<java.lang.Double>"); + + // madness + checkClassTypeSignature("Ljava/util/List<+Ljava/lang/Number;>;","java.util.List<? extends java.lang.Number>"); + checkClassTypeSignature("Ljava/util/List<-Ljava/lang/Number;>;","java.util.List<? super java.lang.Number>"); + checkClassTypeSignature("Ljava/util/List<*>;", "java.util.List<?>"); + checkClassTypeSignature("Ljava/util/Map<*-Ljava/lang/Number;>;","java.util.Map<?,? super java.lang.Number>"); + + // with type params + checkClassTypeSignature("Ljava/util/Collection<TT;>;","java.util.Collection<T>"); + + // arrays + checkClassTypeSignature("Ljava/util/List<[Ljava/lang/String;>;","java.util.List<java.lang.String[]>"); + checkClassTypeSignature("[Ljava/util/List<Ljava/lang/String;>;","java.util.List<java.lang.String>[]"); + + } + + + public void testMethodTypeToSignature() { + checkMethodTypeToSignature("void",new String[]{"java.lang.String[]","boolean"},"([Ljava/lang/String;Z)V"); + checkMethodTypeToSignature("void",new String[]{"java.util.List<java/lang/String>"},"(Ljava/util/List<java/lang/String>;)V"); + } + + public void testMethodSignatureToArgumentTypes() { + checkMethodSignatureArgumentTypes("([Ljava/lang/String;Z)V",new String[]{"java.lang.String[]","boolean"}); +// checkMethodSignatureArgumentTypes("(Ljava/util/List<java/lang/String>;)V",new String[]{"java.util.List<java/lang/String>"}); + } + + public void testMethodSignatureReturnType() { + checkMethodSignatureReturnType("([Ljava/lang/String;)Z","boolean"); + } + + public void testLoadingGenerics() throws ClassNotFoundException { + JavaClass clazz = getClassFromJar("PossibleGenericsSigs"); + // J5TODO asc fill this bit in... + } + + + // helper methods below + + // These routines call BCEL to determine if it can correctly translate from one form to the other. + private void checkClassTypeSignature(String sig, String expected) { + StringBuffer result = new StringBuffer(); + int p = GenericSignatureParsingTest.readClassTypeSignatureFrom(sig,0,result,false); + assertTrue("Only swallowed "+p+" chars of this sig "+sig+" (len="+sig.length()+")",p==sig.length()); + assertTrue("Expected '"+expected+"' but got '"+result.toString()+"'",result.toString().equals(expected)); + } + + private void checkMethodTypeToSignature(String ret,String[] args,String expected) { + String res = GenericSignatureParsingTest.methodTypeToSignature(ret,args); + if (!res.equals(expected)) { + fail("Should match. Got: "+res+" Expected:"+expected); + } + } + + private void checkMethodSignatureReturnType(String sig,String expected) { + String result = GenericSignatureParsingTest.methodSignatureReturnType(sig,false); + if (!result.equals(expected)) { + fail("Should match. Got: "+result+" Expected:"+expected); + } + } + + private void checkMethodSignatureArgumentTypes(String in,String[] expected) { + String[] result = GenericSignatureParsingTest.methodSignatureArgumentTypes(in,false); + if (result.length!=expected.length) { + fail("Expected "+expected.length+" entries to be returned but only got "+result.length); + } + for (int i = 0; i < expected.length; i++) { + String string = result[i]; + if (!string.equals(expected[i])) + fail("Argument: "+i+" should have been "+expected[i]+" but was "+string); + } + } + + public Signature getSignatureAttribute(JavaClass clazz,String name) { + Method m = getMethod(clazz,name); + Attribute[] as = m.getAttributes(); + for (int i = 0; i < as.length; i++) { + Attribute attribute = as[i]; + if (attribute.getName().equals("Signature")) { + return (Signature)attribute; + } + } + return null; + } + + + /** + * Takes a string and consumes a single complete signature from it, returning + * how many chars it consumed. The chopit flag indicates whether to shorten + * type references ( java/lang/String => String ) + * + * FIXME asc this should also create some kind of object you can query for information about whether its parameterized, what the bounds are, etc... + */ + public static final int readClassTypeSignatureFrom(String signature, int posn, StringBuffer result, boolean chopit) { + int idx = posn; + try { + switch (signature.charAt(idx)) { + case 'B' : result.append("byte"); return 1; + case 'C' : result.append("char"); return 1; + case 'D' : result.append("double"); return 1; + case 'F' : result.append("float"); return 1; + case 'I' : result.append("int"); return 1; + case 'J' : result.append("long"); return 1; + case 'S' : result.append("short"); return 1; + case 'Z' : result.append("boolean");return 1; + case 'V' : result.append("void"); return 1; + + + //FIXME ASC Need a state machine to check we are parsing the right stuff here ! + case 'T' : + idx++; + int nextSemiIdx = signature.indexOf(';',idx); + result.append(signature.substring(idx,nextSemiIdx)); + return nextSemiIdx+1-posn; + + case '+' : + result.append("? extends "); + return readClassTypeSignatureFrom(signature,idx+1,result,chopit)+1; + + case '-' : + result.append("? super "); + return readClassTypeSignatureFrom(signature,idx+1,result,chopit)+1; + + case '*' : + result.append("?"); + return 1; + + case 'L' : // Full class name + boolean parameterized = false; + int idxSemicolon = signature.indexOf(';',idx); // Look for closing ';' or '<' + int idxAngly = signature.indexOf('<',idx); + int endOfSig = idxSemicolon; + if ((idxAngly!=-1) && idxAngly<endOfSig) { endOfSig = idxAngly; parameterized = true; } + + String p = signature.substring(idx+1,endOfSig); + String t = Utility.compactClassName(p,chopit); + + result.append(t); + idx=endOfSig; + // we might have finished now, depending on whether this is a parameterized type... + if (parameterized) { + idx++; + result.append("<"); + while (signature.charAt(idx)!='>') { + idx+=readClassTypeSignatureFrom(signature,idx,result,chopit); + if (signature.charAt(idx)!='>') result.append(","); + } + result.append(">");idx++; + } + if (signature.charAt(idx)!=';') throw new RuntimeException("Did not find ';' at end of signature, found "+signature.charAt(idx)); + idx++; + return idx-posn; + + + case '[' : // Array declaration + int dim = 0; + while (signature.charAt(idx)=='[') {dim++;idx++;} + idx+=readClassTypeSignatureFrom(signature,idx,result,chopit); + while (dim>0) {result.append("[]");dim--;} + return idx-posn; + + default : throw new ClassFormatException("Invalid signature: `" + + signature + "'"); + } + } catch(StringIndexOutOfBoundsException e) { // Should never occur + throw new ClassFormatException("Invalid signature: " + e + ":" + signature); + } + } + + + public static final String readClassTypeSignatureFrom(String signature) { + StringBuffer sb = new StringBuffer(); + GenericSignatureParsingTest.readClassTypeSignatureFrom(signature,0,sb,false); + return sb.toString(); + } + + + public static int countBrackets(String brackets) { + char[] chars = brackets.toCharArray(); + int count = 0; + boolean open = false; + + for(int i=0; i<chars.length; i++) { + switch(chars[i]) { + case '[': + if (open) throw new RuntimeException("Illegally nested brackets:" + brackets); + open = true; + break; + + case ']': + if (!open) throw new RuntimeException("Illegally nested brackets:" + brackets); + open = false; + count++; + break; + + default: + } + } + + if (open) throw new RuntimeException("Illegally nested brackets:" + brackets); + + return count; + } + + + /** + * Parse Java type such as "char", or "java.lang.String[]" and return the + * signature in byte code format, e.g. "C" or "[Ljava/lang/String;" respectively. + * + * @param type Java type + * @return byte code signature + */ + public static String getSignature(String type) { + StringBuffer buf = new StringBuffer(); + char[] chars = type.toCharArray(); + boolean char_found = false, delim = false; + int index = -1; + + loop: + for (int i=0; i < chars.length; i++) { + switch (chars[i]) { + case ' ': case '\t': case '\n': case '\r': case '\f': + if (char_found) delim = true; + break; + + case '[': + if (!char_found) throw new RuntimeException("Illegal type: " + type); + index = i; + break loop; + + default: + char_found = true; + if (!delim) buf.append(chars[i]); + } + } + + int brackets = 0; + + if(index > 0) brackets = GenericSignatureParsingTest.countBrackets(type.substring(index)); + + type = buf.toString(); + buf.setLength(0); + + for (int i=0; i < brackets; i++) buf.append('['); + + boolean found = false; + + for(int i=Constants.T_BOOLEAN; (i <= Constants.T_VOID) && !found; i++) { + if (Constants.TYPE_NAMES[i].equals(type)) { + found = true; + buf.append(Constants.SHORT_TYPE_NAMES[i]); + } + } + + // Class name + if (!found) buf.append('L' + type.replace('.', '/') + ';'); + + return buf.toString(); + } + + + /** + * For some method signature (class file format) like '([Ljava/lang/String;)Z' this returns + * the string representing the return type its 'normal' form, e.g. 'boolean' + * + * @param signature Method signature + * @param chopit Shorten class names + * @return return type of method + */ + public static final String methodSignatureReturnType(String signature,boolean chopit) throws ClassFormatException { + int index; + String type; + try { + // Read return type after `)' + index = signature.lastIndexOf(')') + 1; + type = Utility.signatureToString(signature.substring(index), chopit); + } catch (StringIndexOutOfBoundsException e) { + throw new ClassFormatException("Invalid method signature: " + signature); + } + return type; + } + + + /** + * For some method signature (class file format) like '([Ljava/lang/String;)Z' this returns + * the string representing the return type its 'normal' form, e.g. 'boolean' + * + * @param signature Method signature + * @return return type of method + * @throws ClassFormatException + */ + public static final String methodSignatureReturnType(String signature) throws ClassFormatException { + return GenericSignatureParsingTest.methodSignatureReturnType(signature, true); + } + + + /** + * For some method signature (class file format) like '([Ljava/lang/String;Z)V' this returns an array + * of strings representing the arguments in their 'normal' form, e.g. '{java.lang.String[],boolean}' + * + * @param signature Method signature + * @param chopit Shorten class names + * @return Array of argument types + */ + public static final String[] methodSignatureArgumentTypes(String signature,boolean chopit) throws ClassFormatException { + ArrayList<String> vec = new ArrayList<String>(); + int index; + String[] types; + + try { // Read all declarations between for `(' and `)' + if (signature.charAt(0) != '(') + throw new ClassFormatException("Invalid method signature: " + signature); + + index = 1; // current string position + + while(signature.charAt(index) != ')') { + Utility.ResultHolder rh = Utility.signatureToStringInternal(signature.substring(index),chopit); + vec.add(rh.getResult()); + index += rh.getConsumedChars(); + } + } catch(StringIndexOutOfBoundsException e) { + throw new ClassFormatException("Invalid method signature: " + signature); + } + + types = new String[vec.size()]; + vec.toArray(types); + return types; + } + + + /** + * Converts string containing the method return and argument types + * to a byte code method signature. + * + * @param returnType Return type of method (e.g. "char" or "java.lang.String[]") + * @param methodArgs Types of method arguments + * @return Byte code representation of method signature + */ + public final static String methodTypeToSignature(String returnType, String[] methodArgs) throws ClassFormatException { + + StringBuffer buf = new StringBuffer("("); + + if (methodArgs != null) { + for (int i=0; i < methodArgs.length; i++) { + String str = GenericSignatureParsingTest.getSignature(methodArgs[i]); + + if (str.equals("V")) // void can't be a method argument + throw new ClassFormatException("Invalid type: " + methodArgs[i]); + + buf.append(str); + } + } + + buf.append(")" + GenericSignatureParsingTest.getSignature(returnType)); + + return buf.toString(); + } + +} diff --git a/bcel-builder/src/test/java/org/aspectj/apache/bcel/classfile/tests/GenericsErasureTesting.java b/bcel-builder/src/test/java/org/aspectj/apache/bcel/classfile/tests/GenericsErasureTesting.java new file mode 100644 index 000000000..f0e5de738 --- /dev/null +++ b/bcel-builder/src/test/java/org/aspectj/apache/bcel/classfile/tests/GenericsErasureTesting.java @@ -0,0 +1,50 @@ +/* ******************************************************************* + * Copyright (c) 2005 Contributors + * 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: + * Andy Clement (IBM) initial implementation + * ******************************************************************/ +package org.aspectj.apache.bcel.classfile.tests; + +import org.aspectj.apache.bcel.classfile.Attribute; +import org.aspectj.apache.bcel.classfile.JavaClass; +import org.aspectj.apache.bcel.classfile.Method; +import org.aspectj.apache.bcel.classfile.Signature; + +/** + * Should be possible to recover original declared signatures after erasure by using + * the signature attribute. + */ +public class GenericsErasureTesting extends BcelTestCase { + + + public void testLoadingGenerics() throws ClassNotFoundException { + JavaClass clazz = getClassFromJar("ErasureTestData"); + Method m = getMethod(clazz,"getData"); + String sig = m.getDeclaredSignature(); + System.err.println(getSignatureAttribute(clazz,"getData")); + System.err.println(sig); + assertTrue("Incorrect: "+sig,sig.equals("()Ljava/util/Vector<Ljava/lang/String;>;")); + } + + + // helper methods below + + public Signature getSignatureAttribute(JavaClass clazz,String name) { + Method m = getMethod(clazz,name); + Attribute[] as = m.getAttributes(); + for (int i = 0; i < as.length; i++) { + Attribute attribute = as[i]; + if (attribute.getName().equals("Signature")) { + return (Signature)attribute; + } + } + return null; + } + +} diff --git a/bcel-builder/src/test/java/org/aspectj/apache/bcel/classfile/tests/GetReflectMembersTest.java b/bcel-builder/src/test/java/org/aspectj/apache/bcel/classfile/tests/GetReflectMembersTest.java new file mode 100644 index 000000000..06bd9ccc8 --- /dev/null +++ b/bcel-builder/src/test/java/org/aspectj/apache/bcel/classfile/tests/GetReflectMembersTest.java @@ -0,0 +1,61 @@ +/* ******************************************************************* + * Copyright (c) 2005 Contributors. + * 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://eclipse.org/legal/epl-v10.html + * + * Contributors: + * Adrian Colyer Initial implementation + * ******************************************************************/ +package org.aspectj.apache.bcel.classfile.tests; + +import org.aspectj.apache.bcel.classfile.JavaClass; +import org.aspectj.apache.bcel.util.ClassLoaderRepository; +import org.aspectj.apache.bcel.util.Repository; + +import junit.framework.TestCase; + +/** + * @author colyer + * + */ +public class GetReflectMembersTest extends TestCase { + + private Repository bcelRepository; + private JavaClass jc; + + public void testGetMethod() throws Exception { + assertNotNull(jc.getMethod(GetMe.class.getMethod("foo",new Class[] {String.class}))); + } + + public void testGetConstructor() throws Exception { + assertNotNull(jc.getMethod(GetMe.class.getConstructor(new Class[] {int.class}))); + } + + public void testGetField() throws Exception { + assertNotNull(jc.getField(GetMe.class.getDeclaredField("x"))); + } + + protected void setUp() throws Exception { + super.setUp(); + this.bcelRepository = new ClassLoaderRepository(getClass().getClassLoader()); + this.jc = bcelRepository.loadClass(GetMe.class); + } + + protected void tearDown() throws Exception { + super.tearDown(); + this.bcelRepository.clear(); + } + + private static class GetMe { + + private int x; + + public GetMe(int x) { this.x = x;} + + public void foo(String s) {}; + + } +} diff --git a/bcel-builder/src/test/java/org/aspectj/apache/bcel/classfile/tests/LocalVariableTypeTableTest.java b/bcel-builder/src/test/java/org/aspectj/apache/bcel/classfile/tests/LocalVariableTypeTableTest.java new file mode 100644 index 000000000..41eba95e0 --- /dev/null +++ b/bcel-builder/src/test/java/org/aspectj/apache/bcel/classfile/tests/LocalVariableTypeTableTest.java @@ -0,0 +1,72 @@ +/* ******************************************************************* + * Copyright (c) 2004 IBM + * 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: + * Andy Clement - initial implementation + * ******************************************************************/ + +package org.aspectj.apache.bcel.classfile.tests; + +import org.aspectj.apache.bcel.classfile.Code; +import org.aspectj.apache.bcel.classfile.JavaClass; +import org.aspectj.apache.bcel.classfile.LocalVariable; +import org.aspectj.apache.bcel.classfile.LocalVariableTypeTable; +import org.aspectj.apache.bcel.classfile.Method; +import org.aspectj.apache.bcel.classfile.Utility; + + +public class LocalVariableTypeTableTest extends BcelTestCase { + + + protected void setUp() throws Exception { + super.setUp(); + } + + /** + * Check the local variable type table includes information about generic signatures. + */ + public void testLocalVariableTypeTableAttribute() throws ClassNotFoundException { + JavaClass clazz = getClassFromJar("SimpleGenericsProgram"); + + Method mainMethod = getMethod(clazz,"main"); + Code codeAttr = (Code) findAttribute("Code",mainMethod.getAttributes()); + LocalVariableTypeTable localVariableTypeTable = + (LocalVariableTypeTable) findAttribute("LocalVariableTypeTable",codeAttr.getAttributes()); + + assertTrue("Should be two entries in the LocalVariableTypeTable but found "+localVariableTypeTable.getTableLength(), + localVariableTypeTable.getTableLength()==2); + + LocalVariable[] lvtable = localVariableTypeTable.getLocalVariableTypeTable(); + boolean tc1OK = false; + boolean tc2OK = false; + String errormessage = null; + for (int i = 0; i < lvtable.length; i++) { + String sig = Utility.signatureToString(lvtable[i].getSignature()); + if (lvtable[i].getName().equals("tc1")) { + if (!sig.equals("TreasureChest<String>")) { + errormessage="Expected signature of 'TreasureChest<String>' for tc1 but got "+sig; + } else { + tc1OK = true; + } + } + if (lvtable[i].getName().equals("tc2")) { + if (!sig.equals("TreasureChest<Integer>")) { + errormessage="Expected signature of 'TreasureChest<Integer>' for tc2 but got "+sig; + } else { + tc2OK = true; + } + } + } + if (!tc1OK || !tc2OK) fail(errormessage); + } + + protected void tearDown() throws Exception { + super.tearDown(); + } + +} diff --git a/bcel-builder/src/test/java/org/aspectj/apache/bcel/classfile/tests/MethodAnnotationsTest.java b/bcel-builder/src/test/java/org/aspectj/apache/bcel/classfile/tests/MethodAnnotationsTest.java new file mode 100644 index 000000000..14d009cf0 --- /dev/null +++ b/bcel-builder/src/test/java/org/aspectj/apache/bcel/classfile/tests/MethodAnnotationsTest.java @@ -0,0 +1,107 @@ +/* ******************************************************************* + * Copyright (c) 2004 IBM + * 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: + * Andy Clement - initial implementation + * ******************************************************************/ + +package org.aspectj.apache.bcel.classfile.tests; + +import java.io.File; +import java.io.IOException; +import java.util.Iterator; + +import org.aspectj.apache.bcel.classfile.JavaClass; +import org.aspectj.apache.bcel.classfile.Method; +import org.aspectj.apache.bcel.classfile.annotation.AnnotationGen; +import org.aspectj.apache.bcel.classfile.annotation.NameValuePair; +import org.aspectj.apache.bcel.util.SyntheticRepository; + + +public class MethodAnnotationsTest extends BcelTestCase { + + + protected void setUp() throws Exception { + super.setUp(); + } + + public void testMethodAnnotations() throws ClassNotFoundException { + JavaClass clazz = getClassFromJar("AnnotatedMethods"); + + checkAnnotatedMethod(clazz,"method1","SimpleAnnotation","id","1"); + checkAnnotatedMethod(clazz,"method2","SimpleAnnotation","id","2"); + + } + + public void testMethodAnnotationsReadWrite() throws ClassNotFoundException,IOException { + JavaClass clazz = getClassFromJar("AnnotatedMethods"); + + checkAnnotatedMethod(clazz,"method1","SimpleAnnotation","id","1"); + checkAnnotatedMethod(clazz,"method2","SimpleAnnotation","id","2"); + + // Write it out + File tfile = createTestdataFile("AnnotatedMethods.class"); + clazz.dump(tfile); + + SyntheticRepository repos2 = createRepos("."); + JavaClass clazz2 = repos2.loadClass("AnnotatedMethods"); + + checkAnnotatedMethod(clazz,"method1","SimpleAnnotation","id","1"); + checkAnnotatedMethod(clazz,"method2","SimpleAnnotation","id","2"); + + assertTrue(tfile.delete()); + } + + // helper methods + + public void checkAnnotatedMethod(JavaClass clazz,String methodname, + String annotationName,String annotationElementName,String annotationElementValue) { + Method[] methods = clazz.getMethods(); + + for (int i = 0; i < methods.length; i++) { + Method m = methods[i]; + AnnotationGen[] methodAnnotations = m.getAnnotations(); + if (m.getName().equals(methodname)) { + checkAnnotation(methodAnnotations[0],annotationName,annotationElementName,annotationElementValue); + + } + } + } + + private void checkAnnotation(AnnotationGen a,String name,String elementname,String elementvalue) { + assertTrue("Expected annotation to have name "+name+" but it had name "+a.getTypeName(), + a.getTypeName().equals(name)); + assertTrue("Expected annotation to have one element but it had "+a.getValues().size(),a.getValues().size()==1); + NameValuePair envp = a.getValues().get(0); + assertTrue("Expected element name "+elementname+" but was "+envp.getNameString(), + elementname.equals(envp.getNameString())); + assertTrue("Expected element value "+elementvalue+" but was "+envp.getValue().stringifyValue(), + elementvalue.equals(envp.getValue().stringifyValue())); + } + + + // helper methods + + public void checkValue(AnnotationGen a,String name,String tostring) { + for (Iterator<NameValuePair> i = a.getValues().iterator(); i.hasNext();) { + NameValuePair element = i.next(); + if (element.getNameString().equals(name)) { + if (!element.getValue().stringifyValue().equals(tostring)) { + fail("Expected element "+name+" to have value "+tostring+" but it had value "+element.getValue().stringifyValue()); + } + return; + } + } + fail("Didnt find named element "+name); + } + + protected void tearDown() throws Exception { + super.tearDown(); + } + +} diff --git a/bcel-builder/src/test/java/org/aspectj/apache/bcel/classfile/tests/MethodParametersTest.java b/bcel-builder/src/test/java/org/aspectj/apache/bcel/classfile/tests/MethodParametersTest.java new file mode 100644 index 000000000..76a9b84d8 --- /dev/null +++ b/bcel-builder/src/test/java/org/aspectj/apache/bcel/classfile/tests/MethodParametersTest.java @@ -0,0 +1,86 @@ +/* ******************************************************************* + * Copyright (c) 2013 VMware + * 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: + * Andy Clement - initial implementation + * ******************************************************************/ +package org.aspectj.apache.bcel.classfile.tests; + +import org.aspectj.apache.bcel.Constants; +import org.aspectj.apache.bcel.classfile.JavaClass; +import org.aspectj.apache.bcel.classfile.Method; +import org.aspectj.apache.bcel.classfile.MethodParameters; + +public class MethodParametersTest extends BcelTestCase { + + protected void setUp() throws Exception { + super.setUp(); + } + + public void testMethodParameters1() throws Exception { + JavaClass jc = getClassFromJava8Jar("Parameters"); + Method m = getMethod(jc, "foo"); + MethodParameters mp = (MethodParameters)getAttribute(m.getAttributes(),Constants.ATTR_METHOD_PARAMETERS); + assertEquals(3,mp.getParametersCount()); + assertEquals("abc",mp.getParameterName(0)); + assertEquals("def",mp.getParameterName(1)); + assertEquals("ghi",mp.getParameterName(2)); + assertFalse(mp.isFinal(0)); + assertFalse(mp.isSynthetic(0)); + assertFalse(mp.isMandated(0)); + } + + // this method specifies the receiver + public void testMethodParameters2() throws Exception { + JavaClass jc = getClassFromJava8Jar("Parameters"); + Method m = getMethod(jc, "bar"); + MethodParameters mp = (MethodParameters)getAttribute(m.getAttributes(),Constants.ATTR_METHOD_PARAMETERS); + assertEquals(1,mp.getParametersCount()); + assertEquals("abc",mp.getParameterName(0)); + assertFalse(mp.isFinal(0)); + assertFalse(mp.isSynthetic(0)); + assertFalse(mp.isMandated(0)); + } + + // access flags + public void testMethodParameters3() throws Exception { + JavaClass jc = getClassFromJava8Jar("Parameters$Inner"); + Method m = getMethod(jc, "<init>"); + MethodParameters mp = (MethodParameters)getAttribute(m.getAttributes(),Constants.ATTR_METHOD_PARAMETERS); + assertEquals(2,mp.getParametersCount()); + + assertEquals("this$0",mp.getParameterName(0)); + assertTrue(mp.isFinal(0)); + assertFalse(mp.isSynthetic(0)); + assertTrue(mp.isMandated(0)); + + assertEquals("x",mp.getParameterName(1)); + assertFalse(mp.isFinal(1)); + assertFalse(mp.isSynthetic(1)); + assertFalse(mp.isMandated(1)); + } + + // access flags + public void testMethodParameters4() throws Exception { + JavaClass jc = getClassFromJava8Jar("Parameters$Color"); + Method m = getMethod(jc, "<init>"); + MethodParameters mp = (MethodParameters)getAttribute(m.getAttributes(),Constants.ATTR_METHOD_PARAMETERS); + assertEquals(2,mp.getParametersCount()); + + assertEquals("$enum$name",mp.getParameterName(0)); + assertFalse(mp.isFinal(0)); + assertTrue(mp.isSynthetic(0)); + assertFalse(mp.isMandated(0)); + + assertEquals("$enum$ordinal",mp.getParameterName(1)); + assertFalse(mp.isFinal(1)); + assertTrue(mp.isSynthetic(1)); + assertFalse(mp.isMandated(1)); + } + +}
\ No newline at end of file diff --git a/bcel-builder/src/test/java/org/aspectj/apache/bcel/classfile/tests/ModuleTest.java b/bcel-builder/src/test/java/org/aspectj/apache/bcel/classfile/tests/ModuleTest.java new file mode 100644 index 000000000..454817df8 --- /dev/null +++ b/bcel-builder/src/test/java/org/aspectj/apache/bcel/classfile/tests/ModuleTest.java @@ -0,0 +1,136 @@ +/* ******************************************************************* + * Copyright (c) 2016-2017 Contributors + * 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 + * ******************************************************************/ + +package org.aspectj.apache.bcel.classfile.tests; + +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; + +import org.aspectj.apache.bcel.Constants; +import org.aspectj.apache.bcel.classfile.Attribute; +import org.aspectj.apache.bcel.classfile.ClassParser; +import org.aspectj.apache.bcel.classfile.JavaClass; +import org.aspectj.apache.bcel.classfile.Module; +import org.aspectj.apache.bcel.classfile.Module.Export; +import org.aspectj.apache.bcel.classfile.Module.Open; +import org.aspectj.apache.bcel.classfile.Module.Provide; +import org.aspectj.apache.bcel.classfile.Module.Require; +import org.aspectj.apache.bcel.classfile.Module.Uses; +import org.aspectj.apache.bcel.classfile.SourceFile; + +/** + * http://cr.openjdk.java.net/~mr/jigsaw/spec/lang-vm.html + * + * @author Andy Clement + */ +public class ModuleTest extends BcelTestCase { + + public void testLoadSimpleModuleClass() throws Exception { + String moduleFilename = "testdata/modules/one/module-info.class"; + ClassParser classParser = new ClassParser(moduleFilename); + JavaClass javaClass = classParser.parse(); + assertNotNull(javaClass); + assertEquals(Constants.MAJOR_1_9,javaClass.getMajor()); + assertEquals(Constants.MINOR_1_9,javaClass.getMinor()); + assertEquals(Constants.ACC_MODULE,javaClass.getModifiers()); + assertEquals(0,javaClass.getSuperclassNameIndex()); + assertEquals(0,javaClass.getInterfaceIndices().length); + assertEquals(0,javaClass.getFields().length); + assertEquals(0,javaClass.getMethods().length); + Attribute[] attrs = javaClass.getAttributes(); + assertEquals(2,attrs.length); + SourceFile sourceFile = (SourceFile) getAttribute(attrs,Constants.ATTR_SOURCE_FILE); + Module moduleAttr = (Module) getAttribute(attrs, Constants.ATTR_MODULE); + byte[] originalData = moduleAttr.getBytes(); + String[] requiredModuleNames = moduleAttr.getRequiredModuleNames(); + assertEquals(1,requiredModuleNames.length); + assertEquals("java.base",requiredModuleNames[0]); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + moduleAttr.dump(new DataOutputStream(baos)); + byte[] newData = baos.toByteArray(); + // The 6 offset here is because the newdata includes the 2byte cpool pointer for the name 'Module' + // and the 4byte int length field for the attribute data + if (newData.length!=originalData.length+6) { + fail("Expected the length of the original attribute ("+originalData.length+") to match the new written length ("+newData.length+")"); + } + for (int i=0;i<originalData.length;i++) { + if (originalData[i]!=newData[i+6]) { + fail("byte mismatch at position "+i+" of "+newData.length); + } + } + } + + + public void testRequires() throws Exception { + Module moduleAttr = getModuleAttribute("testdata/modules/two/d/module-info.class"); + Require[] requires = moduleAttr.getRequires(); + assertEquals(4, requires.length); + assertEquals("requires mandated java.base 9",requires[0].toString()); + assertEquals("requires a.b.c",requires[1].toString()); + assertEquals("requires static b.c.d",requires[2].toString()); + assertEquals("requires transitive c.d.e",requires[3].toString()); + assertEquals("java.base",requires[0].getModuleName()); + assertEquals("a.b.c",requires[1].getModuleName()); + assertEquals("b.c.d",requires[2].getModuleName()); + assertEquals("c.d.e",requires[3].getModuleName()); + } + + public void testExports() throws Exception { + Module moduleAttr = getModuleAttribute("testdata/modules/two/e/module-info.class"); + Export[] exports = moduleAttr.getExports(); + assertEquals(3, exports.length); + assertEquals("exports com.foo1", exports[0].toString()); + assertEquals("exports com.foo2 to a.b.c",exports[1].toString()); + assertEquals("exports com.foo3 to a.b.c, b.c.d",exports[2].toString()); + assertEquals("com/foo1",exports[0].getPackage()); + assertEquals("com/foo2",exports[1].getPackage()); + assertEquals("com/foo3",exports[2].getPackage()); + assertEquals("a.b.c",exports[1].getToModuleNames()[0]); + assertEquals("a.b.c",exports[2].getToModuleNames()[0]); + assertEquals("b.c.d",exports[2].getToModuleNames()[1]); + } + + public void testOpens() throws Exception { + Module moduleAttr = getModuleAttribute("testdata/modules/two/h/module-info.class"); + Open[] opens = moduleAttr.getOpens(); + assertEquals(3, opens.length); + assertEquals("opens com.foo1", opens[0].toString()); + assertEquals("opens com.foo2 to a.b.c", opens[1].toString()); + assertEquals("opens com.foo3 to a.b.c, b.c.d", opens[2].toString()); + } + + public void testUses() throws Exception { + Module moduleAttr = getModuleAttribute("testdata/modules/two/f/module-info.class"); + Uses[] uses = moduleAttr.getUses(); + assertEquals(1,uses.length); + assertEquals("com/foo1/I1",uses[0].getTypeName()); + assertEquals("uses com.foo1.I1",uses[0].toString()); + } + + public void testProvides() throws Exception { + Module moduleAttr = getModuleAttribute("testdata/modules/two/g/module-info.class"); + Provide[] provides = moduleAttr.getProvides(); + assertEquals(2,provides.length); + assertEquals("provides com.foo1.I1 with com.foo1.C1",provides[0].toString()); + assertEquals("provides com.foo2.I2 with com.foo2.C2",provides[1].toString()); + assertEquals("com/foo1/I1",provides[0].getProvidedType()); + assertEquals("com/foo1/C1",provides[0].getWithTypeStrings()[0]); + assertEquals("com/foo2/I2",provides[1].getProvidedType()); + assertEquals("com/foo2/C2",provides[1].getWithTypeStrings()[0]); + } + + // --- + + private Module getModuleAttribute(String moduleInfoClass) throws Exception { + ClassParser classParser = new ClassParser(moduleInfoClass); + JavaClass javaClass = classParser.parse(); + return (Module)getAttribute(javaClass.getAttributes(), Constants.ATTR_MODULE); + } + +} diff --git a/bcel-builder/src/test/java/org/aspectj/apache/bcel/classfile/tests/NonCachingClassLoaderRepositoryTest.java b/bcel-builder/src/test/java/org/aspectj/apache/bcel/classfile/tests/NonCachingClassLoaderRepositoryTest.java new file mode 100644 index 000000000..4b7a14715 --- /dev/null +++ b/bcel-builder/src/test/java/org/aspectj/apache/bcel/classfile/tests/NonCachingClassLoaderRepositoryTest.java @@ -0,0 +1,149 @@ +/* ==================================================================== + * The Apache Software License, Version 1.1 + * + * Copyright (c) 2001 The Apache Software Foundation. All rights + * reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, + * if any, must include the following acknowledgment: + * "This product includes software developed by the + * Apache Software Foundation (http://www.apache.org/)." + * Alternately, this acknowledgment may appear in the software itself, + * if and wherever such third-party acknowledgments normally appear. + * + * 4. The names "Apache" and "Apache Software Foundation" and + * "Apache BCEL" must not be used to endorse or promote products + * derived from this software without prior written permission. For + * written permission, please contact apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache", + * "Apache BCEL", nor may "Apache" appear in their name, without + * prior written permission of the Apache Software Foundation. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * <http://www.apache.org/>. + */ + +package org.aspectj.apache.bcel.classfile.tests; + +import java.util.ArrayList; +import java.util.List; + +import junit.framework.TestCase; + +import org.aspectj.apache.bcel.classfile.JavaClass; +import org.aspectj.apache.bcel.util.NonCachingClassLoaderRepository; + +/** + * @author Kristian Rosenvold + */ +public class NonCachingClassLoaderRepositoryTest extends TestCase { + + private final NonCachingClassLoaderRepository nonCachingClassLoaderRepository = new NonCachingClassLoaderRepository( + NonCachingClassLoaderRepositoryTest.class.getClassLoader()); + + protected void setUp() throws Exception { + super.setUp(); + } + + abstract class DoneChecker implements Runnable { + private volatile boolean success = false; + private volatile boolean done = false; + + public boolean isSuccess() { + return success; + } + + public boolean isDone() { + return done; + } + + protected void setDone(boolean successFully) { + success = successFully; + done = true; + } + + public abstract void run(); + } + + class Loader extends DoneChecker implements Runnable { + public void run() { + try { + JavaClass javaClass = nonCachingClassLoaderRepository.loadClass(NonCachingClassLoaderRepositoryTest.class + .getCanonicalName()); + nonCachingClassLoaderRepository.clear(); + setDone(true); + } catch (Throwable e) { + e.printStackTrace(System.out); + setDone(false); + } + } + } + + class Clearer extends DoneChecker implements Runnable { + public void run() { + try { + nonCachingClassLoaderRepository.clear(); + setDone(true); + } catch (Throwable e) { + e.printStackTrace(System.out); + setDone(false); + } + } + } + + public void testConcurrency() throws ClassNotFoundException, InterruptedException { + List<DoneChecker> loaders = new ArrayList<DoneChecker>(); + int i1 = 1000; + for (int i = 0; i < i1; i++) { + DoneChecker loader = new Loader(); + loaders.add(loader); + new Thread(loader).start(); + DoneChecker clearer = new Clearer(); + loaders.add(clearer); + new Thread(clearer).start(); + } + + for (int i = 0; i < i1 * 2; i++) { + DoneChecker loader = loaders.get(i); + while (!loader.isDone()) { + Thread.sleep(10); + } + assertTrue("Loader " + i + " is supposed to run successfully", loader.isSuccess()); + } + + } + + protected void tearDown() throws Exception { + super.tearDown(); + } + +} diff --git a/bcel-builder/src/test/java/org/aspectj/apache/bcel/classfile/tests/ParameterAnnotationsTest.java b/bcel-builder/src/test/java/org/aspectj/apache/bcel/classfile/tests/ParameterAnnotationsTest.java new file mode 100644 index 000000000..093a4b6e4 --- /dev/null +++ b/bcel-builder/src/test/java/org/aspectj/apache/bcel/classfile/tests/ParameterAnnotationsTest.java @@ -0,0 +1,590 @@ +/******************************************************************************* + * Copyright (c) 2004 IBM 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: Andy Clement - initial implementation + ******************************************************************************/ + +package org.aspectj.apache.bcel.classfile.tests; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +import org.aspectj.apache.bcel.Constants; +import org.aspectj.apache.bcel.classfile.Attribute; +import org.aspectj.apache.bcel.classfile.ConstantPool; +import org.aspectj.apache.bcel.classfile.JavaClass; +import org.aspectj.apache.bcel.classfile.Method; +import org.aspectj.apache.bcel.classfile.annotation.AnnotationElementValue; +import org.aspectj.apache.bcel.classfile.annotation.AnnotationGen; +import org.aspectj.apache.bcel.classfile.annotation.ArrayElementValue; +import org.aspectj.apache.bcel.classfile.annotation.NameValuePair; +import org.aspectj.apache.bcel.classfile.annotation.ElementValue; +import org.aspectj.apache.bcel.classfile.annotation.SimpleElementValue; +import org.aspectj.apache.bcel.generic.ArrayType; +import org.aspectj.apache.bcel.generic.ClassGen; +import org.aspectj.apache.bcel.generic.InstructionBranch; +import org.aspectj.apache.bcel.generic.InstructionConstants; +import org.aspectj.apache.bcel.generic.InstructionFactory; +import org.aspectj.apache.bcel.generic.InstructionHandle; +import org.aspectj.apache.bcel.generic.InstructionLV; +import org.aspectj.apache.bcel.generic.InstructionList; +import org.aspectj.apache.bcel.generic.LocalVariableGen; +import org.aspectj.apache.bcel.generic.MethodGen; +import org.aspectj.apache.bcel.generic.ObjectType; +import org.aspectj.apache.bcel.generic.Type; +import org.aspectj.apache.bcel.util.SyntheticRepository; + +/** + * The program that some of the tests generate looks like this: + public class HelloWorld { + public static void main(String[] argv) { + BufferedReader in = new BufferedReader(new InputStreamReader(System.in)); + String name = null; + try { + name = "Andy"; + } catch(IOException e) { return; } + System.out.println("Hello, " + name); + } + } + * + */ +public class ParameterAnnotationsTest extends BcelTestCase { + + protected void setUp() throws Exception { + super.setUp(); + } + + /** + * Programmatically construct a class and add an annotation to the main method parameter 'argv' + */ + public void testParameterAnnotations_builtOK() { + ClassGen clg = createClassGen("HelloWorld"); + ConstantPool cpg = clg.getConstantPool(); + InstructionList il = new InstructionList(); + + buildClassContentsWithAnnotatedMethods(clg,cpg,il,true); + + int i = clg.getMethods().length; + assertTrue("Class should have 2 methods but has "+i,i==2); + + Method mainMethod = clg.getMethods()[0]; + AnnotationGen[] annos = mainMethod.getAnnotationsOnParameter(0); + assertTrue("Should be two annotation on the 'argv' parameter to main() but there are "+annos.length,annos.length==2); + assertTrue("This annotation should contain the string 'fruit=Apples' but it is "+annos[0].toString(), + annos[0].toString().indexOf("fruit=Apples")!=-1); + assertTrue("This annotation should contain the string 'fruit=Oranges' but it is "+annos[1].toString(), + annos[1].toString().indexOf("fruit=Oranges")!=-1); + } + + + + /** + * Check we can save and load a constructed class that contains parameter annotations + */ + public void testParameterAnnotations_savedAndLoadedOK() throws ClassNotFoundException { + ClassGen clg = createClassGen("HelloWorld"); + ConstantPool cpg = clg.getConstantPool(); + InstructionList il = new InstructionList(); + + buildClassContentsWithAnnotatedMethods(clg,cpg,il,true); + + dumpClass(clg,"temp5","HelloWorld.class"); + + JavaClass jc = getClassFrom("temp5","HelloWorld"); + + clg = new ClassGen(jc); + + int i = clg.getMethods().length; + assertTrue("Class should have 2 methods but has "+i,i==2); + + Method mainMethod = clg.getMethods()[0]; + AnnotationGen[] annos = mainMethod.getAnnotationsOnParameter(0); + assertTrue("Should be two annotation on the 'argv' parameter to main() but there are "+annos.length,annos.length==2); + assertTrue("This annotation should contain the string 'fruit=Apples' but it is "+annos[0].toString(), + annos[0].toString().indexOf("fruit=Apples")!=-1); + assertTrue("This annotation should contain the string 'fruit=Oranges' but it is "+annos[1].toString(), + annos[1].toString().indexOf("fruit=Oranges")!=-1); + assertTrue(wipe("temp5","HelloWorld.class")); + + } + + + + /* + * Load an existing class, add new parameter annotations, save and then reload it + */ + public void testParameterAnnotations_loadedThenModifiedThenSavedAndLoadedOK() throws ClassNotFoundException { + JavaClass jc = getClassFrom("testcode.jar","AnnotatedParameters"); + + ClassGen clg = new ClassGen(jc); + ConstantPool cpg = clg.getConstantPool(); + + // + // Foo method looks like this: + // public void foo(@SimpleAnnotation(id=2) int arg1, + // @SimpleAnnotation(id=3) @AnnotationEnumElement(enumval=SimpleEnum.Red) String arg2) + Method m = findMethod(clg,"foo"); + assertTrue("Should be able to find method foo but couldn't",m!=null); + + + /////////////////////// 1. Check the right number of annotations are there + int i = m.getAnnotationsOnParameter(1).length; + assertTrue("Should be two annotations on the second parameter but found: "+i,i==2); + + + /////////////////////// 2. Let's add a new parameter annotation, a visible one, to the first parameter. + + // Build a modifiable version of the foo method + MethodGen mg = new MethodGen(m,clg.getClassName(),cpg); + + // Check the annotations survived that transform + i = mg.getAnnotationsOnParameter(1).size(); + assertTrue("Should be two annotations on the second parameter but found: "+i,i==2); + + // That worked, so let's add a new parameter annotation + mg.addParameterAnnotation(0,createFruitAnnotation(cpg,"Banana",true)); + + // Foo method should now look like this: + // public void foo(@SimpleAnnotation(id=2) @SimpleStringAnnotation(fruit=Banana) int arg1, + // @SimpleAnnotation(id=3) @AnnotationEnumElement(enumval=SimpleEnum.Red) String arg2) + i = mg.getAnnotationsOnParameter(0).size(); + assertTrue("Should now be 2 parameter annotations but found "+i,i==2); + i = mg.getAnnotationsOnParameter(0).get(1).toString().indexOf("fruit=Banana"); + assertTrue("Expected 'fruit=Banana' in the 2nd annotation on the first argument but got "+ + mg.getAnnotationsOnParameter(0).get(1).toString(),i!=-1); + + // delete the old method and add the new one + clg.removeMethod(m); + clg.addMethod(mg.getMethod()); + + /////////////////////// 3. Dump it to disk + dumpClass(clg,"temp2","AnnotatedParameters.class"); + + /////////////////////// 4. Load it back in and verify the annotations persisted + JavaClass jc2 = getClassFrom("temp2","AnnotatedParameters"); + + m = jc2.getMethods()[2]; + AnnotationGen[] p1annotations = m.getAnnotationsOnParameter(0); + AnnotationGen[] p2annotations = m.getAnnotationsOnParameter(1); + + assertTrue("Expected two annotations on the first parameter but found "+p1annotations.length,p1annotations.length==2); + assertTrue("Expected two annotations on the second parameter but found "+p2annotations.length,p2annotations.length==2); + String expectedString = "[@SimpleAnnotation(id=2),@SimpleStringAnnotation(fruit=Banana)]"; + assertTrue("Expected formatted short string of '"+expectedString+"' but it was '"+dumpAnnotations(p1annotations)+"'", + dumpAnnotations(p1annotations).equals(expectedString)); + expectedString = "[@SimpleAnnotation(id=3),@AnnotationEnumElement(enumval=LSimpleEnum;Red)]"; + assertTrue("Expected formatted short string of '"+expectedString+"' but it was '"+dumpAnnotations(p2annotations)+"'", + dumpAnnotations(p2annotations).equals(expectedString)); + + assertTrue(wipe("temp2","AnnotatedParameters.class")); + } + + + /** + * same as above test but attaching invisible runtime parameter annotations + */ + public void testParameterAnnotations_loadedThenModifiedWithInvisibleAnnotationThenSavedAndLoadedOK() throws ClassNotFoundException { + JavaClass jc = getClassFrom("testcode.jar","AnnotatedParameters"); + ClassGen clg = new ClassGen(jc); + ConstantPool cpg = clg.getConstantPool(); + + // + // Foo method looks like this: + // public void foo(@SimpleAnnotation(id=2) int arg1, + // @SimpleAnnotation(id=3) @AnnotationEnumElement(enumval=SimpleEnum.Red) String arg2) + Method m = findMethod(clg,"foo"); + assertTrue("Should be able to find method foo but couldn't",m!=null); + + + /////////////////////// 1. Check the right number of annotations are there + int i = m.getAnnotationsOnParameter(1).length; + assertTrue("Should be two annotations on the second parameter but found: "+i,i==2); + + + /////////////////////// 2. Let's add a new parameter annotation, a visible one, to the first parameter. + + // Build a modifiable version of the foo method + MethodGen mg = new MethodGen(m,clg.getClassName(),cpg); + + // Check the annotations survived that transform + i = mg.getAnnotationsOnParameter(1).size(); + assertTrue("Should be two annotations on the second parameter but found: "+i,i==2); + + // That worked, so let's add a new parameter annotation + mg.addParameterAnnotation(0,createFruitAnnotation(cpg,"Banana",false)); + + // Foo method should now look like this: + // public void foo(@SimpleAnnotation(id=2) @SimpleStringAnnotation(fruit=Banana) int arg1, + // @SimpleAnnotation(id=3) @AnnotationEnumElement(enumval=SimpleEnum.Red) String arg2) + i = mg.getAnnotationsOnParameter(0).size(); + assertTrue("Should now be 2 parameter annotations but found "+i,i==2); + i = mg.getAnnotationsOnParameter(0).get(1).toString().indexOf("fruit=Banana"); + assertTrue("Expected 'fruit=Banana' in the 2nd annotation on the first argument but got "+ + mg.getAnnotationsOnParameter(0).get(1).toString(),i!=-1); + assertTrue("New annotation should be runtime invisible?",!((AnnotationGen)mg.getAnnotationsOnParameter(0).get(1)).isRuntimeVisible()); + + // delete the old method and add the new one + clg.removeMethod(m); + clg.addMethod(mg.getMethod()); + + /////////////////////// 3. Dump it to disk + dumpClass(clg,"temp3","AnnotatedParameters.class"); + + /////////////////////// 4. Load it back in and verify the annotations persisted + + JavaClass jc2 = getClassFrom("temp3","AnnotatedParameters"); + + m = jc2.getMethods()[2]; + AnnotationGen[] p1annotations = m.getAnnotationsOnParameter(0); + AnnotationGen[] p2annotations = m.getAnnotationsOnParameter(1); + + assertTrue("Expected two annotations on the first parameter but found "+p1annotations.length,p1annotations.length==2); + assertTrue("Expected two annotations on the second parameter but found "+p2annotations.length,p2annotations.length==2); + String expectedString = "[@SimpleAnnotation(id=2),@SimpleStringAnnotation(fruit=Banana)]"; + assertTrue("Expected formatted short string of '"+expectedString+"' but it was '"+dumpAnnotations(p1annotations)+"'", + dumpAnnotations(p1annotations).equals(expectedString)); + expectedString = "[@SimpleAnnotation(id=3),@AnnotationEnumElement(enumval=LSimpleEnum;Red)]"; + assertTrue("Expected formatted short string of '"+expectedString+"' but it was '"+dumpAnnotations(p2annotations)+"'", + dumpAnnotations(p2annotations).equals(expectedString)); + + assertTrue("Second annotation on first parameter should be runtime invisible?", + !p1annotations[1].isRuntimeVisible()); + assertTrue(wipe("temp3","AnnotatedParameters.class")); + + + // 5. Verify that when annotations for parameters are unpacked from attributes, the + // attributes vanish ! + clg = new ClassGen(jc2); + mg = new MethodGen(m,clg.getClassName(),clg.getConstantPool()); + List<Attribute> as = mg.getAttributes(); + assertTrue("Should be 2 (RIPA and RVPA) but there are "+mg.getAttributes().size(),mg.getAttributes().size()==2); + List<AnnotationGen> l = mg.getAnnotationsOnParameter(0); + assertTrue("Should be 2 annotations on first parameter but there is only "+l.size()+":"+l.toString(), + l.size()==2); + assertTrue("Should be 0 but there are "+mg.getAttributes().size(),mg.getAttributes().size()==0); + } + + + private Method findMethod(ClassGen c,String mname) { + Method[] ms = c.getMethods(); + for (int i = 0; i < ms.length; i++) { + if (ms[i].getName().equals(mname)) return ms[i]; + } + return null; + } + + private void dumpClass(ClassGen cg, String fname) { + try { + File f = createTestdataFile(fname); + cg.getJavaClass().dump(f); + } catch (java.io.IOException e) { + System.err.println(e); + } + } + + + private void dumpClass(ClassGen cg, String dir, String fname) { + dumpClass(cg,dir+File.separator+fname); + } + + private void buildClassContentsWithAnnotatedMethods(ClassGen cg, ConstantPool cp, InstructionList il,boolean addParameterAnnotations) { + // Create method 'public static void main(String[]argv)' + MethodGen mg = createMethodGen("main",il,cp); + InstructionFactory factory = new InstructionFactory(cg); + mg.addAnnotation(createSimpleVisibleAnnotation(mg.getConstantPool())); + // We now define some often used types: + + ObjectType i_stream = new ObjectType("java.io.InputStream"); + ObjectType p_stream = new ObjectType("java.io.PrintStream"); + + // Create variables in and name : We call the constructors, i.e., + // execute BufferedReader(InputStreamReader(System.in)) . The reference + // to the BufferedReader object stays on top of the stack and is stored + // in the newly allocated in variable. + + il.append(factory.createNew("java.io.BufferedReader")); + il.append(InstructionConstants.DUP); // Use predefined constant + il.append(factory.createNew("java.io.InputStreamReader")); + il.append(InstructionConstants.DUP); + il.append(factory.createFieldAccess("java.lang.System", "in", i_stream,Constants.GETSTATIC)); + il.append(factory.createInvoke("java.io.InputStreamReader", "<init>", + Type.VOID, new Type[] { i_stream }, Constants.INVOKESPECIAL)); + il.append(factory.createInvoke("java.io.BufferedReader", "<init>", + Type.VOID, new Type[] { new ObjectType("java.io.Reader") }, + Constants.INVOKESPECIAL)); + + LocalVariableGen lg = mg.addLocalVariable("in", new ObjectType( + "java.io.BufferedReader"), null, null); + int in = lg.getIndex(); + lg.setStart(il.append(InstructionFactory.createASTORE(in))); // "in" valid from here + + // Create local variable name and initialize it to null + + lg = mg.addLocalVariable("name", Type.STRING, null, null); + int name = lg.getIndex(); + il.append(InstructionConstants.ACONST_NULL); + lg.setStart(il.append(InstructionFactory.createASTORE(name))); // "name" valid from here + + // Create try-catch block: We remember the start of the block, read a + // line from the standard input and store it into the variable name . + +// InstructionHandle try_start = il.append(factory.createFieldAccess( +// "java.lang.System", "out", p_stream, Constants.GETSTATIC)); + +// il.append(new PUSH(cp, "Please enter your name> ")); +// il.append(factory.createInvoke("java.io.PrintStream", "print", +// Type.VOID, new Type[] { Type.STRING }, +// Constants.INVOKEVIRTUAL)); +// il.append(new ALOAD(in)); +// il.append(factory.createInvoke("java.io.BufferedReader", "readLine", +// Type.STRING, Type.NO_ARGS, Constants.INVOKEVIRTUAL)); + InstructionHandle try_start = il.append(InstructionFactory.PUSH(cp,"Andy")); + il.append(InstructionFactory.createASTORE(name)); + + // Upon normal execution we jump behind exception handler, the target + // address is not known yet. + + InstructionBranch g = new InstructionBranch(Constants.GOTO); + InstructionHandle try_end = il.append(g); + + // We add the exception handler which simply returns from the method. + + LocalVariableGen var_ex = mg.addLocalVariable("ex",Type.getType("Ljava.io.IOException;"),null,null); + int var_ex_slot = var_ex.getIndex(); + + InstructionHandle handler = il.append(InstructionFactory.createASTORE(var_ex_slot)); + var_ex.setStart(handler); + var_ex.setEnd(il.append(InstructionConstants.RETURN)); + + mg.addExceptionHandler(try_start, try_end, handler, + new ObjectType("java.io.IOException")); + + // "Normal" code continues, now we can set the branch target of the GOTO + // . + + InstructionHandle ih = il.append(factory.createFieldAccess( + "java.lang.System", "out", p_stream, Constants.GETSTATIC)); + g.setTarget(ih); + + // Printing "Hello": String concatenation compiles to StringBuffer + // operations. + + il.append(factory.createNew(Type.STRINGBUFFER)); + il.append(InstructionConstants.DUP); + il.append(InstructionFactory.PUSH(cp, "Hello, ")); + il + .append(factory.createInvoke("java.lang.StringBuffer", + "<init>", Type.VOID, new Type[] { Type.STRING }, + Constants.INVOKESPECIAL)); + il.append(new InstructionLV(Constants.ALOAD,name)); + il.append(factory.createInvoke("java.lang.StringBuffer", "append", + Type.STRINGBUFFER, new Type[] { Type.STRING }, + Constants.INVOKEVIRTUAL)); + il.append(factory.createInvoke("java.lang.StringBuffer", "toString", + Type.STRING, Type.NO_ARGS, Constants.INVOKEVIRTUAL)); + + il.append(factory.createInvoke("java.io.PrintStream", "println", + Type.VOID, new Type[] { Type.STRING }, + Constants.INVOKEVIRTUAL)); + il.append(InstructionConstants.RETURN); + + // Finalization: Finally, we have to set the stack size, which normally + // would have to be computed on the fly and add a default constructor + // method to the class, which is empty in this case. + + mg.addParameterAnnotation(0,createFruitAnnotation(cp,"Apples",true)); + mg.addParameterAnnotation(0,createFruitAnnotation(cp,"Oranges",true)); + mg.setMaxStack(); + mg.setMaxLocals(); + cg.addMethod(mg.getMethod()); + il.dispose(); // Allow instruction handles to be reused + cg.addEmptyConstructor(Constants.ACC_PUBLIC); + } + + private void buildClassContents(ClassGen cg, ConstantPool cp, InstructionList il) { + // Create method 'public static void main(String[]argv)' + MethodGen mg = createMethodGen("main",il,cp); + InstructionFactory factory = new InstructionFactory(cg); + // We now define some often used types: + + ObjectType i_stream = new ObjectType("java.io.InputStream"); + ObjectType p_stream = new ObjectType("java.io.PrintStream"); + + // Create variables in and name : We call the constructors, i.e., + // execute BufferedReader(InputStreamReader(System.in)) . The reference + // to the BufferedReader object stays on top of the stack and is stored + // in the newly allocated in variable. + + il.append(factory.createNew("java.io.BufferedReader")); + il.append(InstructionConstants.DUP); // Use predefined constant + il.append(factory.createNew("java.io.InputStreamReader")); + il.append(InstructionConstants.DUP); + il.append(factory.createFieldAccess("java.lang.System", "in", i_stream,Constants.GETSTATIC)); + il.append(factory.createInvoke("java.io.InputStreamReader", "<init>", + Type.VOID, new Type[] { i_stream }, Constants.INVOKESPECIAL)); + il.append(factory.createInvoke("java.io.BufferedReader", "<init>", + Type.VOID, new Type[] { new ObjectType("java.io.Reader") }, + Constants.INVOKESPECIAL)); + + LocalVariableGen lg = mg.addLocalVariable("in", new ObjectType( + "java.io.BufferedReader"), null, null); + int in = lg.getIndex(); + lg.setStart(il.append(InstructionFactory.createASTORE(in))); // "in" valid from here + + // Create local variable name and initialize it to null + + lg = mg.addLocalVariable("name", Type.STRING, null, null); + int name = lg.getIndex(); + il.append(InstructionConstants.ACONST_NULL); + lg.setStart(il.append(InstructionFactory.createASTORE(name))); // "name" valid from here + + // Create try-catch block: We remember the start of the block, read a + // line from the standard input and store it into the variable name . + +// InstructionHandle try_start = il.append(factory.createFieldAccess( +// "java.lang.System", "out", p_stream, Constants.GETSTATIC)); + +// il.append(new PUSH(cp, "Please enter your name> ")); +// il.append(factory.createInvoke("java.io.PrintStream", "print", +// Type.VOID, new Type[] { Type.STRING }, +// Constants.INVOKEVIRTUAL)); +// il.append(new ALOAD(in)); +// il.append(factory.createInvoke("java.io.BufferedReader", "readLine", +// Type.STRING, Type.NO_ARGS, Constants.INVOKEVIRTUAL)); + InstructionHandle try_start = il.append(InstructionFactory.PUSH(cp,"Andy")); + il.append(InstructionFactory.createASTORE(name)); + + // Upon normal execution we jump behind exception handler, the target + // address is not known yet. + + InstructionBranch g = new InstructionBranch(Constants.GOTO); + InstructionHandle try_end = il.append(g); + + // We add the exception handler which simply returns from the method. + + LocalVariableGen var_ex = mg.addLocalVariable("ex",Type.getType("Ljava.io.IOException;"),null,null); + int var_ex_slot = var_ex.getIndex(); + + InstructionHandle handler = il.append(InstructionFactory.createASTORE(var_ex_slot)); + var_ex.setStart(handler); + var_ex.setEnd(il.append(InstructionConstants.RETURN)); + + mg.addExceptionHandler(try_start, try_end, handler, + new ObjectType("java.io.IOException")); + + // "Normal" code continues, now we can set the branch target of the GOTO + // . + + InstructionHandle ih = il.append(factory.createFieldAccess( + "java.lang.System", "out", p_stream, Constants.GETSTATIC)); + g.setTarget(ih); + + // Printing "Hello": String concatenation compiles to StringBuffer + // operations. + + il.append(factory.createNew(Type.STRINGBUFFER)); + il.append(InstructionConstants.DUP); + il.append(InstructionFactory.PUSH(cp, "Hello, ")); + il + .append(factory.createInvoke("java.lang.StringBuffer", + "<init>", Type.VOID, new Type[] { Type.STRING }, + Constants.INVOKESPECIAL)); + il.append(InstructionFactory.createALOAD(name)); + il.append(factory.createInvoke("java.lang.StringBuffer", "append", + Type.STRINGBUFFER, new Type[] { Type.STRING }, + Constants.INVOKEVIRTUAL)); + il.append(factory.createInvoke("java.lang.StringBuffer", "toString", + Type.STRING, Type.NO_ARGS, Constants.INVOKEVIRTUAL)); + + il.append(factory.createInvoke("java.io.PrintStream", "println", + Type.VOID, new Type[] { Type.STRING }, + Constants.INVOKEVIRTUAL)); + il.append(InstructionConstants.RETURN); + + // Finalization: Finally, we have to set the stack size, which normally + // would have to be computed on the fly and add a default constructor + // method to the class, which is empty in this case. + + mg.setMaxStack(); + mg.setMaxLocals(); + cg.addMethod(mg.getMethod()); + il.dispose(); // Allow instruction handles to be reused + cg.addEmptyConstructor(Constants.ACC_PUBLIC); + } + + private JavaClass getClassFrom(String where,String clazzname) throws ClassNotFoundException { + SyntheticRepository repos = createRepos(where); + return repos.loadClass(clazzname); + } + + + + + // helper methods + + + private ClassGen createClassGen(String classname) { + return new ClassGen(classname, "java.lang.Object", + "<generated>", Constants.ACC_PUBLIC | Constants.ACC_SUPER, null); + } + + private MethodGen createMethodGen(String methodname,InstructionList il,ConstantPool cp) { + return new MethodGen( + Constants.ACC_STATIC | Constants.ACC_PUBLIC, // access flags + Type.VOID, // return type + new Type[] { new ArrayType(Type.STRING, 1) }, // argument types + new String[] { "argv" }, // arg names + methodname, "HelloWorld", // method, class + il, cp); + } + + + public AnnotationGen createSimpleVisibleAnnotation(ConstantPool cp) { + SimpleElementValue evg = new SimpleElementValue( + ElementValue.PRIMITIVE_INT, cp, 4); + + NameValuePair nvGen = new NameValuePair("id", evg,cp); + + ObjectType t = new ObjectType("SimpleAnnotation"); + + List<NameValuePair> elements = new ArrayList<NameValuePair>(); + elements.add(nvGen); + + AnnotationGen a = new AnnotationGen(t, elements,true, cp); + return a; + } + + public AnnotationGen createCombinedAnnotation(ConstantPool cp) { + // Create an annotation instance + AnnotationGen a = createSimpleVisibleAnnotation(cp); + ArrayElementValue array = new ArrayElementValue(cp); + array.addElement(new AnnotationElementValue(a,cp)); + NameValuePair nvp = new NameValuePair("value",array,cp); + List<NameValuePair> elements = new ArrayList<NameValuePair>(); + elements.add(nvp); + return new AnnotationGen(new ObjectType("CombinedAnnotation"),elements,true,cp); + } + + public AnnotationGen createSimpleInvisibleAnnotation(ConstantPool cp) { + SimpleElementValue evg = new SimpleElementValue( + ElementValue.PRIMITIVE_INT, cp, 4); + + NameValuePair nvGen = new NameValuePair("id", evg,cp); + + ObjectType t = new ObjectType("SimpleAnnotation"); + + List<NameValuePair> elements = new ArrayList<NameValuePair>(); + elements.add(nvGen); + + AnnotationGen a = new AnnotationGen(t, elements,false, cp); + return a; + } + protected void tearDown() throws Exception { + super.tearDown(); + } + +}
\ No newline at end of file diff --git a/bcel-builder/src/test/java/org/aspectj/apache/bcel/classfile/tests/RuntimeVisibleAnnotationAttributeTest.java b/bcel-builder/src/test/java/org/aspectj/apache/bcel/classfile/tests/RuntimeVisibleAnnotationAttributeTest.java new file mode 100644 index 000000000..0f82b2059 --- /dev/null +++ b/bcel-builder/src/test/java/org/aspectj/apache/bcel/classfile/tests/RuntimeVisibleAnnotationAttributeTest.java @@ -0,0 +1,395 @@ +/* ******************************************************************* + * Copyright (c) 2004 IBM + * 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: + * Andy Clement - initial implementation + * ******************************************************************/ + +package org.aspectj.apache.bcel.classfile.tests; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import org.aspectj.apache.bcel.classfile.Attribute; +import org.aspectj.apache.bcel.classfile.ConstantPool; +import org.aspectj.apache.bcel.classfile.JavaClass; +import org.aspectj.apache.bcel.classfile.Utility; +import org.aspectj.apache.bcel.classfile.annotation.AnnotationElementValue; +import org.aspectj.apache.bcel.classfile.annotation.AnnotationGen; +import org.aspectj.apache.bcel.classfile.annotation.ArrayElementValue; +import org.aspectj.apache.bcel.classfile.annotation.ClassElementValue; +import org.aspectj.apache.bcel.classfile.annotation.NameValuePair; +import org.aspectj.apache.bcel.classfile.annotation.ElementValue; +import org.aspectj.apache.bcel.classfile.annotation.EnumElementValue; +import org.aspectj.apache.bcel.classfile.annotation.RuntimeVisAnnos; +import org.aspectj.apache.bcel.classfile.annotation.SimpleElementValue; +import org.aspectj.apache.bcel.generic.ClassGen; +import org.aspectj.apache.bcel.util.SyntheticRepository; + + +public class RuntimeVisibleAnnotationAttributeTest extends BcelTestCase { + + + protected void setUp() throws Exception { + super.setUp(); + } + + public void testSeeAnnotationsAsAttribute() throws ClassNotFoundException { + SyntheticRepository repos = createRepos("testcode.jar"); + JavaClass clazz = repos.loadClass("SimpleAnnotatedClass"); + ConstantPool pool = clazz.getConstantPool(); + Attribute[] rvaAttr = findAttribute("RuntimeVisibleAnnotations",clazz); + assertTrue("Expected a RuntimeVisibleAnnotations attribute but found none", + rvaAttr.length==1); + } + + public void testAnnotationsAttributeContainsRightData() throws ClassNotFoundException { + SyntheticRepository repos = createRepos("testcode.jar"); + JavaClass clazz = repos.loadClass("SimpleAnnotatedClass"); + ConstantPool pool = clazz.getConstantPool(); + Attribute[] rvaAttr = findAttribute("RuntimeVisibleAnnotations",clazz); + RuntimeVisAnnos rva = (RuntimeVisAnnos) rvaAttr[0]; + List<AnnotationGen> anns = rva.getAnnotations(); + assertTrue("Should be one annotation but found "+anns.size(), + anns.size()==1); + AnnotationGen ann = anns.get(0); + assertTrue("Should be called 'SimpleAnnotation' but was called "+ann.getTypeName(), + ann.getTypeName().equals("SimpleAnnotation")); + List<NameValuePair> l = ann.getValues(); + assertTrue("Should be one value for annotation 'SimpleAnnotation' but found "+l.size(), + l.size()==1); + NameValuePair envp = l.get(0); + assertTrue("Name of element in SimpleAnnotation should be 'id' but it is "+envp.getNameString(), + envp.getNameString().equals("id")); + SimpleElementValue evalue = (SimpleElementValue)envp.getValue(); + assertTrue("'id' should be of type int, but it is "+evalue.getElementValueType(),evalue.getElementValueType()==SimpleElementValue.PRIMITIVE_INT); + assertTrue("'id' should have value 4 but it is "+evalue.getValueInt(), + evalue.getValueInt()==4); + } + + public void testAccessingAnnotationsOnClazz() throws ClassNotFoundException { + SyntheticRepository repos = createRepos("testcode.jar"); + JavaClass clazz = repos.loadClass("SimpleAnnotatedClass"); + ConstantPool pool = clazz.getConstantPool(); + AnnotationGen[] anns = clazz.getAnnotations(); + assertTrue("Expected one annotation on SimpleAnnotatedClass class but got "+anns.length, + anns.length==1); + } + + public void testReadingWritingAnnotations() throws ClassNotFoundException, IOException { + SyntheticRepository repos = createRepos("testcode.jar"); + JavaClass clazz = repos.loadClass("SimpleAnnotatedClass"); + ConstantPool pool = clazz.getConstantPool(); + AnnotationGen[] anns = clazz.getAnnotations(); + assertTrue("Expected one annotation on SimpleAnnotatedClass class but got "+anns.length, + anns.length==1); + + // Write it out + File tfile = createTestdataFile("SimpleAnnotatedClass.class"); + clazz.dump(tfile); + + SyntheticRepository repos2 = createRepos("."); + JavaClass clazz2 = repos.loadClass("SimpleAnnotatedClass"); + ConstantPool pool2 = clazz2.getConstantPool(); + AnnotationGen[] anns2 = clazz2.getAnnotations(); + assertTrue("Expected one annotation on SimpleAnnotatedClass class but got "+anns2.length, + anns2.length==1); + + assertTrue(tfile.delete()); + } + + + + //// + // Test for annotations containing string elements + + public void testAnnotationStringElement() throws ClassNotFoundException { + SyntheticRepository repos = createRepos("testcode.jar"); + JavaClass clazz = repos.loadClass("AnnotatedClass"); + verifyAnnotationStringElement(clazz); + } + + + public void testAnnotationStringElementReadWrite() throws ClassNotFoundException, IOException { + SyntheticRepository repos = createRepos("testcode.jar"); + JavaClass clazz = repos.loadClass("AnnotatedClass"); + verifyAnnotationStringElement(clazz); + + // Write it out + File tfile = createTestdataFile("AnnotatedClass.class"); + clazz.dump(tfile); + + SyntheticRepository repos2 = createRepos("."); + JavaClass clazz2 = repos2.loadClass("AnnotatedClass"); + verifyAnnotationStringElement(clazz2); + + assertTrue(tfile.delete()); + } + + private void verifyAnnotationStringElement(JavaClass clazz) { + AnnotationGen[] anns = clazz.getAnnotations(); + assertTrue("should be one annotation but found "+anns.length,anns.length==1); + AnnotationGen ann = anns[0]; + assertTrue("should be called 'AnnotationStringElement' but was called "+ann.getTypeName(), + ann.getTypeName().equals("AnnotationStringElement")); + List<NameValuePair> l = ann.getValues(); + assertTrue("Should be one value but there were "+l.size(),l.size()==1); + NameValuePair nvp = l.get(0); + assertTrue("Name of element should be 'stringval' but was "+nvp.getNameString(), + nvp.getNameString().equals("stringval")); + SimpleElementValue ev = (SimpleElementValue)nvp.getValue(); + assertTrue("String value should be 'hello' but was '"+ev.getValueString()+"'", + ev.getValueString().equals("hello")); + } + + //// + // Test for complex annotation that includes all primitives + + public void testComplexAnnotation() throws ClassNotFoundException { + SyntheticRepository repos = createRepos("testcode.jar"); + JavaClass clazz = repos.loadClass("ComplexAnnotatedClass"); + verifyComplexAnnotation(clazz); + } + + + public void testComplexAnnotationsReadWrite() throws ClassNotFoundException, IOException { + SyntheticRepository repos = createRepos("testcode.jar"); + JavaClass clazz = repos.loadClass("ComplexAnnotatedClass"); + verifyComplexAnnotation(clazz); + + // Write it out + File tfile = createTestdataFile("ComplexAnnotatedClass.class"); + clazz.dump(tfile); + + SyntheticRepository repos2 = createRepos("."); + JavaClass clazz2 = repos.loadClass("ComplexAnnotatedClass"); + verifyComplexAnnotation(clazz2); + + assertTrue(tfile.delete()); + + } + + private void verifyComplexAnnotation(JavaClass clazz) { + AnnotationGen[] anns = clazz.getAnnotations(); + assertTrue("Should be one annotation but found "+anns.length,anns.length==1); + AnnotationGen ann = anns[0]; + assertTrue("Should be called 'ComplexAnnotation' but was called "+ann.getTypeName(), + ann.getTypeName().equals("ComplexAnnotation")); + List<NameValuePair> l = ann.getValues(); + assertTrue("Should be eight values for annotation 'ComplexAnnotation' but found "+l.size(), + l.size()==8); + List<String> names = RuntimeVisibleAnnotationAttributeTest.getListOfAnnotationNames(ann); + assertTrue("Cant find expected element ",names.contains("ival")); + assertTrue("Cant find expected element ",names.contains("dval")); + assertTrue("Cant find expected element ",names.contains("zval")); + assertTrue("Cant find expected element ",names.contains("fval")); + assertTrue("Cant find expected element ",names.contains("jval")); + assertTrue("Cant find expected element ",names.contains("sval")); + assertTrue("Cant find expected element ",names.contains("bval")); + assertTrue("Cant find expected element ",names.contains("cval")); + + checkValue(ann,"ival","4"); + checkValue(ann,"jval","56"); + checkValue(ann,"fval","3.0"); + checkValue(ann,"dval","33.4"); + checkValue(ann,"sval","99"); + checkValue(ann,"bval","2"); + checkValue(ann,"cval",new Character('5').toString()); + checkValue(ann,"zval","false"); + + } + + private void checkValue(AnnotationGen a,String name,String tostring) { + for (Iterator<NameValuePair> i = a.getValues().iterator(); i.hasNext();) { + NameValuePair element = i.next(); + if (element.getNameString().equals(name)) { + if (!element.getValue().stringifyValue().equals(tostring)) { + fail("Expected element "+name+" to have value "+tostring+" but it had value "+element.getValue().stringifyValue()); + } + return; + } + } + fail("Didnt find named element "+name); + } + + //// + // Test an annotation containing a 'Class' element + + public void testAnnotationClassElement() throws ClassNotFoundException { + SyntheticRepository repos = createRepos("testcode.jar"); + JavaClass clazz = repos.loadClass("AnnotatedWithClassClass"); + verifyClassAnnotation(clazz); + } + + public void testAnnotationClassElementCopying() throws ClassNotFoundException { + SyntheticRepository repos = createRepos("testcode.jar"); + JavaClass clazz = repos.loadClass("AnnotatedWithClassClass"); + AnnotationGen[] anns = clazz.getAnnotations(); + ClassGen cg = new ClassGen(clazz); + // Checks we can copy class values in an annotation + new AnnotationGen(anns[0],cg.getConstantPool(),true); + new AnnotationGen(anns[0],cg.getConstantPool(),false); + } + + public void testAnnotationClassElementReadWrite() throws ClassNotFoundException,IOException { + SyntheticRepository repos = createRepos("testcode.jar"); + JavaClass clazz = repos.loadClass("AnnotatedWithClassClass"); + verifyClassAnnotation(clazz); + + // Write it out + File tfile = createTestdataFile("AnnotatedWithClassClass.class"); + clazz.dump(tfile); + + SyntheticRepository repos2 = createRepos("."); + JavaClass clazz2 = repos2.loadClass("AnnotatedWithClassClass"); + verifyClassAnnotation(clazz2); + + assertTrue(wipe("AnnotatedWithClassClass.class")); + } + + private void verifyClassAnnotation(JavaClass clazz) { + AnnotationGen[] anns = clazz.getAnnotations(); + assertTrue("should be one annotation but found "+anns.length,anns.length==1); + AnnotationGen ann = anns[0]; + assertTrue("should be called 'AnnotationClassElement' but was called "+ann.getTypeName(), + ann.getTypeName().equals("AnnotationClassElement")); + List<NameValuePair> l = ann.getValues(); + assertTrue("Should be one value but there were "+l.size(),l.size()==1); + NameValuePair nvp = l.get(0); + assertTrue("Name of element should be 'clz' but was "+nvp.getNameString(), + nvp.getNameString().equals("clz")); + ClassElementValue ev = (ClassElementValue)nvp.getValue(); + assertTrue("String value should be 'Ljava/lang/Integer;' but was '"+ev.getClassString()+"'", + ev.getClassString().equals("Ljava/lang/Integer;")); + + } + + //// + // Test an annotation containing an enum element + + public void testAnnotationEnumElement() throws ClassNotFoundException { + SyntheticRepository repos = createRepos("testcode.jar"); + JavaClass clazz = repos.loadClass("AnnotatedWithEnumClass"); + verifyAnnotationEnumElement(clazz); + } + + public void testAnnotationEnumElementReadWrite() throws ClassNotFoundException, IOException { + SyntheticRepository repos = createRepos("testcode.jar"); + JavaClass clazz = repos.loadClass("AnnotatedWithEnumClass"); + verifyAnnotationEnumElement(clazz); + + // Write it out + File tfile = createTestdataFile("AnnotatedWithEnumClass.class"); + clazz.dump(tfile); + + SyntheticRepository repos2 = createRepos("."); + JavaClass clazz2 = repos2.loadClass("AnnotatedWithEnumClass"); + verifyAnnotationEnumElement(clazz2); + + assertTrue(tfile.delete()); + } + + public void verifyAnnotationEnumElement(JavaClass clazz) { + AnnotationGen[] anns = clazz.getAnnotations(); + assertTrue("should be one annotation but found "+anns.length,anns.length==1); + AnnotationGen ann = anns[0]; + assertTrue("should be called 'AnnotationEnumElement' but was called "+ann.getTypeName(), + ann.getTypeName().equals("AnnotationEnumElement")); + List<NameValuePair> l = ann.getValues(); + assertTrue("Should be one value but there were "+l.size(),l.size()==1); + NameValuePair nvp = l.get(0); + assertTrue("Name of element should be 'enumval' but was "+nvp.getNameString(), + nvp.getNameString().equals("enumval")); + ElementValue ev = nvp.getValue(); + assertTrue("Should be of type EnumElementValue but is "+ev,ev instanceof EnumElementValue); + EnumElementValue eev = (EnumElementValue)ev; + assertTrue("Should be an enum type value but is "+eev.getElementValueType(),eev.getElementValueType()==SimpleElementValue.ENUM_CONSTANT); + assertTrue("Enum type for annotation should be 'SimpleEnum' but is "+Utility.signatureToString(eev.getEnumTypeString()),Utility.signatureToString(eev.getEnumTypeString()).equals("SimpleEnum")); + assertTrue("String value should be 'Red' but was '"+eev.getEnumValueString()+"'", + eev.getEnumValueString().equals("Red")); + } + + //// + // Test an annotation with an array element + + public void testAnnotationArraysOfAnnotations() throws ClassNotFoundException { + SyntheticRepository repos = createRepos("testcode.jar"); + JavaClass clazz = repos.loadClass("AnnotatedWithCombinedAnnotation"); + AnnotationGen[] anns = clazz.getAnnotations(); + assertTrue("should be one annotation but found "+anns.length,anns.length==1); + checkCombinedAnnotation(anns[0]); + } + + public void testAnnotationArraysOfAnnotationsReadWrite() throws ClassNotFoundException, IOException { + SyntheticRepository repos = createRepos("testcode.jar"); + JavaClass clazz = repos.loadClass("AnnotatedWithCombinedAnnotation"); + AnnotationGen[] anns = clazz.getAnnotations(); + assertTrue("should be one annotation but found "+anns.length,anns.length==1); + checkCombinedAnnotation(anns[0]); + + // Write it out + File tfile = createTestdataFile("AnnotatedWithCombinedAnnotation.class"); + clazz.dump(tfile); + + SyntheticRepository repos2 = createRepos("."); + JavaClass clazz2 = repos2.loadClass("AnnotatedWithCombinedAnnotation"); + AnnotationGen[] anns2 = clazz2.getAnnotations(); + assertTrue("should be one annotation but found "+anns2.length,anns2.length==1); + checkCombinedAnnotation(anns2[0]); + + assertTrue(tfile.delete()); + } + + + private void checkCombinedAnnotation(AnnotationGen ann) { + assertTrue("should be called 'CombinedAnnotation' but was called "+ann.getTypeName(), + ann.getTypeName().equals("CombinedAnnotation")); + List<NameValuePair> l = ann.getValues(); + assertTrue("Should be one value but there were "+l.size(),l.size()==1); + NameValuePair nvp = l.get(0); + assertTrue("Name of element should be 'value' but was "+nvp.getNameString(), + nvp.getNameString().equals("value")); + ElementValue ev = nvp.getValue(); + assertTrue("Should be of type ArrayElementValue but is "+ev,ev instanceof ArrayElementValue); + ArrayElementValue aev = (ArrayElementValue)ev; + + assertTrue("Array element value should be of size 1 but is "+aev.getElementValuesArraySize(), + aev.getElementValuesArraySize()==1); + ElementValue[] evs = aev.getElementValuesArray(); + assertTrue("Entry in the array should be AnnotationElementValue but is "+evs[0], + evs[0] instanceof AnnotationElementValue); + AnnotationElementValue inner_ev = (AnnotationElementValue)evs[0]; + AnnotationGen a = inner_ev.getAnnotation(); + assertTrue("Should be SimpleAnnotation but is "+a.getTypeName(),a.getTypeName().equals("SimpleAnnotation")); + List<NameValuePair> envps = a.getValues(); + assertTrue("Should be one name value pair but found "+envps.size(),envps.size()==1); + NameValuePair envp = envps.get(0); + assertTrue("Name should be 'id' but it is "+envp.getNameString(),envp.getNameString().equals("id")); + assertTrue("Value of 'id' should be 4 but it is "+envp.getValue().stringifyValue(), + envp.getValue().stringifyValue().equals("4")); + } + + + protected void tearDown() throws Exception { + super.tearDown(); + } + + public static List<String> getListOfAnnotationNames(AnnotationGen a) { + List<NameValuePair> l = a.getValues(); + List<String> names = new ArrayList<String>(); + for (Iterator<NameValuePair> i = l.iterator(); i.hasNext();) { + NameValuePair element = i.next(); + names.add(element.getNameString()); + } + return names; + } + +} diff --git a/bcel-builder/src/test/java/org/aspectj/apache/bcel/classfile/tests/RuntimeVisibleParameterAnnotationAttributeTest.java b/bcel-builder/src/test/java/org/aspectj/apache/bcel/classfile/tests/RuntimeVisibleParameterAnnotationAttributeTest.java new file mode 100644 index 000000000..1b8af7419 --- /dev/null +++ b/bcel-builder/src/test/java/org/aspectj/apache/bcel/classfile/tests/RuntimeVisibleParameterAnnotationAttributeTest.java @@ -0,0 +1,143 @@ +/* ******************************************************************* + * Copyright (c) 2004 IBM + * 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: + * Andy Clement - initial implementation + * ******************************************************************/ + +package org.aspectj.apache.bcel.classfile.tests; + +import java.io.File; +import java.io.IOException; +import java.util.Iterator; + +import org.aspectj.apache.bcel.classfile.Attribute; +import org.aspectj.apache.bcel.classfile.JavaClass; +import org.aspectj.apache.bcel.classfile.Method; +import org.aspectj.apache.bcel.classfile.annotation.AnnotationGen; +import org.aspectj.apache.bcel.classfile.annotation.NameValuePair; +import org.aspectj.apache.bcel.classfile.annotation.RuntimeVisParamAnnos; +import org.aspectj.apache.bcel.util.SyntheticRepository; + + +public class RuntimeVisibleParameterAnnotationAttributeTest extends BcelTestCase { + + + protected void setUp() throws Exception { + super.setUp(); + } + + + public void testAccessingRuntimeVisibleParameterAnnotations() throws ClassNotFoundException { + JavaClass clazz = getClassFromJar("AnnotatedParameters"); + Attribute[] rvaAttr = findAttribute("RuntimeVisibleParameterAnnotations",clazz); + Method[] methods = clazz.getMethods(); + + for (int i = 0; i < methods.length; i++) { + Method m = methods[i]; + if (m.getName().equals("foo")) { + RuntimeVisParamAnnos paramAnns = + (RuntimeVisParamAnnos) findAttribute("RuntimeVisibleParameterAnnotations",m.getAttributes()); + assertTrue("foo takes two parameters, not "+paramAnns.getParameterAnnotations().size(), + paramAnns.getParameterAnnotations().size()==2); + + AnnotationGen[] firstParamAnnotations = paramAnns.getAnnotationsOnParameter(0); + checkAnnotation(firstParamAnnotations[0],"SimpleAnnotation","id","2"); + + AnnotationGen[] secondParamAnnotations = paramAnns.getAnnotationsOnParameter(1); + checkAnnotation(secondParamAnnotations[0],"SimpleAnnotation","id","3"); + checkAnnotation(secondParamAnnotations[1],"AnnotationEnumElement","enumval","LSimpleEnum;Red"); + + } + if (m.getName().equals("main")) { + RuntimeVisParamAnnos paramAnns = + (RuntimeVisParamAnnos) findAttribute("RuntimeVisibleParameterAnnotations",m.getAttributes()); + assertTrue("main takes one parameter, not "+paramAnns.getParameterAnnotations().size(), + paramAnns.getParameterAnnotations().size()==1); + + AnnotationGen[] firstParamAnnotations = paramAnns.getAnnotationsOnParameter(0); + checkAnnotation(firstParamAnnotations[0],"SimpleAnnotation","id","1"); + } + } + } + + public void testAccessingParameterAnnotationsThroughGetAnnotations() throws ClassNotFoundException { + JavaClass clazz = getClassFromJar("AnnotatedParameters"); + Attribute[] rvaAttr = findAttribute("RuntimeVisibleParameterAnnotations",clazz); + + checkFooMethod(clazz); + } + + public void testParameterAnnotationsReadWrite() throws ClassNotFoundException,IOException { + JavaClass clazz = getClassFromJar("AnnotatedParameters"); + + checkFooMethod(clazz); + + // Write it out + File tfile = createTestdataFile("AnnotatedParameters.class"); + clazz.dump(tfile); + + SyntheticRepository repos2 = createRepos("."); + JavaClass clazz2 = repos2.loadClass("AnnotatedParameters"); + + checkFooMethod(clazz); + + assertTrue(tfile.delete()); + } + + + public void checkFooMethod(JavaClass clazz) { + Method[] methods = clazz.getMethods(); + + for (int i = 0; i < methods.length; i++) { + Method m = methods[i]; + if (m.getName().equals("foo")) { + + AnnotationGen[] firstParamAnnotations = m.getAnnotationsOnParameter(0); + checkAnnotation(firstParamAnnotations[0],"SimpleAnnotation","id","2"); + + AnnotationGen[] secondParamAnnotations = m.getAnnotationsOnParameter(1); + checkAnnotation(secondParamAnnotations[0],"SimpleAnnotation","id","3"); + checkAnnotation(secondParamAnnotations[1],"AnnotationEnumElement","enumval","LSimpleEnum;Red"); + + } + } + } + + private void checkAnnotation(AnnotationGen a,String name,String elementname,String elementvalue) { + assertTrue("Expected annotation to have name "+name+" but it had name "+a.getTypeName(), + a.getTypeName().equals(name)); + assertTrue("Expected annotation to have one element but it had "+a.getValues().size(),a.getValues().size()==1); + NameValuePair envp = a.getValues().get(0); + assertTrue("Expected element name "+elementname+" but was "+envp.getNameString(), + elementname.equals(envp.getNameString())); + assertTrue("Expected element value "+elementvalue+" but was "+envp.getValue().stringifyValue(), + elementvalue.equals(envp.getValue().stringifyValue())); + } + + + // helper methods + + public void checkValue(AnnotationGen a,String name,String tostring) { + for (Iterator<NameValuePair> i = a.getValues().iterator(); i.hasNext();) { + NameValuePair element = i.next(); + if (element.getNameString().equals(name)) { + if (!element.getValue().stringifyValue().equals(tostring)) { + fail("Expected element "+name+" to have value "+tostring+" but it had value "+element.getValue().stringifyValue()); + } + return; + } + } + fail("Didnt find named element "+name); + } + + protected void tearDown() throws Exception { + super.tearDown(); + } + +} diff --git a/bcel-builder/src/test/java/org/aspectj/apache/bcel/classfile/tests/TypeAnnotationsTest.java b/bcel-builder/src/test/java/org/aspectj/apache/bcel/classfile/tests/TypeAnnotationsTest.java new file mode 100644 index 000000000..be3e3d5ac --- /dev/null +++ b/bcel-builder/src/test/java/org/aspectj/apache/bcel/classfile/tests/TypeAnnotationsTest.java @@ -0,0 +1,374 @@ +/* ******************************************************************* + * Copyright (c) 2013 VMware + * 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: + * Andy Clement - initial implementation + * ******************************************************************/ +package org.aspectj.apache.bcel.classfile.tests; + +import org.aspectj.apache.bcel.Constants; +import org.aspectj.apache.bcel.classfile.Attribute; +import org.aspectj.apache.bcel.classfile.Field; +import org.aspectj.apache.bcel.classfile.JavaClass; +import org.aspectj.apache.bcel.classfile.Method; +import org.aspectj.apache.bcel.classfile.annotation.RuntimeTypeAnnos; +import org.aspectj.apache.bcel.classfile.annotation.RuntimeVisTypeAnnos; +import org.aspectj.apache.bcel.classfile.annotation.TypeAnnotationGen; + +public class TypeAnnotationsTest extends BcelTestCase { + + protected void setUp() throws Exception { + super.setUp(); + } + + public Attribute getAttribute(Attribute[] attrs, byte tag) { + for (Attribute attr: attrs) { + if (attr.getTag() == tag) { + return attr; + } + } + return null; + } + + public void testClassTypeParameter() throws Exception { + JavaClass jc = getClassFromJava8Jar("TypeAnnoOnClassTypeParameter"); + RuntimeVisTypeAnnos rvta = (RuntimeVisTypeAnnos)getAttribute(jc.getAttributes(), Constants.ATTR_RUNTIME_VISIBLE_TYPE_ANNOTATIONS); + assertTrue(rvta.areVisible()); + TypeAnnotationGen[] tas = rvta.getTypeAnnotations(); + assertEquals(2,tas.length); + checkTypeAnnotationClassTypeParameter(tas[0],0,"@Anno"); + checkTypeAnnotationClassTypeParameter(tas[1],1,"@Anno(value=2)"); + } + + public void testMethodTypeParameter() throws Exception { + JavaClass jc = getClassFromJava8Jar("TypeAnnoOnMethodTypeParameter"); + Method m = getMethod(jc, "m"); + RuntimeVisTypeAnnos rvta = (RuntimeVisTypeAnnos)getAttribute(m.getAttributes(), Constants.ATTR_RUNTIME_VISIBLE_TYPE_ANNOTATIONS); + assertTrue(rvta.areVisible()); + TypeAnnotationGen[] tas = rvta.getTypeAnnotations(); + assertEquals(1,tas.length); + checkTypeAnnotationMethodTypeParameter(tas[0],0,"@Anno"); + } + + public void testSuperinterface() throws Exception { + JavaClass jc = getClassFromJava8Jar("TypeAnnoOnSuperinterface1"); + RuntimeVisTypeAnnos rvta = (RuntimeVisTypeAnnos)getAttribute(jc.getAttributes(), Constants.ATTR_RUNTIME_VISIBLE_TYPE_ANNOTATIONS); + assertTrue(rvta.areVisible()); + TypeAnnotationGen[] tas = rvta.getTypeAnnotations(); + assertEquals(1,tas.length); + TypeAnnotationGen ta = tas[0]; + checkTypeAnnotationClassExtends(ta, 0, "@Anno"); + } + + public void testSupertypes() throws Exception { + JavaClass jc = getClassFromJava8Jar("TypeAnnoOnSupertypes"); + RuntimeVisTypeAnnos rvta = (RuntimeVisTypeAnnos)getAttribute(jc.getAttributes(), Constants.ATTR_RUNTIME_VISIBLE_TYPE_ANNOTATIONS); + assertTrue(rvta.areVisible()); + TypeAnnotationGen[] tas = rvta.getTypeAnnotations(); + assertEquals(3,tas.length); + checkTypeAnnotationClassExtends(tas[0],-1,"@Anno(value=1)"); + checkTypeAnnotationClassExtends(tas[1],0,"@Anno"); + checkTypeAnnotationClassExtends(tas[2],1,"@Anno(value=2)"); + } + + public void testClassTypeParameterBound() throws Exception { + JavaClass jc = getClassFromJava8Jar("TypeAnnoOnClassTypeParameterBound"); + RuntimeVisTypeAnnos rvta = (RuntimeVisTypeAnnos)getAttribute(jc.getAttributes(), Constants.ATTR_RUNTIME_VISIBLE_TYPE_ANNOTATIONS); + assertTrue(rvta.areVisible()); + TypeAnnotationGen[] tas = rvta.getTypeAnnotations(); + assertEquals(3,tas.length); + checkTypeAnnotationClassTypeParameterBound(tas[0],0,0,"@Anno"); + checkTypePath(tas[0],TypeAnnotationGen.NO_TYPE_PATH); + checkTypeAnnotationClassTypeParameterBound(tas[1],0,1,"@Anno(value=2)"); + checkTypePath(tas[1],TypeAnnotationGen.NO_TYPE_PATH); + checkTypeAnnotationClassTypeParameterBound(tas[2],0,1,"@Anno(value=3)"); + checkTypePath(tas[2],new int[]{TypeAnnotationGen.TYPE_PATH_ENTRY_KIND_TYPE_ARGUMENT,0}); + } + + public void testMethodTypeParameterBound() throws Exception { + JavaClass jc = getClassFromJava8Jar("TypeAnnoOnMethodTypeParameterBound"); + Method m = getMethod(jc, "m"); + RuntimeVisTypeAnnos rvta = (RuntimeVisTypeAnnos)getAttribute(m.getAttributes(), Constants.ATTR_RUNTIME_VISIBLE_TYPE_ANNOTATIONS); + assertTrue(rvta.areVisible()); + TypeAnnotationGen[] tas = rvta.getTypeAnnotations(); + assertEquals(3,tas.length); + checkTypeAnnotationMethodTypeParameterBound(tas[0],0,0,"@Anno"); + checkTypePath(tas[0],TypeAnnotationGen.NO_TYPE_PATH); + checkTypeAnnotationMethodTypeParameterBound(tas[1],0,1,"@Anno(value=2)"); + checkTypePath(tas[1],TypeAnnotationGen.NO_TYPE_PATH); + checkTypeAnnotationMethodTypeParameterBound(tas[2],0,1,"@Anno(value=3)"); + checkTypePath(tas[2],new int[]{TypeAnnotationGen.TYPE_PATH_ENTRY_KIND_TYPE_ARGUMENT,1}); + } + + public void testField() throws Exception { + JavaClass jc = getClassFromJava8Jar("TypeAnnoOnField"); + Field f = getField(jc,"f1"); + RuntimeVisTypeAnnos rvta = (RuntimeVisTypeAnnos)getAttribute(f.getAttributes(), Constants.ATTR_RUNTIME_VISIBLE_TYPE_ANNOTATIONS); + assertTrue(rvta.areVisible()); + TypeAnnotationGen[] tas = rvta.getTypeAnnotations(); + assertEquals(1,tas.length); + checkTypeAnnotationField(tas[0],"@Anno"); + checkTypePath(tas[0],TypeAnnotationGen.NO_TYPE_PATH); + + tas = getTypeAnnotations(getField(jc,"f2"),true); + checkTypeAnnotationField(tas[0],"@Anno"); + checkTypePath(tas[0],new int[]{TypeAnnotationGen.TYPE_PATH_ENTRY_KIND_TYPE_ARGUMENT,0}); + + tas = getTypeAnnotations(getField(jc,"f3"),true); + checkTypeAnnotationField(tas[0],"@Anno"); + checkTypePath(tas[0],new int[]{TypeAnnotationGen.TYPE_PATH_ENTRY_KIND_ARRAY,0}); + + tas = getTypeAnnotations(getField(jc,"f4"),true); + checkTypeAnnotationField(tas[0],"@Anno"); + checkTypePath(tas[0],new int[]{ + TypeAnnotationGen.TYPE_PATH_ENTRY_KIND_ARRAY,0, + TypeAnnotationGen.TYPE_PATH_ENTRY_KIND_TYPE_ARGUMENT,0 + }); + } + + public void testMethodReturn() throws Exception { + JavaClass jc = getClassFromJava8Jar("TypeAnnoOnMethodReturn"); + TypeAnnotationGen[] tas = getTypeAnnotations(getMethod(jc,"m"), true); + checkTypeAnnotationMethodReturn(tas[0],"@Anno"); + checkTypePath(tas[0],TypeAnnotationGen.NO_TYPE_PATH); + } + + public void testMethodReceiver() throws Exception { + JavaClass jc = getClassFromJava8Jar("TypeAnnoOnMethodReceiver"); + TypeAnnotationGen[] tas = getTypeAnnotations(getMethod(jc,"m"), true); + checkTypeAnnotationMethodReceiver(tas[0],"@Anno"); + checkTypePath(tas[0],TypeAnnotationGen.NO_TYPE_PATH); + } + + public void testMethodFormalParameter() throws Exception { + JavaClass jc = getClassFromJava8Jar("TypeAnnoOnMethodFormalParameter"); + TypeAnnotationGen[] tas = getTypeAnnotations(getMethod(jc,"m"), true); + checkTypeAnnotationMethodFormalParameter(tas[0],0, "@Anno"); + checkTypePath(tas[0],TypeAnnotationGen.NO_TYPE_PATH); + } + + public void testThrows() throws Exception { + JavaClass jc = getClassFromJava8Jar("TypeAnnoOnThrows"); + TypeAnnotationGen[] tas = getTypeAnnotations(getMethod(jc,"m"), true); + checkTypeAnnotationThrows(tas[0],0, "@Anno"); + checkTypePath(tas[0],TypeAnnotationGen.NO_TYPE_PATH); + checkTypeAnnotationThrows(tas[1],1, "@Anno(value=2)"); + checkTypePath(tas[1],TypeAnnotationGen.NO_TYPE_PATH); + } + + public void testLocalVariable() throws Exception { + JavaClass jc = getClassFromJava8Jar("TypeAnnoOnLocalVariable"); + // TODO I think the attribute should be on the code for the method, not the method + TypeAnnotationGen[] tas = getTypeAnnotations(getMethod(jc,"m").getAttributes(), true); + assertEquals(1,tas.length); + checkTypeAnnotationLocalVariable(tas[0],new int[]{11,8,1}, "@Anno"); + } + + public void testResourceVariable() throws Exception { + JavaClass jc = getClassFromJava8Jar("TypeAnnoOnResourceVariable"); + // TODO I think the attribute should be on the code for the method, not the method + TypeAnnotationGen[] tas = getTypeAnnotations(getMethod(jc,"m").getAttributes(), true); + assertEquals(2,tas.length); + checkTypeAnnotationResourceVariable(tas[0],new int[]{17,204,1}, "@Anno"); + checkTypeAnnotationResourceVariable(tas[1],new int[]{36,114,3}, "@Anno(value=99)"); + } + + public void testExceptionParameter() throws Exception { + JavaClass jc = getClassFromJava8Jar("TypeAnnoOnExceptionParameter"); + // TODO I think the attribute should be on the code for the method, not the method + TypeAnnotationGen[] tas = getTypeAnnotations(getMethod(jc,"m").getAttributes(), true); + assertEquals(2,tas.length); + checkTypeAnnotationExceptionParameter(tas[0],0, "@Anno(value=99)"); + checkTypeAnnotationExceptionParameter(tas[1],0, "@Anno"); + } + + public void testInstanceOf() throws Exception { + JavaClass jc = getClassFromJava8Jar("TypeAnnoOnInstanceOf"); + // TODO I think the attribute should be on the code for the method, not the method + TypeAnnotationGen[] tas = getTypeAnnotations(getMethod(jc,"m").getAttributes(), true); + assertEquals(2,tas.length); + checkTypeAnnotationInstanceOf(tas[0],3, "@Anno(value=1)"); + checkTypeAnnotationInstanceOf(tas[1],18, "@Anno(value=1)"); + } + + public void testNew() throws Exception { + JavaClass jc = getClassFromJava8Jar("TypeAnnoOnNew"); + // TODO I think the attribute should be on the code for the method, not the method + TypeAnnotationGen[] tas = getTypeAnnotations(getMethod(jc,"m").getAttributes(), true); + assertEquals(4,tas.length); + checkTypeAnnotationNew(tas[0],0, "@Anno"); + checkTypePath(tas[0],TypeAnnotationGen.NO_TYPE_PATH); + + // TODO type path bugs in javac b90 according to the spec +// checkTypeAnnotationNew(tas[1],8, "@Anno(value=2)"); +// checkTypePath(tas[1],new int[]{ +// TypeAnnotationGen.TYPE_PATH_ENTRY_KIND_ARRAY,0 +// }); +// checkTypeAnnotationNew(tas[2],13, "@Anno(value=4)"); +// checkTypePath(tas[2],TypeAnnotationGen.NO_TYPE_PATH); +// checkTypeAnnotationNew(tas[3],13, "@Anno(value=3)"); +// checkTypePath(tas[3],new int[]{ +// TypeAnnotationGen.TYPE_PATH_ENTRY_KIND_ARRAY,0, +// TypeAnnotationGen.TYPE_PATH_ENTRY_KIND_ARRAY,0 +// }); + } + + + // --- + private TypeAnnotationGen[] getTypeAnnotations(Attribute[] attrs, boolean visible) { + RuntimeTypeAnnos rvta = (RuntimeTypeAnnos)getAttribute(attrs, visible?Constants.ATTR_RUNTIME_VISIBLE_TYPE_ANNOTATIONS:Constants.ATTR_RUNTIME_INVISIBLE_TYPE_ANNOTATIONS); + return rvta.getTypeAnnotations(); + } + + private TypeAnnotationGen[] getTypeAnnotations(Method m, boolean visible) { + RuntimeTypeAnnos rvta = (RuntimeTypeAnnos)getAttribute(m.getAttributes(), visible?Constants.ATTR_RUNTIME_VISIBLE_TYPE_ANNOTATIONS:Constants.ATTR_RUNTIME_INVISIBLE_TYPE_ANNOTATIONS); + return rvta.getTypeAnnotations(); + } + + private TypeAnnotationGen[] getTypeAnnotations(Field f, boolean visible) { + RuntimeTypeAnnos rvta = (RuntimeTypeAnnos)getAttribute(f.getAttributes(), visible?Constants.ATTR_RUNTIME_VISIBLE_TYPE_ANNOTATIONS:Constants.ATTR_RUNTIME_INVISIBLE_TYPE_ANNOTATIONS); + return rvta.getTypeAnnotations(); + } + + private void checkTypePath(TypeAnnotationGen ta, int[] expectedTypePath) { + int[] typepath = ta.getTypePath(); + if (expectedTypePath==TypeAnnotationGen.NO_TYPE_PATH || expectedTypePath==null) { + if (typepath!=TypeAnnotationGen.NO_TYPE_PATH) { + fail("Expected no type path but was "+ta.getTypePathString()); + } + } else { + assertEquals(expectedTypePath.length, typepath.length); + for (int i=0;i<expectedTypePath.length;i++) { + if (expectedTypePath[i]!=typepath[i]) { + fail("Expected type path: "+TypeAnnotationGen.toTypePathString(expectedTypePath)+" does not match actual type path "+ta.getTypePathString()); + } + } + } + } + + private void checkLocalVarTarget(TypeAnnotationGen ta, int[] expectedLocalVarTarget) { + int[] localVarTarget = ta.getLocalVarTarget(); + assertEquals(expectedLocalVarTarget.length, localVarTarget.length); + for (int i=0;i<expectedLocalVarTarget.length;i++) { + if (expectedLocalVarTarget[i]!=localVarTarget[i]) { + fail("Expected local var target: "+toLocalVarTargetString(expectedLocalVarTarget)+" does not match actual type path "+toLocalVarTargetString(localVarTarget)); + } + } + } + + public static String toLocalVarTargetString(int[] localVarTarget) { + StringBuilder sb = new StringBuilder(); + int count = 0; + while (count < localVarTarget.length) { + sb.append("{start_pc="+localVarTarget[count++]+",length="+localVarTarget[count++]+",index="+localVarTarget[count++]+"}"); + } + return sb.toString(); + } + + private void checkTypeAnnotationLocalVariable(TypeAnnotationGen ta, int[] expectedLocalVarTarget, String expectedAnnotationText) { + assertEquals(TypeAnnotationGen.LOCAL_VARIABLE,ta.getTargetType()); + checkLocalVarTarget(ta,expectedLocalVarTarget); + assertEquals(expectedAnnotationText,ta.getAnnotation().toShortString()); + } + + private void checkTypeAnnotationResourceVariable(TypeAnnotationGen ta, int[] expectedLocalVarTarget, String expectedAnnotationText) { + assertEquals(TypeAnnotationGen.RESOURCE_VARIABLE,ta.getTargetType()); + checkLocalVarTarget(ta,expectedLocalVarTarget); + assertEquals(expectedAnnotationText,ta.getAnnotation().toShortString()); + } + + private void checkTypeAnnotationExceptionParameter(TypeAnnotationGen ta, int expectedExceptionTableIndex, String expectedAnnotationText) { + assertEquals(TypeAnnotationGen.EXCEPTION_PARAMETER,ta.getTargetType()); + assertEquals(expectedExceptionTableIndex,ta.getExceptionTableIndex()); + assertEquals(expectedAnnotationText,ta.getAnnotation().toShortString()); + } + + private void checkTypeAnnotationInstanceOf(TypeAnnotationGen ta, int expectedOffset, String expectedAnnotationText) { + assertEquals(TypeAnnotationGen.INSTANCEOF,ta.getTargetType()); + assertEquals(expectedOffset,ta.getOffset()); + assertEquals(expectedAnnotationText,ta.getAnnotation().toShortString()); + } + + private void checkTypeAnnotationNew(TypeAnnotationGen ta, int expectedOffset, String expectedAnnotationText) { + assertEquals(TypeAnnotationGen.NEW,ta.getTargetType()); + assertEquals(expectedOffset,ta.getOffset()); + assertEquals(expectedAnnotationText,ta.getAnnotation().toShortString()); + } + + private void checkTypeAnnotationConstructorReference(TypeAnnotationGen ta, int expectedOffset, String expectedAnnotationText) { + assertEquals(TypeAnnotationGen.CONSTRUCTOR_REFERENCE,ta.getTargetType()); + assertEquals(expectedOffset,ta.getOffset()); + assertEquals(expectedAnnotationText,ta.getAnnotation().toShortString()); + } + + private void checkTypeAnnotationMethodReference(TypeAnnotationGen ta, int expectedOffset, String expectedAnnotationText) { + assertEquals(TypeAnnotationGen.METHOD_REFERENCE,ta.getTargetType()); + assertEquals(expectedOffset,ta.getOffset()); + assertEquals(expectedAnnotationText,ta.getAnnotation().toShortString()); + } + + private void checkTypeAnnotationField(TypeAnnotationGen ta, String expectedAnnotationText) { + assertEquals(TypeAnnotationGen.FIELD,ta.getTargetType()); + assertEquals(expectedAnnotationText,ta.getAnnotation().toShortString()); + } + + private void checkTypeAnnotationMethodReturn(TypeAnnotationGen ta, String expectedAnnotationText) { + assertEquals(TypeAnnotationGen.METHOD_RETURN,ta.getTargetType()); + assertEquals(expectedAnnotationText,ta.getAnnotation().toShortString()); + } + + private void checkTypeAnnotationMethodFormalParameter(TypeAnnotationGen ta, int expectedFormalParameterIndex, String expectedAnnotationText) { + assertEquals(TypeAnnotationGen.METHOD_FORMAL_PARAMETER,ta.getTargetType()); + assertEquals(expectedFormalParameterIndex,ta.getMethodFormalParameterIndex()); + assertEquals(expectedAnnotationText,ta.getAnnotation().toShortString()); + } + + private void checkTypeAnnotationThrows(TypeAnnotationGen ta, int expectedThrowsTypeIndex, String expectedAnnotationText) { + assertEquals(TypeAnnotationGen.THROWS,ta.getTargetType()); + assertEquals(expectedThrowsTypeIndex,ta.getThrowsTypeIndex()); + assertEquals(expectedAnnotationText,ta.getAnnotation().toShortString()); + } + + private void checkTypeAnnotationMethodReceiver(TypeAnnotationGen ta, String expectedAnnotationText) { + assertEquals(TypeAnnotationGen.METHOD_RECEIVER,ta.getTargetType()); + assertEquals(expectedAnnotationText,ta.getAnnotation().toShortString()); + } + + private void checkTypeAnnotationClassExtends(TypeAnnotationGen ta, int expectedSupertypeIndex, String expectedAnnotationText) { + assertEquals(TypeAnnotationGen.CLASS_EXTENDS,ta.getTargetType()); + assertEquals(expectedSupertypeIndex,ta.getSupertypeIndex()); + assertEquals(expectedAnnotationText,ta.getAnnotation().toShortString()); + } + + private void checkTypeAnnotationClassTypeParameter(TypeAnnotationGen ta, int expectedTypeParameterIndex, String expectedAnnotationText) { + assertEquals(TypeAnnotationGen.CLASS_TYPE_PARAMETER,ta.getTargetType()); + assertEquals(expectedTypeParameterIndex,ta.getTypeParameterIndex()); + assertEquals(expectedAnnotationText,ta.getAnnotation().toShortString()); + } + + private void checkTypeAnnotationClassTypeParameterBound(TypeAnnotationGen ta, int expectedTypeParameterIndex, int expectedBoundIndex, String expectedAnnotationText) { + assertEquals(TypeAnnotationGen.CLASS_TYPE_PARAMETER_BOUND,ta.getTargetType()); + assertEquals(expectedTypeParameterIndex,ta.getTypeParameterIndex()); + assertEquals(expectedBoundIndex,ta.getBoundIndex()); + assertEquals(expectedAnnotationText,ta.getAnnotation().toShortString()); + } + + private void checkTypeAnnotationMethodTypeParameterBound(TypeAnnotationGen ta, int expectedTypeParameterIndex, int expectedBoundIndex, String expectedAnnotationText) { + assertEquals(TypeAnnotationGen.METHOD_TYPE_PARAMETER_BOUND,ta.getTargetType()); + assertEquals(expectedTypeParameterIndex,ta.getTypeParameterIndex()); + assertEquals(expectedBoundIndex,ta.getBoundIndex()); + assertEquals(expectedAnnotationText,ta.getAnnotation().toShortString()); + } + + private void checkTypeAnnotationMethodTypeParameter(TypeAnnotationGen ta, int expectedTypeParameterIndex, String expectedAnnotationText) { + assertEquals(TypeAnnotationGen.METHOD_TYPE_PARAMETER,ta.getTargetType()); + assertEquals(expectedTypeParameterIndex,ta.getTypeParameterIndex()); + assertEquals(expectedAnnotationText,ta.getAnnotation().toShortString()); + } + +} diff --git a/bcel-builder/src/test/java/org/aspectj/apache/bcel/classfile/tests/UtilTests.java b/bcel-builder/src/test/java/org/aspectj/apache/bcel/classfile/tests/UtilTests.java new file mode 100644 index 000000000..750dbf4f1 --- /dev/null +++ b/bcel-builder/src/test/java/org/aspectj/apache/bcel/classfile/tests/UtilTests.java @@ -0,0 +1,68 @@ +/* ******************************************************************* + * Copyright (c) 2004 IBM + * 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: + * Andy Clement - initial implementation + * ******************************************************************/ + +package org.aspectj.apache.bcel.classfile.tests; + +import org.aspectj.apache.bcel.classfile.ClassFormatException; +import org.aspectj.apache.bcel.classfile.Utility; +import org.aspectj.apache.bcel.generic.Type; + +import junit.framework.TestCase; + +public class UtilTests extends TestCase { + + protected void setUp() throws Exception { + super.setUp(); + } + + public void testUtilityClassSignatureManipulation1() { + String[] ss = UtilTests.methodSignatureArgumentTypes("(Ljava/lang/String;I[Ljava/lang/Integer;)"); + assertTrue("should be 3 not "+ss.length,ss.length==3); + + assertTrue("first should be 'String', not "+ss[0],ss[0].equals("String")); + assertTrue("second should be 'int', not "+ss[1],ss[1].equals("int")); + assertTrue("third should be 'Integer[]', not "+ss[2],ss[2].equals("Integer[]")); + } + + public void testUtilityClassSignatureManipulation2() { + String s = Utility.methodSignatureToString("(Ljava/lang/String;[Z[[Ljava/lang/Integer;II)Z","hello","public"); + String expected = "public boolean hello(String arg1, boolean[] arg2, Integer[][] arg3, int arg4, int arg5)"; + assertTrue("Expected '"+expected+"' but got "+s,s.equals(expected)); + } + + public void testTypeUtilMethods1() { + String s = Utility.toMethodSignature(Type.DOUBLE,new Type[]{Type.INT,Type.STRING,Type.SHORT}); + System.err.println(s); + } + + public void testTypeUtilMethods2() { + Type s = Type.getType("Ljava/lang/String;"); + System.err.println(s); + s = Type.getType("Z"); + System.err.println(s); + } + + protected void tearDown() throws Exception { + super.tearDown(); + } + + /** + * @param signature Method signature + * @return Array of argument types + * @throws ClassFormatException + */ + public static final String[] methodSignatureArgumentTypes(String signature) throws ClassFormatException { + return GenericSignatureParsingTest.methodSignatureArgumentTypes(signature, true); + } + + +} diff --git a/bcel-builder/src/test/java/org/aspectj/apache/bcel/classfile/tests/VarargsTest.java b/bcel-builder/src/test/java/org/aspectj/apache/bcel/classfile/tests/VarargsTest.java new file mode 100644 index 000000000..877594728 --- /dev/null +++ b/bcel-builder/src/test/java/org/aspectj/apache/bcel/classfile/tests/VarargsTest.java @@ -0,0 +1,97 @@ +/* ******************************************************************* + * Copyright (c) 2004 IBM + * 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: + * Andy Clement - initial implementation + * ******************************************************************/ + +package org.aspectj.apache.bcel.classfile.tests; + +import java.io.File; +import java.io.IOException; +import java.util.Iterator; + +import org.aspectj.apache.bcel.classfile.JavaClass; +import org.aspectj.apache.bcel.classfile.Method; +import org.aspectj.apache.bcel.classfile.annotation.AnnotationGen; +import org.aspectj.apache.bcel.classfile.annotation.NameValuePair; +import org.aspectj.apache.bcel.util.SyntheticRepository; + + +public class VarargsTest extends BcelTestCase { + + + protected void setUp() throws Exception { + super.setUp(); + } + + + public void testVarargs() throws ClassNotFoundException { + JavaClass clazz = getClassFromJar("VarargsClass"); + + checkMarkedVarargs(clazz,"foo",true); + checkMarkedVarargs(clazz,"goo",true); + checkMarkedVarargs(clazz,"hoo",false); + } + + public void testVarargsReadWrite() throws ClassNotFoundException,IOException { + JavaClass clazz = getClassFromJar("VarargsClass"); + + checkMarkedVarargs(clazz,"foo",true); + checkMarkedVarargs(clazz,"goo",true); + checkMarkedVarargs(clazz,"hoo",false); + + // Write it out + File tfile = createTestdataFile("VarargsClass.class"); + clazz.dump(tfile); + + SyntheticRepository repos2 = createRepos("."); + JavaClass clazz2 = repos2.loadClass("VarargsClass"); + + checkMarkedVarargs(clazz,"foo",true); + checkMarkedVarargs(clazz,"goo",true); + checkMarkedVarargs(clazz,"hoo",false); + + assertTrue(tfile.delete()); + } + + // helper methods + + public void checkMarkedVarargs(JavaClass clazz,String methodname,boolean shouldBeMarked) { + Method[] methods = clazz.getMethods(); + + for (int i = 0; i < methods.length; i++) { + Method m = methods[i]; + if (m.getName().equals(methodname)) { + assertTrue("Method '"+methodname+"' should answer varargs="+shouldBeMarked, + m.isVarargs()==shouldBeMarked); + } + } + } + + + // helper methods + + public void checkValue(AnnotationGen a,String name,String tostring) { + for (Iterator<NameValuePair> i = a.getValues().iterator(); i.hasNext();) { + NameValuePair element = i.next(); + if (element.getNameString().equals(name)) { + if (!element.getValue().stringifyValue().equals(tostring)) { + fail("Expected element "+name+" to have value "+tostring+" but it had value "+element.getValue().stringifyValue()); + } + return; + } + } + fail("Didnt find named element "+name); + } + + protected void tearDown() throws Exception { + super.tearDown(); + } + +} diff --git a/bcel-builder/src/test/java/org/aspectj/apache/bcel/util/ClassPathTests.java b/bcel-builder/src/test/java/org/aspectj/apache/bcel/util/ClassPathTests.java new file mode 100644 index 000000000..711011213 --- /dev/null +++ b/bcel-builder/src/test/java/org/aspectj/apache/bcel/util/ClassPathTests.java @@ -0,0 +1,22 @@ +package org.aspectj.apache.bcel.util; + +import java.io.IOException; + +import org.aspectj.apache.bcel.classfile.tests.BcelTestCase; +import org.aspectj.apache.bcel.util.ClassPath.ClassFile; + +public class ClassPathTests extends BcelTestCase { + + public void testJava9ImageFile() throws IOException { + String sunbootClasspath = System.getProperty("sun.boot.class.path"); + if (sunbootClasspath==null || sunbootClasspath.indexOf(".jimage")==-1) { + // Not java9 + return; + } + ClassPath cp = new ClassPath(sunbootClasspath); + ClassFile cf = cp.getClassFile("java/lang/Object"); + assertNotNull(cf); + assertTrue(cf.getSize()>0); + assertTrue(cf.getTime()>0); + } +} diff --git a/bcel-builder/src/test/java/org/aspectj/apache/bcel/util/Play.java b/bcel-builder/src/test/java/org/aspectj/apache/bcel/util/Play.java new file mode 100644 index 000000000..abea68c1d --- /dev/null +++ b/bcel-builder/src/test/java/org/aspectj/apache/bcel/util/Play.java @@ -0,0 +1,84 @@ +package org.aspectj.apache.bcel.util; + +import java.io.File; +import java.io.FileInputStream; + +import org.aspectj.apache.bcel.classfile.Attribute; +import org.aspectj.apache.bcel.classfile.ClassParser; +import org.aspectj.apache.bcel.classfile.Field; +import org.aspectj.apache.bcel.classfile.JavaClass; +import org.aspectj.apache.bcel.classfile.Method; +import org.aspectj.apache.bcel.classfile.annotation.RuntimeAnnos; + +public class Play { + + public static void printBytes(byte[] bs) { + StringBuilder sb = new StringBuilder("Bytes:"+bs.length+"["); + for (int i=0;i<bs.length;i++) { + if (i>0) sb.append(" "); + sb.append(bs[i]); + } + sb.append("]"); + System.out.println(sb); + } + + public static void main(String[] args) throws Exception { + if (args==null || args.length==0 ) { + System.out.println("Specify a file"); + return; + } + if (!args[0].endsWith(".class")) { + args[0] = args[0]+".class"; + } + FileInputStream fis = new FileInputStream(new File(args[0])); + ClassParser cp = new ClassParser(fis,args[0]); + JavaClass jc = cp.parse(); + Attribute[] attributes = jc.getAttributes(); + printUsefulAttributes(attributes); + System.out.println("Fields"); + Field[] fs = jc.getFields(); + if (fs!=null) { + for (Field f: fs) { + System.out.println(f); + printUsefulAttributes(f.getAttributes()); + } + } + System.out.println("Methods"); + Method[] ms = jc.getMethods(); + if (ms!=null) { + for (Method m: ms) { + System.out.println(m); + printUsefulAttributes(m.getAttributes()); + System.out.println("Code attributes:"); + printUsefulAttributes(m.getCode().getAttributes()); + } + } +// Method[] ms = jc.getMethods(); +// for (Method m: ms) { +// System.out.println("=========="); +// System.out.println("Method: "+m.getName()+" modifiers=0x"+Integer.toHexString(m.getModifiers())); +// Attribute[] as = m.getAttributes(); +// for (Attribute a: as) { +// if (a.getName().toLowerCase().contains("synthetic")) { +// System.out.println("> "+a.getName()); +// } +// } +// } + } + + private static void printUsefulAttributes(Attribute[] attributes) throws Exception { + for (Attribute attribute: attributes) { + String n = attribute.getName(); + if (n.equals("RuntimeInvisibleAnnotations") || + n.equals("RuntimeVisibleAnnotations")) { + RuntimeAnnos ra = (RuntimeAnnos)attribute; + // private byte[] annotation_data; + java.lang.reflect.Field f = RuntimeAnnos.class.getDeclaredField("annotation_data"); + f.setAccessible(true); + byte[] bs = (byte[])f.get(ra); +// byte[] bs = unknown.getBytes(); + printBytes(bs); + } + } + } +} |