aboutsummaryrefslogtreecommitdiffstats
path: root/bcel-builder/src/test
diff options
context:
space:
mode:
authorAndy Clement <aclement@pivotal.io>2019-01-24 12:41:06 -0800
committerAndy Clement <aclement@pivotal.io>2019-01-24 12:41:06 -0800
commit9659cfe976a424a20e7b840152a13d266e794226 (patch)
tree0ae99f276260be65a84660bc37bd7ebbed908e63 /bcel-builder/src/test
parent52c4cbfa1b8bcc0d2c7ac50c77203e516b335205 (diff)
downloadaspectj-9659cfe976a424a20e7b840152a13d266e794226.tar.gz
aspectj-9659cfe976a424a20e7b840152a13d266e794226.zip
mavenizing bcel-builder - complete
Diffstat (limited to 'bcel-builder/src/test')
-rw-r--r--bcel-builder/src/test/java/org/aspectj/apache/bcel/classfile/tests/AnnotationAccessFlagTest.java55
-rw-r--r--bcel-builder/src/test/java/org/aspectj/apache/bcel/classfile/tests/AnnotationDefaultAttributeTest.java49
-rw-r--r--bcel-builder/src/test/java/org/aspectj/apache/bcel/classfile/tests/AnnotationGenTest.java179
-rw-r--r--bcel-builder/src/test/java/org/aspectj/apache/bcel/classfile/tests/AnonymousClassTest.java60
-rw-r--r--bcel-builder/src/test/java/org/aspectj/apache/bcel/classfile/tests/BcelTestCase.java186
-rw-r--r--bcel-builder/src/test/java/org/aspectj/apache/bcel/classfile/tests/ClassloaderRepositoryTest.java89
-rw-r--r--bcel-builder/src/test/java/org/aspectj/apache/bcel/classfile/tests/ConstantPoolToStringTest.java54
-rw-r--r--bcel-builder/src/test/java/org/aspectj/apache/bcel/classfile/tests/ElementValueGenTest.java229
-rw-r--r--bcel-builder/src/test/java/org/aspectj/apache/bcel/classfile/tests/EnclosingMethodAttributeTest.java98
-rw-r--r--bcel-builder/src/test/java/org/aspectj/apache/bcel/classfile/tests/EnumAccessFlagTest.java56
-rw-r--r--bcel-builder/src/test/java/org/aspectj/apache/bcel/classfile/tests/FieldAnnotationsTest.java146
-rw-r--r--bcel-builder/src/test/java/org/aspectj/apache/bcel/classfile/tests/Fundamentals.java319
-rw-r--r--bcel-builder/src/test/java/org/aspectj/apache/bcel/classfile/tests/GeneratingAnnotatedClassesTest.java572
-rw-r--r--bcel-builder/src/test/java/org/aspectj/apache/bcel/classfile/tests/GenericSignatureParsingTest.java472
-rw-r--r--bcel-builder/src/test/java/org/aspectj/apache/bcel/classfile/tests/GenericsErasureTesting.java50
-rw-r--r--bcel-builder/src/test/java/org/aspectj/apache/bcel/classfile/tests/GetReflectMembersTest.java61
-rw-r--r--bcel-builder/src/test/java/org/aspectj/apache/bcel/classfile/tests/LocalVariableTypeTableTest.java72
-rw-r--r--bcel-builder/src/test/java/org/aspectj/apache/bcel/classfile/tests/MethodAnnotationsTest.java107
-rw-r--r--bcel-builder/src/test/java/org/aspectj/apache/bcel/classfile/tests/MethodParametersTest.java86
-rw-r--r--bcel-builder/src/test/java/org/aspectj/apache/bcel/classfile/tests/ModuleTest.java136
-rw-r--r--bcel-builder/src/test/java/org/aspectj/apache/bcel/classfile/tests/NonCachingClassLoaderRepositoryTest.java149
-rw-r--r--bcel-builder/src/test/java/org/aspectj/apache/bcel/classfile/tests/ParameterAnnotationsTest.java590
-rw-r--r--bcel-builder/src/test/java/org/aspectj/apache/bcel/classfile/tests/RuntimeVisibleAnnotationAttributeTest.java395
-rw-r--r--bcel-builder/src/test/java/org/aspectj/apache/bcel/classfile/tests/RuntimeVisibleParameterAnnotationAttributeTest.java143
-rw-r--r--bcel-builder/src/test/java/org/aspectj/apache/bcel/classfile/tests/TypeAnnotationsTest.java374
-rw-r--r--bcel-builder/src/test/java/org/aspectj/apache/bcel/classfile/tests/UtilTests.java68
-rw-r--r--bcel-builder/src/test/java/org/aspectj/apache/bcel/classfile/tests/VarargsTest.java97
-rw-r--r--bcel-builder/src/test/java/org/aspectj/apache/bcel/util/ClassPathTests.java22
-rw-r--r--bcel-builder/src/test/java/org/aspectj/apache/bcel/util/Play.java84
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);
+ }
+ }
+ }
+}