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}