    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}