Proxetta creates the fastest proxy subclasses in an easy, java-friendly way.
Proxetta adds proxy methods arround the target method. Proxy method is defined in {@link ProxyTarget#invoke()}
Proxy methods uses special 'macro'-alike static methods from ProxyTarget class.
These static method invocations will be replaced with appropriate target invocation, once when proxy
subclass is created. This unique feature makes generated code not to use reflections.
Replacements and modification rules
During creation of proxy method in proxy subclass, several replacement and modification rules are applied
in order to produce valid subclass that will use its target superclass instead of ProxyTarget macros.
Here is the list of all such rules.
Add all constructors [A1]
Proxy subclass must contain all constructors as a target subclass. New constructors simply
delegates invocation to the super class. All contructor 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).
Replace ProxyTarget.invoke [R1]
Call to ProxyTarget.invoke() 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, no matter how many arguments there are.
invokestatic package/ProxyTarget.invoke
aload_0
aload_1
iload_2
...
invokespecial package/Target.method
Fix return for ProxyTarget.invoke [F2]
When [R1] is applied, target methods with return value simply put the value onto stack. Since it is not used
until the end of method, it may remain there. The only thing is to return this value at the end, if target method
doesn't return void . Note that this is an optimized version of what java compiler would do: it will
store internally the return value to new local variable and carry it until the return, where it will be loaded
before returning.
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. 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.invokeAndGetResult [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, no matter how many arguments there are.
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.invokeAndGetResult [F3]
When ProxyTarget.invokeAndGetResult() 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 ASTORE for ProxyTarget.invokeAndGetResult [F4]
When ProxyTarget.invokeAndGetResult() 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.
Replace ProxyTarget.setReturnVal [R8]
Sets resulting value. Must be called after invokeAndGetResult .
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.
|