View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *   http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   *
17   */
18  package org.apache.bcel.generic;
19  
20  import java.io.File;
21  import java.util.ArrayList;
22  import java.util.List;
23  
24  import org.apache.bcel.AbstractTestCase;
25  import org.apache.bcel.Const;
26  import org.apache.bcel.classfile.AnnotationEntry;
27  import org.apache.bcel.classfile.ArrayElementValue;
28  import org.apache.bcel.classfile.ElementValue;
29  import org.apache.bcel.classfile.ElementValuePair;
30  import org.apache.bcel.classfile.JavaClass;
31  import org.apache.bcel.classfile.Method;
32  import org.apache.bcel.classfile.ParameterAnnotationEntry;
33  import org.apache.bcel.classfile.SimpleElementValue;
34  import org.apache.bcel.util.SyntheticRepository;
35  
36  /**
37   * The program that some of the tests generate looks like this:
38   *
39   * <pre>
40   * public class HelloWorld
41   * {
42   *  public static void main(String[] argv)
43   *  {
44   *      BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
45   *      String name = null;
46   *
47   *      try
48   *      {
49   *          name = &quot;Andy&quot;;
50   *      }
51   *      catch (IOException e)
52   *      {
53   *          return;
54   *      }
55   *      System.out.println(&quot;Hello, &quot; + name);
56   *  }
57   * }
58   * </pre>
59   */
60  public class GeneratingAnnotatedClassesTestCase extends AbstractTestCase
61  {
62      /**
63       * Steps in the test:
64       * <ol>
65       * <li>Programmatically construct the HelloWorld program</li>
66       * <li>Add two simple annotations at the class level</li>
67       * <li>Save the class to disk</li>
68       * <li>Reload the class using the 'static' variant of the BCEL classes</li>
69       * <li>Check the attributes are OK</li>
70       * </ol>
71       */
72      public void testGenerateClassLevelAnnotations()
73              throws ClassNotFoundException
74      {
75          // Create HelloWorld
76          final ClassGen cg = createClassGen("HelloWorld");
77          cg.setMajor(49);
78          cg.setMinor(0);
79          final ConstantPoolGen cp = cg.getConstantPool();
80          final InstructionList il = new InstructionList();
81          cg.addAnnotationEntry(createSimpleVisibleAnnotation(cp));
82          cg.addAnnotationEntry(createSimpleInvisibleAnnotation(cp));
83          buildClassContents(cg, cp, il);
84          //System.out.println(cg.getJavaClass().toString());
85          dumpClass(cg, "HelloWorld.class");
86          final JavaClass jc = getClassFrom(".", "HelloWorld");
87          final AnnotationEntry[] as = jc.getAnnotationEntries();
88          assertTrue("Should be two AnnotationEntries but found " + as.length,
89                  as.length == 2);
90          // TODO L??;
91          assertTrue(
92                  "Name of annotation 1 should be LSimpleAnnotation; but it is "
93                          + as[0].getAnnotationType(), as[0].getAnnotationType()
94                          .equals("LSimpleAnnotation;"));
95          assertTrue(
96                  "Name of annotation 2 should be LSimpleAnnotation; but it is "
97                          + as[1].getAnnotationType(), as[1].getAnnotationType()
98                          .equals("LSimpleAnnotation;"));
99          final ElementValuePair[] vals = as[0].getElementValuePairs();
100         final ElementValuePair nvp = vals[0];
101         assertTrue(
102                 "Name of element in SimpleAnnotation should be 'id' but it is "
103                         + nvp.getNameString(), nvp.getNameString().equals("id"));
104         final ElementValue ev = nvp.getValue();
105         assertTrue("Type of element value should be int but it is "
106                 + ev.getElementValueType(),
107                 ev.getElementValueType() == ElementValue.PRIMITIVE_INT);
108         assertTrue("Value of element should be 4 but it is "
109                 + ev.stringifyValue(), ev.stringifyValue().equals("4"));
110         assertTrue(createTestdataFile("HelloWorld.class").delete());
111     }
112 
113     /**
114      * Just check that we can dump a class that has a method annotation on it
115      * and it is still there when we read it back in
116      */
117     public void testGenerateMethodLevelAnnotations1()
118             throws ClassNotFoundException
119     {
120         // Create HelloWorld
121         final ClassGen cg = createClassGen("HelloWorld");
122         final ConstantPoolGen cp = cg.getConstantPool();
123         final InstructionList il = new InstructionList();
124         buildClassContentsWithAnnotatedMethods(cg, cp, il);
125         // Check annotation is OK
126         int i = cg.getMethods()[0].getAnnotationEntries().length;
127         assertTrue(
128                 "Prior to dumping, main method should have 1 annotation but has "
129                         + i, i == 1);
130         dumpClass(cg, "temp1" + File.separator + "HelloWorld.class");
131         final JavaClass jc2 = getClassFrom("temp1", "HelloWorld");
132         // Check annotation is OK
133         i = jc2.getMethods()[0].getAnnotationEntries().length;
134         assertTrue("JavaClass should say 1 annotation on main method but says "
135                 + i, i == 1);
136         final ClassGen cg2 = new ClassGen(jc2);
137         // Check it now it is a ClassGen
138         final Method[] m = cg2.getMethods();
139         i = m[0].getAnnotationEntries().length;
140         assertTrue("The main 'Method' should have one annotation but has " + i,
141                 i == 1);
142         final MethodGen mg = new MethodGen(m[0], cg2.getClassName(), cg2
143                 .getConstantPool());
144         // Check it finally when the Method is changed to a MethodGen
145         i = mg.getAnnotationEntries().length;
146         assertTrue("The main 'MethodGen' should have one annotation but has "
147                 + i, i == 1);
148 
149         assertTrue(wipe("temp1", "HelloWorld.class"));
150     }
151 
152     /**
153      * Going further than the last test - when we reload the method back in,
154      * let's change it (adding a new annotation) and then store that, read it
155      * back in and verify both annotations are there !
156      */
157     public void testGenerateMethodLevelAnnotations2()
158             throws ClassNotFoundException
159     {
160         // Create HelloWorld
161         final ClassGen cg = createClassGen("HelloWorld");
162         final ConstantPoolGen cp = cg.getConstantPool();
163         final InstructionList il = new InstructionList();
164         buildClassContentsWithAnnotatedMethods(cg, cp, il);
165         dumpClass(cg, "temp2", "HelloWorld.class");
166         final JavaClass jc2 = getClassFrom("temp2", "HelloWorld");
167         final ClassGen cg2 = new ClassGen(jc2);
168         // Main method after reading the class back in
169         final Method mainMethod1 = jc2.getMethods()[0];
170         assertTrue("The 'Method' should have one annotations but has "
171                 + mainMethod1.getAnnotationEntries().length, mainMethod1
172                 .getAnnotationEntries().length == 1);
173         final MethodGen mainMethod2 = new MethodGen(mainMethod1, cg2.getClassName(),
174                 cg2.getConstantPool());
175         assertTrue("The 'MethodGen' should have one annotations but has "
176                 + mainMethod2.getAnnotationEntries().length, mainMethod2
177                 .getAnnotationEntries().length == 1);
178         mainMethod2.addAnnotationEntry(createFruitAnnotation(cg2
179                 .getConstantPool(), "Pear"));
180         cg2.removeMethod(mainMethod1);
181         cg2.addMethod(mainMethod2.getMethod());
182         dumpClass(cg2, "temp3", "HelloWorld.class");
183         final JavaClass jc3 = getClassFrom("temp3", "HelloWorld");
184         final ClassGen cg3 = new ClassGen(jc3);
185         final Method mainMethod3 = cg3.getMethods()[1];
186         final int i = mainMethod3.getAnnotationEntries().length;
187         assertTrue("The 'Method' should now have two annotations but has " + i,
188                 i == 2);
189         assertTrue(wipe("temp2", "HelloWorld.class"));
190         assertTrue(wipe("temp3", "HelloWorld.class"));
191     }
192 
193     // J5TODO: Need to add deleteFile calls to many of these tests
194     /**
195      * Transform simple class from an immutable to a mutable object.
196      */
197     public void testTransformClassToClassGen_SimpleTypes()
198             throws ClassNotFoundException
199     {
200         final JavaClass jc = getTestClass(PACKAGE_BASE_NAME+".data.SimpleAnnotatedClass");
201         final ClassGen cgen = new ClassGen(jc);
202         // Check annotations are correctly preserved
203         final AnnotationEntryGen[] annotations = cgen.getAnnotationEntries();
204         assertTrue("Expected one annotation but found " + annotations.length,
205                 annotations.length == 1);
206     }
207 
208     /**
209      * Transform simple class from an immutable to a mutable object. The class
210      * is annotated with an annotation that uses an enum.
211      */
212     public void testTransformClassToClassGen_EnumType()
213             throws ClassNotFoundException
214     {
215         final JavaClass jc = getTestClass(PACKAGE_BASE_NAME+".data.AnnotatedWithEnumClass");
216         final ClassGen cgen = new ClassGen(jc);
217         // Check annotations are correctly preserved
218         final AnnotationEntryGen[] annotations = cgen.getAnnotationEntries();
219         assertTrue("Expected one annotation but found " + annotations.length,
220                 annotations.length == 1);
221     }
222 
223     /**
224      * Transform simple class from an immutable to a mutable object. The class
225      * is annotated with an annotation that uses an array of SimpleAnnotations.
226      */
227     public void testTransformClassToClassGen_ArrayAndAnnotationTypes()
228             throws ClassNotFoundException
229     {
230         final JavaClass jc = getTestClass(PACKAGE_BASE_NAME+".data.AnnotatedWithCombinedAnnotation");
231         final ClassGen cgen = new ClassGen(jc);
232         // Check annotations are correctly preserved
233         final AnnotationEntryGen[] annotations = cgen.getAnnotationEntries();
234         assertTrue("Expected one annotation but found " + annotations.length,
235                 annotations.length == 1);
236         final AnnotationEntryGen a = annotations[0];
237         assertTrue("That annotation should only have one value but has "
238                 + a.getValues().size(), a.getValues().size() == 1);
239         final ElementValuePairGen nvp = a.getValues().get(0);
240         final ElementValueGen value = nvp.getValue();
241         assertTrue("Value should be ArrayElementValueGen but is " + value,
242                 value instanceof ArrayElementValueGen);
243         final ArrayElementValueGen arrayValue = (ArrayElementValueGen) value;
244         assertTrue("Array value should be size one but is "
245                 + arrayValue.getElementValuesSize(), arrayValue
246                 .getElementValuesSize() == 1);
247         final ElementValueGen innerValue = arrayValue.getElementValues().get(0);
248         assertTrue(
249                 "Value in the array should be AnnotationElementValueGen but is "
250                         + innerValue,
251                 innerValue instanceof AnnotationElementValueGen);
252         final AnnotationElementValueGen innerAnnotationValue = (AnnotationElementValueGen) innerValue;
253         assertTrue("Should be called L"+PACKAGE_BASE_SIG+"/data/SimpleAnnotation; but is called: "
254                 + innerAnnotationValue.getAnnotation().getTypeName(),
255                 innerAnnotationValue.getAnnotation().getTypeSignature().equals(
256                         "L"+PACKAGE_BASE_SIG+"/data/SimpleAnnotation;"));
257 
258         // check the three methods
259         final Method[] methods = cgen.getMethods();
260         assertEquals(3, methods.length);
261         for (final Method method : methods)
262         {
263             final String methodName= method.getName();
264             if(methodName.equals("<init>"))
265             {
266                 assertMethodAnnotations(method, 0, 1);
267                 assertParameterAnnotations(method, 0, 1);
268             }
269             else if(methodName.equals("methodWithArrayOfZeroAnnotations"))
270             {
271                 assertMethodAnnotations(method, 1, 0);
272             }
273             else if(methodName.equals("methodWithArrayOfTwoAnnotations"))
274             {
275                 assertMethodAnnotations(method, 1, 2);
276             }
277             else
278             {
279                 fail("unexpected method "+method.getName());
280             }
281         }
282     }
283 
284     private void assertMethodAnnotations(final Method method, final int expectedNumberAnnotations, final int nExpectedArrayValues)
285     {
286         final String methodName= method.getName();
287         final AnnotationEntry[] annos= method.getAnnotationEntries();
288         assertEquals("For "+methodName, expectedNumberAnnotations, annos.length);
289         if(expectedNumberAnnotations!=0)
290         {
291             assertArrayElementValue(nExpectedArrayValues, annos[0]);
292         }
293     }
294 
295     private void assertArrayElementValue(final int nExpectedArrayValues, final AnnotationEntry anno)
296     {
297         final ElementValuePair elementValuePair = anno.getElementValuePairs()[0];
298         assertEquals("value", elementValuePair.getNameString());
299         final ArrayElementValue ev = (ArrayElementValue) elementValuePair.getValue();
300         final ElementValue[] eva = ev.getElementValuesArray();
301         assertEquals(nExpectedArrayValues, eva.length);
302     }
303 
304     private void assertParameterAnnotations(final Method method, final int... expectedNumberOfParmeterAnnotations)
305     {
306         final String methodName= "For "+method.getName();
307         final ParameterAnnotationEntry[] parameterAnnotations= method.getParameterAnnotationEntries();
308         assertEquals(methodName, expectedNumberOfParmeterAnnotations.length, parameterAnnotations.length);
309 
310         int i= 0;
311         for (final ParameterAnnotationEntry parameterAnnotation : parameterAnnotations)
312         {
313             final AnnotationEntry[] annos= parameterAnnotation.getAnnotationEntries();
314             final int expectedLength = expectedNumberOfParmeterAnnotations[i++];
315             assertEquals(methodName+" parameter "+i, expectedLength, annos.length);
316             if(expectedLength!=0)
317             {
318                 assertSimpleElementValue(annos[0]);
319             }
320         }
321     }
322 
323     private void assertSimpleElementValue(final AnnotationEntry anno)
324     {
325         final ElementValuePair elementValuePair = anno.getElementValuePairs()[0];
326         assertEquals("id", elementValuePair.getNameString());
327         final SimpleElementValue ev = (SimpleElementValue)elementValuePair.getValue();
328         assertEquals(42, ev.getValueInt());
329     }
330 
331     /**
332      * Transform complex class from an immutable to a mutable object.
333      */
334     public void testTransformComplexClassToClassGen()
335             throws ClassNotFoundException
336     {
337         final JavaClass jc = getTestClass(PACKAGE_BASE_NAME+".data.ComplexAnnotatedClass");
338         final ClassGen cgen = new ClassGen(jc);
339         // Check annotations are correctly preserved
340         final AnnotationEntryGen[] annotations = cgen.getAnnotationEntries();
341         assertTrue("Expected one annotation but found " + annotations.length,
342                 annotations.length == 1);
343         final List<?> l = annotations[0].getValues();
344         boolean found = false;
345         for (final Object name : l) {
346             final ElementValuePairGen element = (ElementValuePairGen) name;
347             if (element.getNameString().equals("dval"))
348             {
349                 if (((SimpleElementValueGen) element.getValue())
350                         .stringifyValue().equals("33.4")) {
351                     found = true;
352                 }
353             }
354         }
355         assertTrue("Did not find double annotation value with value 33.4",
356                 found);
357     }
358 
359     /**
360      * Load a class in and modify it with a new attribute - A SimpleAnnotation
361      * annotation
362      */
363     public void testModifyingClasses1() throws ClassNotFoundException
364     {
365         final JavaClass jc = getTestClass(PACKAGE_BASE_NAME+".data.SimpleAnnotatedClass");
366         final ClassGen cgen = new ClassGen(jc);
367         final ConstantPoolGen cp = cgen.getConstantPool();
368         cgen.addAnnotationEntry(createFruitAnnotation(cp, "Pineapple"));
369         assertTrue("Should now have two annotations but has "
370                 + cgen.getAnnotationEntries().length, cgen
371                 .getAnnotationEntries().length == 2);
372         dumpClass(cgen, "SimpleAnnotatedClass.class");
373         assertTrue(wipe("SimpleAnnotatedClass.class"));
374     }
375 
376     /**
377      * Load a class in and modify it with a new attribute - A ComplexAnnotation
378      * annotation
379      */
380     public void testModifyingClasses2() throws ClassNotFoundException
381     {
382         final JavaClass jc = getTestClass(PACKAGE_BASE_NAME+".data.SimpleAnnotatedClass");
383         final ClassGen cgen = new ClassGen(jc);
384         final ConstantPoolGen cp = cgen.getConstantPool();
385         cgen.addAnnotationEntry(createCombinedAnnotation(cp));
386         assertTrue("Should now have two annotations but has "
387                 + cgen.getAnnotationEntries().length, cgen
388                 .getAnnotationEntries().length == 2);
389         dumpClass(cgen, "SimpleAnnotatedClass.class");
390         final JavaClass jc2 = getClassFrom(".", "SimpleAnnotatedClass");
391         jc2.getAnnotationEntries();
392         assertTrue(wipe("SimpleAnnotatedClass.class"));
393         // System.err.println(jc2.toString());
394     }
395 
396     private void dumpClass(final ClassGen cg, final String fname)
397     {
398         try
399         {
400             final File f = createTestdataFile(fname);
401             cg.getJavaClass().dump(f);
402         }
403         catch (final java.io.IOException e)
404         {
405             System.err.println(e);
406         }
407     }
408 
409     private void dumpClass(final ClassGen cg, final String dir, final String fname)
410     {
411         dumpClass(cg, dir + File.separator + fname);
412     }
413 
414     private void buildClassContentsWithAnnotatedMethods(final ClassGen cg,
415             final ConstantPoolGen cp, final InstructionList il)
416     {
417         // Create method 'public static void main(String[]argv)'
418         final MethodGen mg = createMethodGen("main", il, cp);
419         final InstructionFactory factory = new InstructionFactory(cg);
420         mg.addAnnotationEntry(createSimpleVisibleAnnotation(mg
421                 .getConstantPool()));
422         // We now define some often used types:
423         final ObjectType i_stream = new ObjectType("java.io.InputStream");
424         final ObjectType p_stream = new ObjectType("java.io.PrintStream");
425         // Create variables in and name : We call the constructors, i.e.,
426         // execute BufferedReader(InputStreamReader(System.in)) . The reference
427         // to the BufferedReader object stays on top of the stack and is stored
428         // in the newly allocated in variable.
429         il.append(factory.createNew("java.io.BufferedReader"));
430         il.append(InstructionConst.DUP); // Use predefined constant
431         il.append(factory.createNew("java.io.InputStreamReader"));
432         il.append(InstructionConst.DUP);
433         il.append(factory.createFieldAccess("java.lang.System", "in", i_stream,
434                 Const.GETSTATIC));
435         il.append(factory.createInvoke("java.io.InputStreamReader", "<init>",
436                 Type.VOID, new Type[] { i_stream }, Const.INVOKESPECIAL));
437         il.append(factory.createInvoke("java.io.BufferedReader", "<init>",
438                 Type.VOID, new Type[] { new ObjectType("java.io.Reader") },
439                 Const.INVOKESPECIAL));
440         LocalVariableGen lg = mg.addLocalVariable("in", new ObjectType(
441                 "java.io.BufferedReader"), null, null);
442         final int in = lg.getIndex();
443         lg.setStart(il.append(new ASTORE(in))); // "in" valid from here
444         // Create local variable name and initialize it to null
445         lg = mg.addLocalVariable("name", Type.STRING, null, null);
446         final int name = lg.getIndex();
447         il.append(InstructionConst.ACONST_NULL);
448         lg.setStart(il.append(new ASTORE(name))); // "name" valid from here
449         // Create try-catch block: We remember the start of the block, read a
450         // line from the standard input and store it into the variable name .
451         // InstructionHandle try_start = il.append(factory.createFieldAccess(
452         // "java.lang.System", "out", p_stream, Constants.GETSTATIC));
453         // il.append(new PUSH(cp, "Please enter your name> "));
454         // il.append(factory.createInvoke("java.io.PrintStream", "print",
455         // Type.VOID, new Type[] { Type.STRING },
456         // Constants.INVOKEVIRTUAL));
457         // il.append(new ALOAD(in));
458         // il.append(factory.createInvoke("java.io.BufferedReader", "readLine",
459         // Type.STRING, Type.NO_ARGS, Constants.INVOKEVIRTUAL));
460         final InstructionHandle try_start = il.append(new PUSH(cp, "Andy"));
461         il.append(new ASTORE(name));
462         // Upon normal execution we jump behind exception handler, the target
463         // address is not known yet.
464         final GOTO g = new GOTO(null);
465         final InstructionHandle try_end = il.append(g);
466         // We add the exception handler which simply returns from the method.
467         final LocalVariableGen var_ex = mg.addLocalVariable("ex", Type
468                 .getType("Ljava.io.IOException;"), null, null);
469         final int var_ex_slot = var_ex.getIndex();
470         final InstructionHandle handler = il.append(new ASTORE(var_ex_slot));
471         var_ex.setStart(handler);
472         var_ex.setEnd(il.append(InstructionConst.RETURN));
473         mg.addExceptionHandler(try_start, try_end, handler, new ObjectType(
474                 "java.io.IOException"));
475         // "Normal" code continues, now we can set the branch target of the GOTO
476         // .
477         final InstructionHandle ih = il.append(factory.createFieldAccess(
478                 "java.lang.System", "out", p_stream, Const.GETSTATIC));
479         g.setTarget(ih);
480         // Printing "Hello": String concatenation compiles to StringBuffer
481         // operations.
482         il.append(factory.createNew(Type.STRINGBUFFER));
483         il.append(InstructionConst.DUP);
484         il.append(new PUSH(cp, "Hello, "));
485         il
486                 .append(factory.createInvoke("java.lang.StringBuffer",
487                         "<init>", Type.VOID, new Type[] { Type.STRING },
488                         Const.INVOKESPECIAL));
489         il.append(new ALOAD(name));
490         il.append(factory.createInvoke("java.lang.StringBuffer", "append",
491                 Type.STRINGBUFFER, new Type[] { Type.STRING },
492                 Const.INVOKEVIRTUAL));
493         il.append(factory.createInvoke("java.lang.StringBuffer", "toString",
494                 Type.STRING, Type.NO_ARGS, Const.INVOKEVIRTUAL));
495         il
496                 .append(factory.createInvoke("java.io.PrintStream", "println",
497                         Type.VOID, new Type[] { Type.STRING },
498                         Const.INVOKEVIRTUAL));
499         il.append(InstructionConst.RETURN);
500         // Finalization: Finally, we have to set the stack size, which normally
501         // would have to be computed on the fly and add a default constructor
502         // method to the class, which is empty in this case.
503         mg.setMaxStack();
504         mg.setMaxLocals();
505         cg.addMethod(mg.getMethod());
506         il.dispose(); // Allow instruction handles to be reused
507         cg.addEmptyConstructor(Const.ACC_PUBLIC);
508     }
509 
510     private void buildClassContents(final ClassGen cg, final ConstantPoolGen cp,
511             final InstructionList il)
512     {
513         // Create method 'public static void main(String[]argv)'
514         final MethodGen mg = createMethodGen("main", il, cp);
515         final InstructionFactory factory = new InstructionFactory(cg);
516         // We now define some often used types:
517         final ObjectType i_stream = new ObjectType("java.io.InputStream");
518         final ObjectType p_stream = new ObjectType("java.io.PrintStream");
519         // Create variables in and name : We call the constructors, i.e.,
520         // execute BufferedReader(InputStreamReader(System.in)) . The reference
521         // to the BufferedReader object stays on top of the stack and is stored
522         // in the newly allocated in variable.
523         il.append(factory.createNew("java.io.BufferedReader"));
524         il.append(InstructionConst.DUP); // Use predefined constant
525         il.append(factory.createNew("java.io.InputStreamReader"));
526         il.append(InstructionConst.DUP);
527         il.append(factory.createFieldAccess("java.lang.System", "in", i_stream,
528                 Const.GETSTATIC));
529         il.append(factory.createInvoke("java.io.InputStreamReader", "<init>",
530                 Type.VOID, new Type[] { i_stream }, Const.INVOKESPECIAL));
531         il.append(factory.createInvoke("java.io.BufferedReader", "<init>",
532                 Type.VOID, new Type[] { new ObjectType("java.io.Reader") },
533                 Const.INVOKESPECIAL));
534         LocalVariableGen lg = mg.addLocalVariable("in", new ObjectType(
535                 "java.io.BufferedReader"), null, null);
536         final int in = lg.getIndex();
537         lg.setStart(il.append(new ASTORE(in))); // "in" valid from here
538         // Create local variable name and initialize it to null
539         lg = mg.addLocalVariable("name", Type.STRING, null, null);
540         final int name = lg.getIndex();
541         il.append(InstructionConst.ACONST_NULL);
542         lg.setStart(il.append(new ASTORE(name))); // "name" valid from here
543         // Create try-catch block: We remember the start of the block, read a
544         // line from the standard input and store it into the variable name .
545         // InstructionHandle try_start = il.append(factory.createFieldAccess(
546         // "java.lang.System", "out", p_stream, Constants.GETSTATIC));
547         // il.append(new PUSH(cp, "Please enter your name> "));
548         // il.append(factory.createInvoke("java.io.PrintStream", "print",
549         // Type.VOID, new Type[] { Type.STRING },
550         // Constants.INVOKEVIRTUAL));
551         // il.append(new ALOAD(in));
552         // il.append(factory.createInvoke("java.io.BufferedReader", "readLine",
553         // Type.STRING, Type.NO_ARGS, Constants.INVOKEVIRTUAL));
554         final InstructionHandle try_start = il.append(new PUSH(cp, "Andy"));
555         il.append(new ASTORE(name));
556         // Upon normal execution we jump behind exception handler, the target
557         // address is not known yet.
558         final GOTO g = new GOTO(null);
559         final InstructionHandle try_end = il.append(g);
560         // We add the exception handler which simply returns from the method.
561         final LocalVariableGen var_ex = mg.addLocalVariable("ex", Type
562                 .getType("Ljava.io.IOException;"), null, null);
563         final int var_ex_slot = var_ex.getIndex();
564         final InstructionHandle handler = il.append(new ASTORE(var_ex_slot));
565         var_ex.setStart(handler);
566         var_ex.setEnd(il.append(InstructionConst.RETURN));
567         mg.addExceptionHandler(try_start, try_end, handler, new ObjectType(
568                 "java.io.IOException"));
569         // "Normal" code continues, now we can set the branch target of the GOTO
570         // .
571         final InstructionHandle ih = il.append(factory.createFieldAccess(
572                 "java.lang.System", "out", p_stream, Const.GETSTATIC));
573         g.setTarget(ih);
574         // Printing "Hello": String concatenation compiles to StringBuffer
575         // operations.
576         il.append(factory.createNew(Type.STRINGBUFFER));
577         il.append(InstructionConst.DUP);
578         il.append(new PUSH(cp, "Hello, "));
579         il
580                 .append(factory.createInvoke("java.lang.StringBuffer",
581                         "<init>", Type.VOID, new Type[] { Type.STRING },
582                         Const.INVOKESPECIAL));
583         il.append(new ALOAD(name));
584         il.append(factory.createInvoke("java.lang.StringBuffer", "append",
585                 Type.STRINGBUFFER, new Type[] { Type.STRING },
586                 Const.INVOKEVIRTUAL));
587         il.append(factory.createInvoke("java.lang.StringBuffer", "toString",
588                 Type.STRING, Type.NO_ARGS, Const.INVOKEVIRTUAL));
589         il
590                 .append(factory.createInvoke("java.io.PrintStream", "println",
591                         Type.VOID, new Type[] { Type.STRING },
592                         Const.INVOKEVIRTUAL));
593         il.append(InstructionConst.RETURN);
594         // Finalization: Finally, we have to set the stack size, which normally
595         // would have to be computed on the fly and add a default constructor
596         // method to the class, which is empty in this case.
597         mg.setMaxStack();
598         mg.setMaxLocals();
599         cg.addMethod(mg.getMethod());
600         il.dispose(); // Allow instruction handles to be reused
601         cg.addEmptyConstructor(Const.ACC_PUBLIC);
602     }
603 
604     private JavaClass getClassFrom(final String where, final String clazzname)
605             throws ClassNotFoundException
606     {
607         // System.out.println(where);
608         final SyntheticRepository repos = createRepos(where);
609         return repos.loadClass(clazzname);
610     }
611 
612     // helper methods
613     private ClassGen createClassGen(final String classname)
614     {
615         return new ClassGen(classname, "java.lang.Object", "<generated>",
616                 Const.ACC_PUBLIC | Const.ACC_SUPER, null);
617     }
618 
619     private MethodGen createMethodGen(final String methodname, final InstructionList il,
620             final ConstantPoolGen cp)
621     {
622         return new MethodGen(Const.ACC_STATIC | Const.ACC_PUBLIC, // access
623                 // flags
624                 Type.VOID, // return type
625                 new Type[] { new ArrayType(Type.STRING, 1) }, // argument
626                 // types
627                 new String[] { "argv" }, // arg names
628                 methodname, "HelloWorld", // method, class
629                 il, cp);
630     }
631 
632     public AnnotationEntryGen createSimpleVisibleAnnotation(final ConstantPoolGen cp)
633     {
634         final SimpleElementValueGen evg = new SimpleElementValueGen(
635                 ElementValueGen.PRIMITIVE_INT, cp, 4);
636         final ElementValuePairGen nvGen = new ElementValuePairGen("id", evg, cp);
637         final ObjectType t = new ObjectType("SimpleAnnotation");
638         final List<ElementValuePairGen> elements = new ArrayList<>();
639         elements.add(nvGen);
640         final AnnotationEntryGen a = new AnnotationEntryGen(t, elements, true, cp);
641         return a;
642     }
643 
644     public AnnotationEntryGen createFruitAnnotation(final ConstantPoolGen cp,
645             final String aFruit)
646     {
647         final SimpleElementValueGen evg = new SimpleElementValueGen(
648                 ElementValueGen.STRING, cp, aFruit);
649         final ElementValuePairGen nvGen = new ElementValuePairGen("fruit", evg, cp);
650         final ObjectType t = new ObjectType("SimpleStringAnnotation");
651         final List<ElementValuePairGen> elements = new ArrayList<>();
652         elements.add(nvGen);
653         return new AnnotationEntryGen(t, elements, true, cp);
654     }
655 
656     public AnnotationEntryGen createCombinedAnnotation(final ConstantPoolGen cp)
657     {
658         // Create an annotation instance
659         final AnnotationEntryGen a = createSimpleVisibleAnnotation(cp);
660         final ArrayElementValueGen array = new ArrayElementValueGen(cp);
661         array.addElement(new AnnotationElementValueGen(a, cp));
662         final ElementValuePairGen nvp = new ElementValuePairGen("value", array, cp);
663         final List<ElementValuePairGen> elements = new ArrayList<>();
664         elements.add(nvp);
665         return new AnnotationEntryGen(new ObjectType("CombinedAnnotation"),
666                 elements, true, cp);
667     }
668 
669     public AnnotationEntryGen createSimpleInvisibleAnnotation(final ConstantPoolGen cp)
670     {
671         final SimpleElementValueGen evg = new SimpleElementValueGen(
672                 ElementValueGen.PRIMITIVE_INT, cp, 4);
673         final ElementValuePairGen nvGen = new ElementValuePairGen("id", evg, cp);
674         final ObjectType t = new ObjectType("SimpleAnnotation");
675         final List<ElementValuePairGen> elements = new ArrayList<>();
676         elements.add(nvGen);
677         final AnnotationEntryGen a = new AnnotationEntryGen(t, elements, false, cp);
678         return a;
679     }
680 }