KoresProxy is a Proxy generator written on top of Kores.
public class MyClass {
public int getNumber() {
return 10;
}
}
MyClass instance = KoresProxy.newProxyInstance(this.getClass().getClassLoader(), MyClass.class, (instance0, method, args, proxyData) -> {
if (method.getName().equals("getNumber"))
return 5;
return null;
});
Assert.assertEquals("getNumber", 5, instance.getNumber());
public class MyClass {
public int getNumber() {
return 10;
}
}
MyClass origin = new MyClass();
MyClass instance = KoresProxy.newProxyInstance(this.getClass().getClassLoader(), MyClass.class, (instance0, method, args, proxyData) -> {
if (method.getName().equals("getNumber"))
return 5;
return method.resolveOrFail(origin.getClass()).bindTo(origin).invokeWithArguments(args);
});
Assert.assertEquals("getNumber", 5, instance.getNumber());
Assert.assertEquals("hash", origin.hashCode(), instance.hashCode());
public class ClassWithConstructor {
private final String name;
public ClassWithConstructor(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
}
ClassWithConstructor cwcOrigin2 = new ClassWithConstructor("Origin 2");
ClassWithConstructor cwc = KoresProxy.newProxyInstance(this.getClass().getClassLoader(), ClassWithConstructor.class, (instance0, method, args, proxyData) -> {
return method.resolveOrFail(cwcOrigin2.getClass()).bindTo(cwcOrigin2).invokeWithArguments(args);
}, new Class[] { String.class }, new Object[]{ cwcOrigin2.getName() });
Assert.assertEquals("getName", "Origin 2", cwc.getName());
- KoresProxy only handles public, protected and package-private (for package private, see below) methods.
- KoresProxy only handles non-final methods.
- KoresProxy only generate proxies to classes that have accessible constructors.
KoresProxy support Super-classes and Interfaces inheritance, Java Proxies only supports Interfaces inheritance.
Since 2.1, KoresProxy uses MethodInfo
to provide method information and delegation instead of Java methods.
In Java 9 (or superior) KoresProxy works in a different way, instead of trying to inject classes in the ProxyData.classLoader
using private inaccessible methods, it only injects when the defineClass
method is public and if the method is not public, it creates a new class loader (CodeClassLoader
) and loads the class with it. If the ProxyData.classLoader
is a CodeClassLoader
, it will use the instance instead of creating a new one. Also, KoresProxy does not override package-private methods in Java 9 nor defines the class in the same package as the target super class. You can disable this behavior using the option described below.
Specify them using -D
or defining using the System.setProperty(String, String)
.
-
koresproxy.saveproxies
- Description: Save proxies generated classes and disassembled code in
gen/
. - Values:
true|false
- Default:
false
- Description: Save proxies generated classes and disassembled code in
-
koresproxy.ignore_module_rules
- Description: Ignore rules that applies to Java 9+, see the Java 9+ section above.
- Values:
true|false
- Default:
false
java.lang.ClassFormatError: Invalid start_pc * in LocalVariableTable in class file *
This happens when a Custom
adds a return statement out-side a flow or inside a flow that is always reached without invoking env.setInvokeHandler(false);
, example:
public class MyCustomGen implements CustomHandlerGenerator {
@Override
public CodeSource gen(Method target, MethodDeclaration methodDeclaration, GenEnv env) {
return CodeSource.fromPart(Factories.returnValue(target.getReturnType(),
InvocationFactory.invokeSpecial(
target.getDeclaringClass(), Access.SUPER, target.getName(), methodDeclaration.getTypeSpec(),
methodDeclaration.getParameters().stream()
.map(ConversionsKt::toVariableAccess)
.collect(Collectors.toList())
)
));
}
}
This code will cause class proxy to fail to load with class format error, to solve that, you should add:
env.setInvokeHandler(false);
env.setMayProceed(false);
Before the return
.
This happens because without env.setInvokeHandler(false)
KoresProxy will append invocation to InvocationHandler
after the code generated by your CustomHandlerGenerator, resulting in dead code. CodeAPI-BytecodeWriter optimizers will remove this dead code, but optimizers does not update LocalVariableTable
, meaning that local variable entries will point to a label that does not exists anymore in the bytecode, resulting in verifier throwing exception.
The env.setMayProceed(false);
will only prevent others Custom
to be run, this is necessary to ensure that no other Custom
will add code after your (resulting in dead code too).