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 }