-
-
Notifications
You must be signed in to change notification settings - Fork 5
CInject
The CInject
annotation can be used to inject code at a specific position in a method.
The original code flow can be cancelled and other values can be returned.
The method signature of the transformer method depends on the target method.
When injecting into a static method, the transformer method also needs to be static and vice versa.
The parameters of the transformer method can be the same as the target method, but can also be left out.
An optional InjectCallback
can be added as the last parameter to cancel the original code flow and return a value.
The return type always has to be void
.
//Injecting into a static method
@CInject(method = "method", ...)
public static void transform(final String arg1, final int arg2, final InjectionCallback ic)
//Injecting into a non-static method
@CInject(method = "method", ...)
public void transform(final int arg1, final InjectionCallback ic)
//Leaving out the parameters
//Example method: public void method(String arg1, int arg2) {
@CInject(method = "method", ...)
public void transform(final InjectionCallback ic)
//Leaving out the parameters and the callback
//Example method: public static void method(String arg1, int arg2) {
@CInject(method = "method", ...)
public static void transform()
When adding the parameters to the transformer method, the types of the parameters need to match the types of the target method.
Object
can be used as a wildcard for any non-primitive type. This can be useful when parameter types are not accessible.
Changing the parameter values is not possible. If you need to change the values, you can use the CLocalVariable
annotation.
The target
field of the CInject
annotation specifies the position in the method to inject into.
Check out the CTarget page for more information about the different targets.
Also, check out CSlice for more information about how slices work.
To cancel the original code flow, the cancellable
field of the CInject
annotation needs to be set to true
. A runtime exception will be thrown if the field is not set to true
.
If the target method returns a value, a value needs to be passed to the callback to return.
//Cancelling a void method
ic.setCancelled(true);
//Cancelling a method with a return value
//setReturnValue also calls setCancelled(true)
ic.setReturnValue("return value");
When using the RETURN
, TAIL
or THROW
target, the original return value can be accessed using getReturnValue()
or castReturnValue()
.
//Accessing the old return value
@CInject(method = "method", target = @CTarget("RETURN"), cancellable = true)
public void transform(final InjectionCallback ic) {
String returnValue = ic.castReturnValue();
//Do something with the return value
}
//Accessing the thrown exception
@CInject(method = "method", target = @CTarget("THROW"), cancellable = true)
public void transform(final InjectionCallback ic) {
Throwable exception = ic.castReturnValue();
//Do something with the exception
}
When using the THROW
target, be aware that the exception can not be set using setReturnValue()
.
Doing so will try to return the exception, which will result in a runtime exception.
//This will not work
ic.setReturnValue(new Exception());
//This will work
throw new Exception();
The method
field can be an array of strings to target multiple methods.
The transformer method needs to be compatible with all targeted methods.
An easy way to do this is to leave out the parameters.
@CInject(method = {"method1", "method2"}, ...)
public void transform(final InjectionCallback ic)
The target
field can be an array of CTarget
annotations to specify multiple targets.
@CInject(method = "method", target = {@CTarget("HEAD"), @CTarget("RETURN")}, ...)
public void transform(final InjectionCallback ic)
Original method:
public String method(final String arg) {
return "hello " + arg;
}
Transformer method:
@CInject(method = "method", target = @CTarget("HEAD"), cancellable = true)
public void transform(final String arg, final InjectionCallback ic) {
if (arg.equals("hello")) { //Check if the first argument is "hello"
ic.setReturnValue("world"); //Return "world" instead of the original return value
}
}
Injected code:
public String method(final String arg) {
InjectionCallback ic = new InjectionCallback(true /* cancellable */);
transform(arg, ic);
if (ic.isCancelled()) {
return (String) ic.getReturnValue();
}
return "hello " + arg;
}
Transformer method:
@CInject(method = "method", target = @CTarget("RETURN"), cancellable = true)
public void transform(final String arg, final InjectionCallback ic) {
String returnValue = ic.castReturnValue(); //Get the original return value
ic.setReturnValue(returnValue + " world"); //Return the original return value + " world"
}
Injected code:
public String method(final String arg) {
String returnValue = "hello " + arg;
InjectionCallback ic = new InjectionCallback(true /* cancellable */, returnValue /* original return value */);
transform(arg, ic);
if (ic.isCancelled()) {
return (String) ic.getReturnValue();
}
return returnValue;
}
Transformer method:
@CInject(method = "method", target = @CTarget("HEAD"))
public void transform() {
System.out.println("Hello world");
}
Injected code:
public String method(final String arg) {
transform();
return "hello " + arg;
}