001 // Copyright 2011 The Apache Software Foundation 002 // 003 // Licensed under the Apache License, Version 2.0 (the "License"); 004 // you may not use this file except in compliance with the License. 005 // You may obtain a copy of the License at 006 // 007 // http://www.apache.org/licenses/LICENSE-2.0 008 // 009 // Unless required by applicable law or agreed to in writing, software 010 // distributed under the License is distributed on an "AS IS" BASIS, 011 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 012 // See the License for the specific language governing permissions and 013 // limitations under the License. 014 015 package org.apache.tapestry5.plastic; 016 017 import java.lang.reflect.Method; 018 019 /** 020 * Simplifies the generation of method instructions for a particular method (or constructor), allowing bytecode to be 021 * created with a friendlier API that focuses on Java type names (names as they would appear in Java source) rather than 022 * JVM descriptors or internal names. In some limited cases, types may be specified as Java Class instances as well. 023 * In addition, there is good support for primitive type boxing and unboxing. 024 * <p/> 025 * Most methods return the same instance of InstructionBuilder, allowing for a "fluid" API. 026 * <p/> 027 * More complex functionality, such as {@linkplain #startTryCatch(TryCatchCallback) 028 * try/catch blocks}, is more like a DSL (domain specific language), and is based on callbacks. This looks better in 029 * Groovy and will be more reasonable once JDK 1.8 closures are available; in the meantime, it means some deeply nested 030 * inner classes, but helps ensure that correct bytecode is generated and helps to limit the amount of bookkeeping is 031 * necessary on the part of code using InstructionBuilder. 032 */ 033 @SuppressWarnings("rawtypes") 034 public interface InstructionBuilder 035 { 036 /** 037 * Returns the default value for the method, which may be null, or a specific primitive value. 038 */ 039 @Opcodes("ACONST_NULL, LCONST_0, FCONST_0, DCONST_0, ICONST_0, RETURN, ARETURN, IRETURN, FRETURN, LRETURN, DRETURN") 040 InstructionBuilder returnDefaultValue(); 041 042 /** 043 * Loads this onto the stack. 044 */ 045 @Opcodes("ALOAD") 046 InstructionBuilder loadThis(); 047 048 /** 049 * Loads the null constant onto the stack. 050 */ 051 @Opcodes("ACONST_NULL") 052 InstructionBuilder loadNull(); 053 054 /** 055 * Loads an argument onto the stack, using the opcode appropriate to the argument's type. In addition 056 * this automatically adjusts for arguments of primitive type long or double (which take up two 057 * local variable indexes, rather than one as for all other types) 058 * 059 * @param index to argument (0 is the first argument, not this) 060 */ 061 @Opcodes("ALOAD, ILOAD, LLOAD, FLOAD, DLOAD") 062 InstructionBuilder loadArgument(int index); 063 064 /** 065 * Loads all arguments for the current method onto the stack; this is used when invoking a method 066 * that takes the exact same parameters (often, a super-class implementation). A call to {@link #loadThis()} (or 067 * some other way of identifying the target method) should precede this call. 068 */ 069 @Opcodes("ALOAD, ILOAD, LLOAD, FLOAD, DLOAD") 070 InstructionBuilder loadArguments(); 071 072 /** 073 * Invokes an instance method of a base class, or a private method of a class, using the target object 074 * and parameters already on the stack. Leaves the result on the stack (unless its a void method). 075 * 076 * @param containingClassName class name containing the method 077 * @param description describes the method name, parameters and return type 078 */ 079 @Opcodes("INVOKESPECIAL") 080 InstructionBuilder invokeSpecial(String containingClassName, MethodDescription description); 081 082 /** 083 * Invokes a standard virtual method. 084 */ 085 @Opcodes("INVOKEVIRTUAL") 086 InstructionBuilder invokeVirtual(String className, String returnType, String methodName, String... argumentTypes); 087 088 @Opcodes("INVOKEVIRTUAL") 089 InstructionBuilder invokeVirtual(PlasticMethod method); 090 091 /** 092 * Invokes a standard virtual method. 093 */ 094 @Opcodes("INVOKEINTERFACE") 095 InstructionBuilder invokeInterface(String interfaceName, String returnType, String methodName, 096 String... argumentTypes); 097 098 /** 099 * Automatically invokes an interface or virtual method. Remember to use {@link #invokeConstructor(Class, Class...)} 100 * for constructors and {@link #invokeSpecial(String, MethodDescription)} for private methods. 101 */ 102 @Opcodes("INVOKEVIRTUAL, INVOKEINTERFACE") 103 InstructionBuilder invoke(Class clazz, Class returnType, String methodName, Class... argumentTypes); 104 105 /** 106 * Automatically invokes an interface or virtual method. Remember to use {@link #invokeConstructor(Class, Class...)} 107 * for constructors and {@link #invokeSpecial(String, MethodDescription)} for private methods. 108 */ 109 InstructionBuilder invoke(Method method); 110 111 /** 112 * Invokes a static method of a class. 113 */ 114 @Opcodes("INVOKESTATIC") 115 InstructionBuilder invokeStatic(Class clazz, Class returnType, String methodName, Class... argumentTypes); 116 117 /** 118 * Returns the top value on the stack. For void methods, no value should 119 * be on the stack and the method will simply return. 120 */ 121 @Opcodes("ARETURN, IRETURN, LRETURN, FRETURN, DRETURN") 122 InstructionBuilder returnResult(); 123 124 /** 125 * If the type name is a primitive type, adds code to box the type into the equivalent wrapper type, using static 126 * methods on the wrapper type. Does nothing if the type is not primitive, or type void. 127 */ 128 @Opcodes("INVOKESTATIC") 129 InstructionBuilder boxPrimitive(String typeName); 130 131 /** 132 * Unboxes a wrapper type to a primitive type if typeName is a primitive type name (the value on the stack 133 * should be the corresponding wrapper type instance). Does nothing for non-primitive types. 134 * 135 * @param typeName possibly primitive type name 136 */ 137 @Opcodes("INVOKEVIRTUAL") 138 InstructionBuilder unboxPrimitive(String typeName); 139 140 /** 141 * Loads an instance field onto the stack. The object containing the field should already be loaded onto the stack 142 * (usually, via {@link #loadThis()}). 143 * 144 * @param className name of class containing the field 145 * @param fieldName name of the field 146 * @param typeName type of field 147 */ 148 @Opcodes("GETFIELD") 149 InstructionBuilder getField(String className, String fieldName, String typeName); 150 151 /** 152 * Loads an instance or static field onto the stack. The plastic class instance containing the field should already be loaded 153 * onto the stack (usually, via {@link #loadThis()}). 154 * 155 * @param field identifies name, type and container of field to load 156 */ 157 @Opcodes("GETFIELD") 158 InstructionBuilder getField(PlasticField field); 159 160 /** 161 * Loads a field onto the stack. This version is used when the 162 * field type is known at build time, rather than discovered at runtime. 163 * 164 * @param className name of class containing the field 165 * @param fieldName name of the field 166 * @param fieldType type of field 167 */ 168 @Opcodes("GETFIELD") 169 InstructionBuilder getField(String className, String fieldName, Class fieldType); 170 171 /** 172 * Gets a static field; does not consume a value from the stack, but pushes the fields' value onto the stack. 173 * 174 * @param className name of class containing the field 175 * @param fieldName name of the field 176 * @param fieldType type of field 177 */ 178 @Opcodes("GETSTATIC") 179 InstructionBuilder getStaticField(String className, String fieldName, Class fieldType); 180 181 /** 182 * Gets a static field; does not consume a value from the stack, but pushes the fields' value onto the stack. 183 * 184 * @param className name of class containing the field 185 * @param fieldName name of the field 186 * @param typeName type of field 187 */ 188 @Opcodes("GETSTATIC") 189 InstructionBuilder getStaticField(String className, String fieldName, 190 String typeName); 191 192 193 /** 194 * Sets a static field; the new field value should be on top of the stack. 195 * 196 * @param className name of class containing the field 197 * @param fieldName name of the field 198 * @param fieldType type of field 199 */ 200 @Opcodes("PUTSTATIC") 201 InstructionBuilder putStaticField(String className, String fieldName, Class fieldType); 202 203 /** 204 * Sets a static field; the new field value should be on top of the stack. 205 * 206 * @param className name of class containing the field 207 * @param fieldName name of the field 208 * @param typeName type of field 209 */ 210 @Opcodes("PUTSTATIC") 211 InstructionBuilder putStaticField(String className, String fieldName, 212 String typeName); 213 214 /** 215 * Expects the stack to contain the instance to update, and the value to store into the field. 216 */ 217 @Opcodes("PUTFIELD") 218 InstructionBuilder putField(String className, String fieldName, String typeName); 219 220 @Opcodes("PUTFIELD") 221 InstructionBuilder putField(String className, String fieldName, Class fieldType); 222 223 /** 224 * Loads a value from an array object, which must be the top element of the stack. 225 * 226 * @param index constant index into the array 227 * @param elementType the type name of the elements of the array 228 * <strong>Note: currently only reference types (objects and arrays) are supported, not 229 * primitives</strong> 230 */ 231 @Opcodes("LDC, AALOAD") 232 InstructionBuilder loadArrayElement(int index, String elementType); 233 234 /** 235 * Loads a value from an array object. The stack should have the array at depth 1, and an array index 236 * on top. Only object arrays (not arrays of primitives) are supported. 237 */ 238 @Opcodes("AALOAD") 239 InstructionBuilder loadArrayElement(); 240 241 /** 242 * Adds a check that the object on top of the stack is assignable to the indicated class. 243 * 244 * @param className class to cast to 245 */ 246 @Opcodes("CHECKCAST") 247 InstructionBuilder checkcast(String className); 248 249 @Opcodes("CHECKCAST") 250 InstructionBuilder checkcast(Class clazz); 251 252 /** 253 * Defines the start of a block that can have exception handlers and finally blocks applied. 254 * Continue using this InstructionBuilder to define code inside the block, then call 255 * methods on the InstructionBlock to define the end of the block and set up handlers. 256 * 257 * @param tryCatchCallback allows generation of try, catch, and finally clauses 258 */ 259 InstructionBuilder startTryCatch(TryCatchCallback tryCatchCallback); 260 261 /** 262 * Creates a new, uninitialized instance of the indicated class. This should be followed 263 * by code to call the new instance's constructor. 264 * 265 * @param className of class to instantiate 266 */ 267 @Opcodes("NEW") 268 InstructionBuilder newInstance(String className); 269 270 /** 271 * A convenience version of {@link #newInstance(String)} used when the class is known 272 * at build time. 273 * 274 * @param clazz to instantiate 275 */ 276 @Opcodes("NEW") 277 InstructionBuilder newInstance(Class clazz); 278 279 /** 280 * Invokes a constructor on a class. The instance should already be on the stack, followed 281 * by the right number and type of parameters. Note that a constructor acts like a void method, 282 * so you will often follow the sequence: newInstance(), dupe(0), invokeConstructor() so that a reference 283 * to the instance is left on the stack.F 284 * 285 * @param className the class containing the constructor 286 * @param argumentTypes java type names for each argument of the constructor 287 */ 288 @Opcodes("INVOKESPECIAL") 289 InstructionBuilder invokeConstructor(String className, String... argumentTypes); 290 291 @Opcodes("INVOKESPECIAL") 292 InstructionBuilder invokeConstructor(Class clazz, Class... argumentTypes); 293 294 /** 295 * Duplicates the top object on the stack, placing the result at some depth. 296 * 297 * @param depth 0 (DUP), 1 (DUP_X1) or 2 (DUP_X2) 298 */ 299 @Opcodes("DUP, DUP_X1, DUP_X2") 300 InstructionBuilder dupe(int depth); 301 302 /** 303 * Duplicates a wide value (a primitive long or double). 304 */ 305 @Opcodes("DUP2") 306 InstructionBuilder dupeWide(); 307 308 /** 309 * Pops a wide value (a primitive long or double). 310 */ 311 @Opcodes("POP2") 312 InstructionBuilder popWide(); 313 314 /** 315 * Duplicates the top object on the stack. Commonly used with {@link #when(Condition, WhenCallback)}. 316 * 317 * @see #dupe(int) 318 */ 319 @Opcodes("DUP") 320 InstructionBuilder dupe(); 321 322 /** 323 * Discards the top value on the stack. Assumes the value is a single word value: an object reference, or a small 324 * primitive) and not a double or long. 325 */ 326 InstructionBuilder pop(); 327 328 /** 329 * Swaps the top element of the stack with the next element down. Note that this can cause problems if the top 330 * element on the stack 331 * is a long or double. 332 */ 333 @Opcodes("SWAP") 334 InstructionBuilder swap(); 335 336 /** 337 * Loads a constant value 338 * 339 * @param constant Integer, Float, Double, Long, String or null 340 */ 341 @Opcodes("LDC, ICONST_*, LCONST_*, FCONST_*, DCONST_*, ACONST_NULL") 342 InstructionBuilder loadConstant(Object constant); 343 344 /** 345 * Loads a Java type (a Class instance) as a constant. This assumes the type name is the name of class (or array) 346 * but not a primitive type. 347 * 348 * @param typeName Java class name 349 */ 350 @Opcodes("LDC") 351 InstructionBuilder loadTypeConstant(String typeName); 352 353 /** 354 * Loads a Java type (a Class instance) as a constant. This assumes the type name is the name of class (or array) 355 * but not a primitive type. 356 * 357 * @param type Java type to load as a constant 358 */ 359 @Opcodes("LDC") 360 InstructionBuilder loadTypeConstant(Class type); 361 362 /** 363 * Casts the object on top of the stack to the indicated type. For primitive types, casts to the wrapper type 364 * and invokes the appropriate unboxing static method call, leaving a primitive type value on the stack. 365 * 366 * @param typeName to cast or unbox to 367 */ 368 @Opcodes("CHECKCAST, INVOKEVIRTUAL") 369 InstructionBuilder castOrUnbox(String typeName); 370 371 /** 372 * Throws an exception with a fixed message. Assumes the exception class includes a constructor that takes a single 373 * string. 374 * 375 * @param className name of exception class to instantiate 376 * @param message message (passed as first and only parameter to constructor) 377 */ 378 @Opcodes("NEW, DUP, LDC, INVOKESPECIAL, ATHROW") 379 InstructionBuilder throwException(String className, String message); 380 381 @Opcodes("NEW, DUP, LDC, INVOKESPECIAL, ATHROW") 382 InstructionBuilder throwException(Class<? extends Throwable> exceptionType, String message); 383 384 /** 385 * Throws the exception on the top of the stack. 386 */ 387 @Opcodes("ATHROW") 388 InstructionBuilder throwException(); 389 390 /** 391 * Starts a switch statement. 392 * 393 * @param min the minimum value to match against 394 * @param max the maximum value to match against 395 */ 396 @Opcodes("TABLESWITCH") 397 InstructionBuilder startSwitch(int min, int max, SwitchCallback callback); 398 399 /** 400 * Starts a block where the given name is active. 401 * 402 * @param type type of local variable 403 * @param callback generates code used when variable is in effect 404 */ 405 InstructionBuilder startVariable(String type, LocalVariableCallback callback); 406 407 /** 408 * Stores the value on top of the stack to a local variable (previously defined by 409 * {@link #startVariable(String, LocalVariableCallback)}. 410 */ 411 @Opcodes("ASTORE, ISTORE, LSTORE, FSTORE, DSTORE") 412 InstructionBuilder storeVariable(LocalVariable variable); 413 414 /** 415 * Loads a value from a local variable and pushes it onto the stack. The variable is defined by 416 * {@link #startVariable(String, LocalVariableCallback)}. 417 */ 418 @Opcodes("ALOAD, ILOAD, LLOAD, FLOAD, DLOAD") 419 InstructionBuilder loadVariable(LocalVariable variable); 420 421 /** 422 * Executes conditional code based on a {@link Condition}. The testing opcodes all pop 423 * the value off the stack, so this is usually preceded by {@link #dupe(int) dupe(0)}. 424 * 425 * @param condition defines true and false cases 426 * @param callback provides code for true and false blocks 427 * @return this builder 428 */ 429 @Opcodes("IFEQ, etc., GOTO") 430 InstructionBuilder when(Condition condition, WhenCallback callback); 431 432 /** 433 * Simplified version of {@link #when(Condition, WhenCallback)} that 434 * simply executes the callback code when the condition is true and does nothing 435 * if the condition is false (the more general case). 436 * <p/> 437 * The testing opcodes all pop the value off the stack, so this is usually preceded by {@link #dupe(int) dupe(0)}. 438 * 439 * @param condition to evaluate 440 * @param ifTrue generates code for when condition is true 441 * @return this builder 442 */ 443 @Opcodes("IFEQ, etc., GOTO") 444 InstructionBuilder when(Condition condition, InstructionBuilderCallback ifTrue); 445 446 /** 447 * Implements a simple loop based on a condition. First the {@linkplain WhileCallback#buildTest(InstructionBuilder)} 448 * code is executed, then the condition is evaluated (which will consume at least the top value on the stack). 449 * When the condition is false, the loop is exited. When the condition is true, the code defined by 450 * {@link WhileCallback#buildBody(InstructionBuilder)} is executed, and then a GOTO back to the test code. 451 * 452 * @param condition 453 * @param callback 454 * @return this builder 455 */ 456 @Opcodes("IFEQ, etc., GOTO") 457 InstructionBuilder doWhile(Condition condition, WhileCallback callback); 458 459 /** 460 * Expects an array to be the top value on the stack. Iterates the array. 461 * The callback generates code that will have each successive value from the array 462 * as the top value on the stack. Creates a variable to store the loop index. 463 * 464 * @param callback to invoke. The element will be the top value on the stack. The callback is responsible 465 * for removing it from the stack. 466 * @return this builder 467 */ 468 @Opcodes("IINC, ARRAYLENGTH, IFEQ, etc., GOTO") 469 InstructionBuilder iterateArray(InstructionBuilderCallback callback); 470 471 /** 472 * Increments a local integer variable. 473 * 474 * @return this builder 475 */ 476 @Opcodes("IINC") 477 InstructionBuilder increment(LocalVariable variable); 478 479 /** 480 * Expects the top object on the stack to be an array. Replaces it with the length of that array. 481 */ 482 @Opcodes("ARRAYLENGTH") 483 InstructionBuilder arrayLength(); 484 485 /** 486 * Special comparison logic for primitive float, double and long. Expect two matching wide values 487 * on the stack. Reduces the two wide values to a single int value: -1, 0, or 1 depending on whether the deeper 488 * value is less than, equal to, or greater than the top value on the stack. 489 * 490 * @param typeName 491 */ 492 @Opcodes("LCMP, FCMPL, DCMPL") 493 InstructionBuilder compareSpecial(String typeName); 494 }