Annotation Type ModifyReturnValue
-
@Documented @Retention(RUNTIME) @Target(METHOD) public @interface ModifyReturnValueTheModifyReturnValueannotation allows to apply a function on the return value of a method. More specifically, the mixin implementation will call the modify return value handler with the original return value and return the actual return value.Availability
This annotation is not available as-is on the spongeian mixin implementation and is part of Llamalad7's MixinExtras. Please refer to your launcher documentation for more details. Recent release of SLL's launcher-sponge variant should come with MixinExtras already bundled by default. However, as MixinExtras is widely used in the minecraft scene it is very likely that your loader supports it, provided you are either using SLL or a loader written for minecraft (such as neoforge or fabric).
ModifyReturnValue versus Inject
Usage of
ModifyReturnValueshould be preferred over other annotations such asInjectfor it's supposed capability of being easily and safely chained. In truth we do not see if that is really the case under the micromixin-transformer implementation. Usage of
can and should be replaced with@Inject(...) private void handleCallback(CallbackInfoReturnable<T> cir) { cir.setReturnValue(apply(cir.getReturnValue())); }@ModifyReturnValue(...) private T handleCallback(T original) { return apply(original); }Aside from the aforementioned improvements this annotations does not (as seen above) allocate a
CallbackInfoReturnableand is as such more performant. It should as such be used in hot code whenever appropriate. While the assumption that the CallbackInfoReturnable allocation will get inlined or otherwise optimized in the JVM may be true, some JVMs might not follow this and will show a notable performance gain when using this annotation overInject.Signature and visibility modifiers
The
ModifyReturnValuehandler (also known as the "return value modifier") MUST declare the same return type (subtypes are not supported) as the return type of the targeted method. If the targeted method isstatic, the handler MUST bestaticandprivate. For non-statictargets the access modifiers are not of relevance, except for constructors where the handler must bestaticwhen not injecting immediately before the final return viaTAIL.The handler should furthermore also have the original return type (supertypes are not supported) as it's argument.
The value that is modified must be the first argument of the return value modifier handler method. All other arguments are there for argument capture.
Argument and local capture
At this point in time, local capture is not supported.
Argument capture behaves the same way as
ModifyConstant. The arguments that can be captured are the arguments with whom the target method was called with (do note that it is permissible for the values to be reassigned - so while it guarantees the argument was captured, the value of the argument may have been altered) - or more plainly, argument capture means that the arguments of the target method are captured and passed to the return value modifier handler method.Captured arguments must be defined in the same order as the target method. It is not permissible to "skip" arguments, but it is permissible to not capture trailing arguments - leading arguments need to be captured however under the premise of the "no skipping" requirement.
Reassigning captured arguments in the return value modifier method has no effect on the target method. As such, the behaviour is quite similar to the argument and local variable capture behaviour of
Inject.
-
-
Optional Element Summary
Optional Elements Modifier and Type Optional Element Description intallowThe maximum amount of injection points that should be allowed.intexpectThe expected amount of injection points.intrequireThe minimum amount of amount of injection points.Slice[]sliceThe available slices used for bisecting the available injection points declared byat().
-
-
-
Element Detail
-
at
At[] at
The injection points where the injection should occur. If none of the injection points apply no exception is thrown by default (this default can be changed throughrequire()), however transformation does not occur (Micromixin still copies the handler into the target class anyways).For
ModifyReturnValuethe injection points MUST target return instructions. If that is not the case, the class will fail to transform.- Returns:
- The injection points.
-
-
-
method
java.lang.String[] method
The targeted method selectors. The amounts of methods that may match and are selected is not bound to any hard value and as such it should be limited by setting attributes such asrequire()orexpect()as otherwise the injector might accidentally not match anything with no way of knowing what exactly went wrong.The following are all valid formats of explicit target selectors:
targetMethodtargetMethod(Lcom/example/Argument;)V(Lcom/example/Argument;)VtargetMethod(I)Lcom/example/ReturnValue;targetMethod()ZLcom/example/Target;targetMethod(Lcom/example/Argument;)VLcom/example/Target;(Lcom/example/Argument;)VLcom/example/Target;targetMethod(Lcom/example/Argument;) VLcom/example/Target;target Method(Lcom/example/Argument;)VLcom/example/Target;targetMethod(Lcom/exam ple/Argument;)V
The parts of the explicit target selector (owner, name, descriptor) must always have the same order, but the individual parts must not necessarily be present.
While permissible, it is strongly discouraged to make use of whitespace in explicit target selectors. When they are used, the spongeian mixin implementation (and also micromixin) will discard all whitespace characters (tabs included). This is documented behaviour (in both micromixin and sponge's mixin) and is unlikely to change in the future. This discouragement exists as this feature may cause target selectors to be illegible.
It is generally recommended to not be lazy when it comes to explicit selectors, the more information is provided the better. Information that is not supplied is comparable to a wildcard - the first matching method will be targeted, even if nonsense. It is especially not recommended to discard the method name, even if that is theoretically valid.
The spongeian implementation also supports schemes other than the explicit selectors. However the Micromixin implementation only supports explicit selectors as documented above. Where as the spongeian implementation supports quantifiers in explicit selectors, Micromixin does not support them (yet). As such, quantifiers are not included in the documentation.
MixinExtra does not support target matching via
Desc. It is likely that this feature will never be implemented by MixinExtra as the developer finds that feature superfluous. While the micromixin-transformer does implement that feature regardless, making use of the feature would require a custom annotation library for as long as Mixin implementations based on the spongeian mixin implementation remain viable in the galimulator modding space and the strict interoperability between the implementations is needed.- Returns:
- The target selectors that define the target method of the handler.
-
-
-
allow
int allow
The maximum amount of injection points that should be allowed. If the value of this element is below 1 or if the value is below theminimum amountof allowable injection points then the limit is not being enforced. However,expect()has no influence onallow().Furthermore this limit is only valid per target class. That is, if multiple target classes are defined as per
Mixin.value()orMixin.targets()then this limit is only applicable for all the injection points in the targeted class. This limitation is caused due to the fact that the targeted classes are not known until they are loaded in by the classloader, at which point all the injection logic occurs.This limit is shared across all methods (as defined by
method()) targeted by the handler within a class.- Returns:
- The maximum amount targeted of injection points within the target class.
- Default:
- -1
-
-
-
expect
int expect
The expected amount of injection points. This behaves similar torequire(), however whilerequire()will cause a class file transformation failure,expect()is a weaker form of it. Under the spongeian implementation, this attribute behaves likerequire()if and only if the appropriate debug flags are activated. The micromixin transformer will meanwhile "just" unconditionally write a warning to the logger.This attribute should be used to identify potentially outdated injectors.
- Returns:
- The expected amount of injection points
- Default:
- -1
-
-
-
require
int require
The minimum amount of amount of injection points. If less injection points are found (as perat()). an exception is thrown during transformation. The default amount of required injection points can be set by mixin configuration file, but by default that is no minimum amount of required injection points.- Returns:
- The minimum amount of injection points
- Default:
- -1
-
-