WrapOperation - LlamaLad7/MixinExtras GitHub Wiki

Allows you to wrap many kinds of operations.

It accepts these injection points: INVOKE, FIELD, @Constant, NEW and MIXINEXTRAS:EXPRESSION.

Your handler method receives the targeted instruction's arguments and an Operation representing the operation being wrapped (optionally followed by the enclosing method's parameters). You should return the same type as the wrapped operation does.

Should be used in favour of Redirects when you are wrapping the original operation and not replacing it outright, as unlike Redirects, this chains when used by multiple people.

Handler Methods

Targeted operation Handler signature
Non-static method call private ReturnType yourHandlerMethod(OwnerType instance, <arguments of the original call>, Operation<ReturnType> original)
super. method call private ReturnType yourHandlerMethod(ThisClass instance, <arguments of the original call>, Operation<ReturnType> original)
Static method call private ReturnType yourHandlerMethod(<arguments of the original call>, Operation<ReturnType> original)
Non-static field get private FieldType yourHandlerMethod(OwnerType instance, Operation<FieldType> original)
Static field get private FieldType yourHandlerMethod(Operation<FieldType> original)
Non-static field write private void yourHandlerMethod(OwnerType instance, FieldType newValue, Operation<Void> original)
Static field write private void yourHandlerMethod(FieldType newValue, Operation<Void> original)
instanceof check private boolean yourHandlerMethod(Object obj, Operation<Boolean> original)
Object instantiation private ObjectType yourHandlerMethod(<arguments of the relevant constructor>, Operation<ObjectType> original)
Primitive comparison private boolean yourHandlerMethod(theType left, theType right, Operation<Boolean> original)
Reference comparison private boolean yourHandlerMethod(Object left, Object right, Operation<Boolean> original)
Array element get private ElementType yourHandlerMethod(ElementType[] array, int index, Operation<ElementType> original)
Array element set private void yourHandlerMethod(ElementType[] array, int index, ElementType value, Operation<Void> original)
Object cast private CastType yourHandlerMethod(Object object, Operation<CastType> original)

In all of these cases, you can optionally add the enclosing method's parameters to the end, should you require them for additional context.

When calling the original, you must pass everything before the original in your handler's parameters. You can optionally pass different values to change what the original uses.

Example

When targeting code such as the following:

int number = this.expensiveCalculation(flag);

you may wish to wrap the call such that it is bypassed if a setting is enabled.

This could be done like so:

@WrapOperation(
	method = "targetMethod", 
	at = @At(value = "INVOKE", target = "Lsome/package/TargetClass;expensiveCalculation(Z)I")
)
private int bypassExpensiveCalculationIfNecessary(TargetClass instance, boolean flag, Operation<Integer> original) {
	if (YourMod.bypassExpensiveCalculation) {
                return 10;
        } else {
                return original.call(instance, flag);
        }
}

expensiveCalculation would then only be called if you called it yourself via the original.call(...) invocation.

Multiple mods can do this at the same time, and the wrapping will chain. The first to be applied receives an Operation representing the vanilla call, if another is applied it receives an Operation representing the first one's wrapping, etc.

Code Diff

- int number = this.expensiveCalculation(flag);
+ int number = this.bypassExpensiveCalculationIfNecessary(this, flag, args -> {
+         return ((TargetClass) args[0]).expensiveCalculation((Boolean) args[1]);
+ });
⚠️ **GitHub.com Fallback** ⚠️