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 * Adds a check that the object on top of the stack is assignable to the indicated class. 252 * 253 * @param className class to cast to 254 * @since 5.8.4 255 */ 256 @Opcodes("CHECKCAST") 257 InstructionBuilder instanceOf(String className); 258 259 /** 260 * Adds a check that the object on top of the stack is assignable to the indicated class. 261 * @since 5.8.4 262 */ 263 @Opcodes("CHECKCAST") 264 InstructionBuilder instanceOf(Class clazz); 265 266 /** 267 * Defines the start of a block that can have exception handlers and finally blocks applied. 268 * Continue using this InstructionBuilder to define code inside the block, then call 269 * methods on the InstructionBlock to define the end of the block and set up handlers. 270 * 271 * @param tryCatchCallback allows generation of try, catch, and finally clauses 272 */ 273 InstructionBuilder startTryCatch(TryCatchCallback tryCatchCallback); 274 275 /** 276 * Creates a new, uninitialized instance of the indicated class. This should be followed 277 * by code to call the new instance's constructor. 278 * 279 * @param className of class to instantiate 280 */ 281 @Opcodes("NEW") 282 InstructionBuilder newInstance(String className); 283 284 /** 285 * A convenience version of {@link #newInstance(String)} used when the class is known 286 * at build time. 287 * 288 * @param clazz to instantiate 289 */ 290 @Opcodes("NEW") 291 InstructionBuilder newInstance(Class clazz); 292 293 /** 294 * Invokes a constructor on a class. The instance should already be on the stack, followed 295 * by the right number and type of parameters. Note that a constructor acts like a void method, 296 * so you will often follow the sequence: newInstance(), dupe(0), invokeConstructor() so that a reference 297 * to the instance is left on the stack.F 298 * 299 * @param className the class containing the constructor 300 * @param argumentTypes java type names for each argument of the constructor 301 */ 302 @Opcodes("INVOKESPECIAL") 303 InstructionBuilder invokeConstructor(String className, String... argumentTypes); 304 305 @Opcodes("INVOKESPECIAL") 306 InstructionBuilder invokeConstructor(Class clazz, Class... argumentTypes); 307 308 /** 309 * Duplicates the top object on the stack, placing the result at some depth. 310 * 311 * @param depth 0 (DUP), 1 (DUP_X1) or 2 (DUP_X2) 312 */ 313 @Opcodes("DUP, DUP_X1, DUP_X2") 314 InstructionBuilder dupe(int depth); 315 316 /** 317 * Duplicates a wide value (a primitive long or double). 318 */ 319 @Opcodes("DUP2") 320 InstructionBuilder dupeWide(); 321 322 /** 323 * Pops a wide value (a primitive long or double). 324 */ 325 @Opcodes("POP2") 326 InstructionBuilder popWide(); 327 328 /** 329 * Duplicates the top object on the stack. Commonly used with {@link #when(Condition, WhenCallback)}. 330 * 331 * @see #dupe(int) 332 */ 333 @Opcodes("DUP") 334 InstructionBuilder dupe(); 335 336 /** 337 * Discards the top value on the stack. Assumes the value is a single word value: an object reference, or a small 338 * primitive) and not a double or long. 339 */ 340 InstructionBuilder pop(); 341 342 /** 343 * Swaps the top element of the stack with the next element down. Note that this can cause problems if the top 344 * element on the stack 345 * is a long or double. 346 */ 347 @Opcodes("SWAP") 348 InstructionBuilder swap(); 349 350 /** 351 * Loads a constant value 352 * 353 * @param constant Integer, Float, Double, Long, String or null 354 */ 355 @Opcodes("LDC, ICONST_*, LCONST_*, FCONST_*, DCONST_*, ACONST_NULL") 356 InstructionBuilder loadConstant(Object constant); 357 358 /** 359 * Loads a Java type (a Class instance) as a constant. This assumes the type name is the name of class (or array) 360 * but not a primitive type. 361 * 362 * @param typeName Java class name 363 */ 364 @Opcodes("LDC") 365 InstructionBuilder loadTypeConstant(String typeName); 366 367 /** 368 * Loads a Java type (a Class instance) as a constant. This assumes the type name is the name of class (or array) 369 * but not a primitive type. 370 * 371 * @param type Java type to load as a constant 372 */ 373 @Opcodes("LDC") 374 InstructionBuilder loadTypeConstant(Class type); 375 376 /** 377 * Casts the object on top of the stack to the indicated type. For primitive types, casts to the wrapper type 378 * and invokes the appropriate unboxing static method call, leaving a primitive type value on the stack. 379 * 380 * @param typeName to cast or unbox to 381 */ 382 @Opcodes("CHECKCAST, INVOKEVIRTUAL") 383 InstructionBuilder castOrUnbox(String typeName); 384 385 /** 386 * Throws an exception with a fixed message. Assumes the exception class includes a constructor that takes a single 387 * string. 388 * 389 * @param className name of exception class to instantiate 390 * @param message message (passed as first and only parameter to constructor) 391 */ 392 @Opcodes("NEW, DUP, LDC, INVOKESPECIAL, ATHROW") 393 InstructionBuilder throwException(String className, String message); 394 395 @Opcodes("NEW, DUP, LDC, INVOKESPECIAL, ATHROW") 396 InstructionBuilder throwException(Class<? extends Throwable> exceptionType, String message); 397 398 /** 399 * Throws the exception on the top of the stack. 400 */ 401 @Opcodes("ATHROW") 402 InstructionBuilder throwException(); 403 404 /** 405 * Starts a switch statement. 406 * 407 * @param min the minimum value to match against 408 * @param max the maximum value to match against 409 */ 410 @Opcodes("TABLESWITCH") 411 InstructionBuilder startSwitch(int min, int max, SwitchCallback callback); 412 413 /** 414 * Starts a block where the given name is active. 415 * 416 * @param type type of local variable 417 * @param callback generates code used when variable is in effect 418 */ 419 InstructionBuilder startVariable(String type, LocalVariableCallback callback); 420 421 /** 422 * Stores the value on top of the stack to a local variable (previously defined by 423 * {@link #startVariable(String, LocalVariableCallback)}. 424 */ 425 @Opcodes("ASTORE, ISTORE, LSTORE, FSTORE, DSTORE") 426 InstructionBuilder storeVariable(LocalVariable variable); 427 428 /** 429 * Loads a value from a local variable and pushes it onto the stack. The variable is defined by 430 * {@link #startVariable(String, LocalVariableCallback)}. 431 */ 432 @Opcodes("ALOAD, ILOAD, LLOAD, FLOAD, DLOAD") 433 InstructionBuilder loadVariable(LocalVariable variable); 434 435 /** 436 * Executes conditional code based on a {@link Condition}. The testing opcodes all pop 437 * the value off the stack, so this is usually preceded by {@link #dupe(int) dupe(0)}. 438 * 439 * @param condition defines true and false cases 440 * @param callback provides code for true and false blocks 441 * @return this builder 442 */ 443 @Opcodes("IFEQ, etc., GOTO") 444 InstructionBuilder when(Condition condition, WhenCallback callback); 445 446 /** 447 * Simplified version of {@link #when(Condition, WhenCallback)} that 448 * simply executes the callback code when the condition is true and does nothing 449 * if the condition is false (the more general case). 450 * 451 * The testing opcodes all pop the value off the stack, so this is usually preceded by {@link #dupe(int) dupe(0)}. 452 * 453 * @param condition to evaluate 454 * @param ifTrue generates code for when condition is true 455 * @return this builder 456 */ 457 @Opcodes("IFEQ, etc., GOTO") 458 InstructionBuilder when(Condition condition, InstructionBuilderCallback ifTrue); 459 460 /** 461 * Implements a simple loop based on a condition. First the {@linkplain WhileCallback#buildTest(InstructionBuilder)} 462 * code is executed, then the condition is evaluated (which will consume at least the top value on the stack). 463 * When the condition is false, the loop is exited. When the condition is true, the code defined by 464 * {@link WhileCallback#buildBody(InstructionBuilder)} is executed, and then a GOTO back to the test code. 465 * 466 * @param condition 467 * @param callback 468 * @return this builder 469 */ 470 @Opcodes("IFEQ, etc., GOTO") 471 InstructionBuilder doWhile(Condition condition, WhileCallback callback); 472 473 /** 474 * Expects an array to be the top value on the stack. Iterates the array. 475 * The callback generates code that will have each successive value from the array 476 * as the top value on the stack. Creates a variable to store the loop index. 477 * 478 * @param callback to invoke. The element will be the top value on the stack. The callback is responsible 479 * for removing it from the stack. 480 * @return this builder 481 */ 482 @Opcodes("IINC, ARRAYLENGTH, IFEQ, etc., GOTO") 483 InstructionBuilder iterateArray(InstructionBuilderCallback callback); 484 485 /** 486 * Increments a local integer variable. 487 * 488 * @return this builder 489 */ 490 @Opcodes("IINC") 491 InstructionBuilder increment(LocalVariable variable); 492 493 /** 494 * Expects the top object on the stack to be an array. Replaces it with the length of that array. 495 */ 496 @Opcodes("ARRAYLENGTH") 497 InstructionBuilder arrayLength(); 498 499 /** 500 * Special comparison logic for primitive float, double and long. Expect two matching wide values 501 * on the stack. Reduces the two wide values to a single int value: -1, 0, or 1 depending on whether the deeper 502 * value is less than, equal to, or greater than the top value on the stack. 503 * 504 * @param typeName 505 */ 506 @Opcodes("LCMP, FCMPL, DCMPL") 507 InstructionBuilder compareSpecial(String typeName); 508}