    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}