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