jodd.proxetta.asm.package-info Maven / Gradle / Ivy
// Copyright (c) 2003-present, Jodd Team (http://jodd.org)
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
/**
*
* This package assembles proxy classes. You will probably like to keep out from this code:)
*
* Replacements and modification rules
*
* During creation of proxy methods, several replacement and modification happens in order to produce valid proxy subclass.
* Here is the list of all such rules.
*
* Add all constructors [A1]
*
* Proxy subclass must contain all constructors as target subclass. New constructors simply
* delegates invocation to the super class. All constructor annotations are copied.
*
* Add the last method in chain [A2]
*
* Last method in proxy chain is the one that simply delegates the invocation to the target method in super class.
*
* Add all type annotations [A3]
*
* Proxy subclass must contain all type annotations as the target one.
*
* Copy all annotations to the first method in proxy method chain [A4]
*
* Proxy methods must contain all type annotations as the target one.
*
* Fix the offset of local variables [F1]
*
* Offset of all local variables has to be incremented by the size of target method argument list.
* Size of arguments list is the number of 32bit words used by arguments on stack, which means that
* all types has length of 1 word, except Long
and Double
that weight 2 words
* (or one dword).
*
*
*
* iconst_1
* istore_1
*
*
*
*
* iconst_1
* istore_13
*
*
*
* Here is the order of local variables:
*
* - 0 - always 'this'
* - arguments (if exist)
* - locals (if exist)
*
*
* Therefore, index 0 is left as it is and offset will not be added to it.
*
*
* Replace ProxyTarget.argsCount [R2]
*
* Call to ProxyTarget.argsCount()
has to be replaces with hardcoded arguments count.
* Method call is simply replaces with appropriate load instruction: iload_n
where n is in [0. 5];
* bipush n
where n is in byte range; or sipush n
when n is in integer range.
*
*
* Replace ProxyTarget.getArgType [R3]
*
* Call to ProxyTarget.getArgType(int )
has to be replaces with hardcoded argument Class, where argument
* index is provided as an argument for ProxyTarget.getArgType(int )
.
* Method call is replaced with getClass()
call on specified argument. If argument is an primitive
* then method is replaced with reading the TYPE
attribute of appropriate wrapper.
*
*
* One caveat: opcode for pushing argument offset to stack is not removed from the bytecode,
* due to performance issues of class creation. Instead, this value is poped from the stack before method call is replaced.
* It is assumed that this value is an integer.
*
*
*
* iconst_1
* invokestatic package/ProxyTarget.getArgClass
* astore_1
* iconst_2
* invokestatic package/ProxyTarget.getArgClass
* astore_2
*
*
*
*
* (iconst_1
* pop)
* aload_1
* invokevirtual java/lang/Object.getClass
* astore 13
* (iconst_2
* pop)
* getstatic java/lang/Byte.TYPE
* astore 14
*
*
*
* Replace ProxyTarget.getArg [R4]
*
* Call to ProxyTarget.getArg(int )
has to be replaces with hardcoded argument value, where
* index is provided as an argument for ProxyTarget.getArg(int )
.
* If argument is a primitive, its wrapper object will be created.
*
*
*
* iconst_1
* invokestatic package/ProxyTarget.getArg
* astore_1
* bipush 6
* invokestatic package/ProxyTarget.getArg
* astore_3
*
*
*
*
* aload_1
* astore_13
* lload 6
* invokestatic java/lang/Long.<init>
* astore 14
*
*
*
* Replace ProxyTarget.setArg [R5]
*
* Call to ProxyTarget.setArg(Object, int )
has to be replaces with hardcoded setting of the argument value,
* where index is provided as an argument for ProxyTarget.setArg(Object, int )
.
* If argument is a primitive, its wrapper object must be provided.
*
* Replace ProxyTarget.createArgsArray [R6]
*
* Call to ProxyTarget.createArgsArray()
has to be replaces with hardcoded creation of an object array,
* where elements are target method arguments. Primitive arguments are wrapped.
*
* Replace ProxyTarget.invoke [R7]
*
* Call to ProxyTarget.invokeAndGetResult()
has to be replaced with call to super target method. Since target methods
* may have one or more arguments, it is required to push all arguments to the stack prior to call of super target method.
* Note that aload_0
is always the first instruction (load this
), no matter how many arguments there are.
*
*
*
* invokestatic package/ProxyTarget.invoke
*
*
*
*
* aload_0
* aload_1
* iload_2
* ...
* invokespecial package/Target.method
*
*
*
* Situation here is a bit more complicated since return value must be provided, so the following fixes has to be
* applied, too.
*
* Fix POP for ProxyTarget.invoke [F3]
*
* When ProxyTarget.invoke()
is invoked without assignment, POP/POP2
instruction
* is added afterwards, to remove the value from the stack. For targets that do not return void, this opcode
* has to be fixed, i.e. removed. (Fact is that targets that return void, do not have POP:).
*
* Fix return value and Fix ASTORE for ProxyTarget.invoke [F4]
*
* When ProxyTarget.invoke()
is invoked with assignment, xSTORE
instruction
* is added afterwards, to assign return value to a local variable. Therefore, it has to be loaded again on stack
* before return.
*
* Creates all return values, performs casting for small types.
* Replace ProxyTarget.getTargetClass [R9]
*
* Returns the target class.
*
* Replace ProxyTarget.getTargetMethodName [R10]
*
* Returns target method name.
*
* Replace ProxyTarget.getReturnType [R11]
*
* Returns return type of the target method or null
if metod returns void.
*
* Fix field access [F5]
*
* Access to advice's fields has to be replaced with access to local fields. In relation with [A5].
*
* Copy advice's fields to proxy [A5]
*
* All fields from advice has to be copied to proxy, with proxy index added to the name, to prevent duplicate names.
*
* Copy and fix advice static constructor [A6/F6]
*
* Static block of an advice should be copied to the proxy, with fixed field access (see F5).
*
* Copy and fix advices default constructors [A7/F7]
*
* Advice's constructor will be copied to regular methods, except first two instructions (calling super constructor) will be
* ignored. Field access will be fixed (see F5).
*
*/
package jodd.proxetta.asm;