    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    }