/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package org.apache.bcel; import java.io.File; import java.util.ArrayList; import java.util.List; import org.apache.bcel.classfile.AnnotationEntry; import org.apache.bcel.classfile.ArrayElementValue; import org.apache.bcel.classfile.ElementValue; import org.apache.bcel.classfile.ElementValuePair; import org.apache.bcel.classfile.JavaClass; import org.apache.bcel.classfile.Method; import org.apache.bcel.classfile.ParameterAnnotationEntry; import org.apache.bcel.classfile.SimpleElementValue; import org.apache.bcel.generic.ALOAD; import org.apache.bcel.generic.ASTORE; import org.apache.bcel.generic.AnnotationElementValueGen; import org.apache.bcel.generic.AnnotationEntryGen; import org.apache.bcel.generic.ArrayElementValueGen; import org.apache.bcel.generic.ArrayType; import org.apache.bcel.generic.ClassGen; import org.apache.bcel.generic.ConstantPoolGen; import org.apache.bcel.generic.ElementValueGen; import org.apache.bcel.generic.ElementValuePairGen; import org.apache.bcel.generic.GOTO; import org.apache.bcel.generic.InstructionConstants; import org.apache.bcel.generic.InstructionFactory; import org.apache.bcel.generic.InstructionHandle; import org.apache.bcel.generic.InstructionList; import org.apache.bcel.generic.LocalVariableGen; import org.apache.bcel.generic.MethodGen; import org.apache.bcel.generic.ObjectType; import org.apache.bcel.generic.PUSH; import org.apache.bcel.generic.SimpleElementValueGen; import org.apache.bcel.generic.Type; import org.apache.bcel.util.SyntheticRepository; /** * The program that some of the tests generate looks like this: * * <pre> * 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); * } * } * </pre> */ public class GeneratingAnnotatedClassesTestCase extends AbstractTestCase { /** * Steps in the test: * <ol> * <li>Programmatically construct the HelloWorld program</li> * <li>Add two simple annotations at the class level</li> * <li>Save the class to disk</li> * <li>Reload the class using the 'static' variant of the BCEL classes</li> * <li>Check the attributes are OK</li> * </ol> */ public void testGenerateClassLevelAnnotations() throws ClassNotFoundException { // Create HelloWorld ClassGen cg = createClassGen("HelloWorld"); cg.setMajor(49); cg.setMinor(0); ConstantPoolGen cp = cg.getConstantPool(); InstructionList il = new InstructionList(); cg.addAnnotationEntry(createSimpleVisibleAnnotation(cp)); cg.addAnnotationEntry(createSimpleInvisibleAnnotation(cp)); buildClassContents(cg, cp, il); //System.out.println(cg.getJavaClass().toString()); dumpClass(cg, "HelloWorld.class"); JavaClass jc = getClassFrom(".", "HelloWorld"); AnnotationEntry[] as = jc.getAnnotationEntries(); assertTrue("Should be two AnnotationEntries but found " + as.length, as.length == 2); // TODO L??; assertTrue( "Name of annotation 1 should be LSimpleAnnotation; but it is " + as[0].getAnnotationType(), as[0].getAnnotationType() .equals("LSimpleAnnotation;")); assertTrue( "Name of annotation 2 should be LSimpleAnnotation; but it is " + as[1].getAnnotationType(), as[1].getAnnotationType() .equals("LSimpleAnnotation;")); ElementValuePair[] vals = as[0].getElementValuePairs(); ElementValuePair nvp = vals[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"); ConstantPoolGen cp = cg.getConstantPool(); InstructionList il = new InstructionList(); buildClassContentsWithAnnotatedMethods(cg, cp, il); // Check annotation is OK int i = cg.getMethods()[0].getAnnotationEntries().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].getAnnotationEntries().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].getAnnotationEntries().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.getAnnotationEntries().length; 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"); ConstantPoolGen 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.getAnnotationEntries().length, mainMethod1 .getAnnotationEntries().length == 1); MethodGen mainMethod2 = new MethodGen(mainMethod1, cg2.getClassName(), cg2.getConstantPool()); assertTrue("The 'MethodGen' should have one annotations but has " + mainMethod2.getAnnotationEntries().length, mainMethod2 .getAnnotationEntries().length == 1); mainMethod2.addAnnotationEntry(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.getAnnotationEntries().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 = getTestClass("org.apache.bcel.data.SimpleAnnotatedClass"); ClassGen cgen = new ClassGen(jc); // Check annotations are correctly preserved AnnotationEntryGen[] annotations = cgen.getAnnotationEntries(); 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 = getTestClass("org.apache.bcel.data.AnnotatedWithEnumClass"); ClassGen cgen = new ClassGen(jc); // Check annotations are correctly preserved AnnotationEntryGen[] annotations = cgen.getAnnotationEntries(); 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 = getTestClass("org.apache.bcel.data.AnnotatedWithCombinedAnnotation"); ClassGen cgen = new ClassGen(jc); // Check annotations are correctly preserved AnnotationEntryGen[] annotations = cgen.getAnnotationEntries(); assertTrue("Expected one annotation but found " + annotations.length, annotations.length == 1); AnnotationEntryGen a = annotations[0]; assertTrue("That annotation should only have one value but has " + a.getValues().size(), a.getValues().size() == 1); ElementValuePairGen nvp = a.getValues().get(0); ElementValueGen value = nvp.getValue(); assertTrue("Value should be ArrayElementValueGen but is " + value, value instanceof ArrayElementValueGen); ArrayElementValueGen arrayValue = (ArrayElementValueGen) value; assertTrue("Array value should be size one but is " + arrayValue.getElementValuesSize(), arrayValue .getElementValuesSize() == 1); ElementValueGen innerValue = arrayValue.getElementValues().get(0); assertTrue( "Value in the array should be AnnotationElementValueGen but is " + innerValue, innerValue instanceof AnnotationElementValueGen); AnnotationElementValueGen innerAnnotationValue = (AnnotationElementValueGen) innerValue; assertTrue("Should be called Lorg/apache/bcel/data/SimpleAnnotation; but is called: " + innerAnnotationValue.getAnnotation().getTypeName(), innerAnnotationValue.getAnnotation().getTypeSignature().equals( "Lorg/apache/bcel/data/SimpleAnnotation;")); // check the three methods Method[] methods = cgen.getMethods(); assertEquals(3, methods.length); for(Method method : methods) { String methodName= method.getName(); if(methodName.equals("<init>")) { assertMethodAnnotations(method, 0, 1); assertParameterAnnotations(method, 0, 1); } else if(methodName.equals("methodWithArrayOfZeroAnnotations")) { assertMethodAnnotations(method, 1, 0); } else if(methodName.equals("methodWithArrayOfTwoAnnotations")) { assertMethodAnnotations(method, 1, 2); } else { fail("unexpected method "+method.getName()); } } } private void assertMethodAnnotations(Method method, int expectedNumberAnnotations, int nExpectedArrayValues) { String methodName= method.getName(); AnnotationEntry[] annos= method.getAnnotationEntries(); assertEquals("For "+methodName, expectedNumberAnnotations, annos.length); if(expectedNumberAnnotations!=0) { assertArrayElementValue(nExpectedArrayValues, annos[0]); } } private void assertArrayElementValue(int nExpectedArrayValues, AnnotationEntry anno) { ElementValuePair elementValuePair = anno.getElementValuePairs()[0]; assertEquals("value", elementValuePair.getNameString()); ArrayElementValue ev = (ArrayElementValue) elementValuePair.getValue(); ElementValue[] eva = ev.getElementValuesArray(); assertEquals(nExpectedArrayValues, eva.length); } private void assertParameterAnnotations(Method method, int... expectedNumberOfParmeterAnnotations) { String methodName= "For "+method.getName(); ParameterAnnotationEntry[] parameterAnnotations= method.getParameterAnnotationEntries(); assertEquals(methodName, expectedNumberOfParmeterAnnotations.length, parameterAnnotations.length); int i= 0; for(ParameterAnnotationEntry parameterAnnotation : parameterAnnotations) { AnnotationEntry[] annos= parameterAnnotation.getAnnotationEntries(); int expectedLength = expectedNumberOfParmeterAnnotations[i++]; assertEquals(methodName+" parameter "+i, expectedLength, annos.length); if(expectedLength!=0) { assertSimpleElementValue(annos[0]); } } } private void assertSimpleElementValue(AnnotationEntry anno) { ElementValuePair elementValuePair = anno.getElementValuePairs()[0]; assertEquals("id", elementValuePair.getNameString()); SimpleElementValue ev = (SimpleElementValue)elementValuePair.getValue(); assertEquals(42, ev.getValueInt()); } /** * Transform complex class from an immutable to a mutable object. */ public void testTransformComplexClassToClassGen() throws ClassNotFoundException { JavaClass jc = getTestClass("org.apache.bcel.data.ComplexAnnotatedClass"); ClassGen cgen = new ClassGen(jc); // Check annotations are correctly preserved AnnotationEntryGen[] annotations = cgen.getAnnotationEntries(); assertTrue("Expected one annotation but found " + annotations.length, annotations.length == 1); List<?> l = annotations[0].getValues(); boolean found = false; for (Object name : l) { ElementValuePairGen element = (ElementValuePairGen) name; if (element.getNameString().equals("dval")) { if (((SimpleElementValueGen) 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 = getTestClass("org.apache.bcel.data.SimpleAnnotatedClass"); ClassGen cgen = new ClassGen(jc); ConstantPoolGen cp = cgen.getConstantPool(); cgen.addAnnotationEntry(createFruitAnnotation(cp, "Pineapple")); assertTrue("Should now have two annotations but has " + cgen.getAnnotationEntries().length, cgen .getAnnotationEntries().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 = getTestClass("org.apache.bcel.data.SimpleAnnotatedClass"); ClassGen cgen = new ClassGen(jc); ConstantPoolGen cp = cgen.getConstantPool(); cgen.addAnnotationEntry(createCombinedAnnotation(cp)); assertTrue("Should now have two annotations but has " + cgen.getAnnotationEntries().length, cgen .getAnnotationEntries().length == 2); dumpClass(cgen, "SimpleAnnotatedClass.class"); JavaClass jc2 = getClassFrom(".", "SimpleAnnotatedClass"); jc2.getAnnotationEntries(); 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, ConstantPoolGen cp, InstructionList il) { // Create method 'public static void main(String[]argv)' MethodGen mg = createMethodGen("main", il, cp); InstructionFactory factory = new InstructionFactory(cg); mg.addAnnotationEntry(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(new ASTORE(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(new ASTORE(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(new PUSH(cp, "Andy")); il.append(new ASTORE(name)); // Upon normal execution we jump behind exception handler, the target // address is not known yet. GOTO g = new GOTO(null); 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(new ASTORE(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(new PUSH(cp, "Hello, ")); il .append(factory.createInvoke("java.lang.StringBuffer", "<init>", Type.VOID, new Type[] { Type.STRING }, Constants.INVOKESPECIAL)); il.append(new 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.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, ConstantPoolGen 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(new ASTORE(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(new ASTORE(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(new PUSH(cp, "Andy")); il.append(new ASTORE(name)); // Upon normal execution we jump behind exception handler, the target // address is not known yet. GOTO g = new GOTO(null); 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(new ASTORE(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(new PUSH(cp, "Hello, ")); il .append(factory.createInvoke("java.lang.StringBuffer", "<init>", Type.VOID, new Type[] { Type.STRING }, Constants.INVOKESPECIAL)); il.append(new 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.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 { // System.out.println(where); 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, ConstantPoolGen 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 AnnotationEntryGen createSimpleVisibleAnnotation(ConstantPoolGen cp) { SimpleElementValueGen evg = new SimpleElementValueGen( ElementValueGen.PRIMITIVE_INT, cp, 4); ElementValuePairGen nvGen = new ElementValuePairGen("id", evg, cp); ObjectType t = new ObjectType("SimpleAnnotation"); List<ElementValuePairGen> elements = new ArrayList<ElementValuePairGen>(); elements.add(nvGen); AnnotationEntryGen a = new AnnotationEntryGen(t, elements, true, cp); return a; } public AnnotationEntryGen createFruitAnnotation(ConstantPoolGen cp, String aFruit) { SimpleElementValueGen evg = new SimpleElementValueGen( ElementValueGen.STRING, cp, aFruit); ElementValuePairGen nvGen = new ElementValuePairGen("fruit", evg, cp); ObjectType t = new ObjectType("SimpleStringAnnotation"); List<ElementValuePairGen> elements = new ArrayList<ElementValuePairGen>(); elements.add(nvGen); return new AnnotationEntryGen(t, elements, true, cp); } public AnnotationEntryGen createCombinedAnnotation(ConstantPoolGen cp) { // Create an annotation instance AnnotationEntryGen a = createSimpleVisibleAnnotation(cp); ArrayElementValueGen array = new ArrayElementValueGen(cp); array.addElement(new AnnotationElementValueGen(a, cp)); ElementValuePairGen nvp = new ElementValuePairGen("value", array, cp); List<ElementValuePairGen> elements = new ArrayList<ElementValuePairGen>(); elements.add(nvp); return new AnnotationEntryGen(new ObjectType("CombinedAnnotation"), elements, true, cp); } public AnnotationEntryGen createSimpleInvisibleAnnotation(ConstantPoolGen cp) { SimpleElementValueGen evg = new SimpleElementValueGen( ElementValueGen.PRIMITIVE_INT, cp, 4); ElementValuePairGen nvGen = new ElementValuePairGen("id", evg, cp); ObjectType t = new ObjectType("SimpleAnnotation"); List<ElementValuePairGen> elements = new ArrayList<ElementValuePairGen>(); elements.add(nvGen); AnnotationEntryGen a = new AnnotationEntryGen(t, elements, false, cp); return a; } }