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    }