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
015package org.apache.tapestry5.plastic;
016
017import 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")
034public 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}