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 = "Andy"; 50 * } 51 * catch (IOException e) 52 * { 53 * return; 54 * } 55 * System.out.println("Hello, " + 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 */ testGenerateClassLevelAnnotations()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 */ testGenerateMethodLevelAnnotations1()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 */ testGenerateMethodLevelAnnotations2()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 */ testTransformClassToClassGen_SimpleTypes()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 */ testTransformClassToClassGen_EnumType()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 */ testTransformClassToClassGen_ArrayAndAnnotationTypes()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 assertMethodAnnotations(final Method method, final int expectedNumberAnnotations, final int nExpectedArrayValues)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 assertArrayElementValue(final int nExpectedArrayValues, final AnnotationEntry anno)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 assertParameterAnnotations(final Method method, final int... expectedNumberOfParmeterAnnotations)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 assertSimpleElementValue(final AnnotationEntry anno)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 */ testTransformComplexClassToClassGen()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 */ testModifyingClasses1()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 */ testModifyingClasses2()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 dumpClass(final ClassGen cg, final String fname)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 dumpClass(final ClassGen cg, final String dir, final String fname)409 private void dumpClass(final ClassGen cg, final String dir, final String fname) 410 { 411 dumpClass(cg, dir + File.separator + fname); 412 } 413 buildClassContentsWithAnnotatedMethods(final ClassGen cg, final ConstantPoolGen cp, final InstructionList il)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 buildClassContents(final ClassGen cg, final ConstantPoolGen cp, final InstructionList il)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 getClassFrom(final String where, final String clazzname)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 createClassGen(final String classname)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 createMethodGen(final String methodname, final InstructionList il, final ConstantPoolGen cp)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 createSimpleVisibleAnnotation(final ConstantPoolGen cp)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 createFruitAnnotation(final ConstantPoolGen cp, final String aFruit)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 createCombinedAnnotation(final ConstantPoolGen cp)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 createSimpleInvisibleAnnotation(final ConstantPoolGen cp)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 } 681