Skip navigation links

Package jodd.proxetta.asm

This package assembles proxy classes.

See: Description

Package jodd.proxetta.asm Description

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:

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).

Skip navigation links

Copyright © 2003-present Jodd Team