Package jodd.proxetta.asm
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).
-
Class Summary Class Description AnnotationReader Reads annotation inner data.InvokeClassBuilder Invocation replacer class adapter.InvokeReplacerMethodAdapter Invocation replacer method adapter.MethodSignatureVisitor Resolves method signature and holds all information.ProxettaAsmUtil Various ASM utilities used byProxetta
.ProxettaClassBuilder Proxetta class builder.ProxettaCtorBuilder Destination ctor builderProxettaMethodBuilder ProxettaWrapperClassBuilder TargetClassInfoReader Reads info from target class.TypeInfoImpl Implementation ofTypeInfo
.WorkData Holds various information about the current process of making proxy.