Annotation Type ModifyReturnValue


  • @Documented
    @Retention(RUNTIME)
    @Target(METHOD)
    public @interface ModifyReturnValue
    The ModifyReturnValue annotation 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 ModifyReturnValue should be preferred over other annotations such as Inject for 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

    
     @Inject(...)
     private void handleCallback(CallbackInfoReturnable<T> cir) {
          cir.setReturnValue(apply(cir.getReturnValue()));
     }
     
    can and should be replaced with
    
     @ModifyReturnValue(...)
     private T handleCallback(T original) {
          return apply(original);
     }
     

    Aside from the aforementioned improvements this annotations does not (as seen above) allocate a CallbackInfoReturnable and 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 over Inject.

    Signature and visibility modifiers

    The ModifyReturnValue handler (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 is static, the handler MUST be static and private. For non-static targets the access modifiers are not of relevance, except for constructors where the handler must be static when not injecting immediately before the final return via TAIL.

    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.

    • Required Element Summary

      Required Elements 
      Modifier and Type Required Element Description
      At[] at
      The injection points where the injection should occur.
      java.lang.String[] method
      The targeted method selectors.
    • Optional Element Summary

      Optional Elements 
      Modifier and Type Optional Element Description
      int allow
      The maximum amount of injection points that should be allowed.
      int expect
      The expected amount of injection points.
      int require
      The minimum amount of amount of injection points.
      Slice[] slice
      The available slices used for bisecting the available injection points declared by at().
    • 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 through require()), however transformation does not occur (Micromixin still copies the handler into the target class anyways).

        For ModifyReturnValue the 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 as require() or expect() 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:

        • targetMethod
        • targetMethod(Lcom/example/Argument;)V
        • (Lcom/example/Argument;)V
        • targetMethod(I)Lcom/example/ReturnValue;
        • targetMethod()Z
        • Lcom/example/Target;targetMethod(Lcom/example/Argument;)V
        • Lcom/example/Target;(Lcom/example/Argument;)V
        • Lcom/example/Target;targetMethod(Lcom/example/Argument;) V
        • Lcom/example/Target;target Method(Lcom/example/Argument;)V
        • Lcom/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 the minimum amount of allowable injection points then the limit is not being enforced. However, expect() has no influence on allow().

        Furthermore this limit is only valid per target class. That is, if multiple target classes are defined as per Mixin.value() or Mixin.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 to require(), however while require() will cause a class file transformation failure, expect() is a weaker form of it. Under the spongeian implementation, this attribute behaves like require() 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 per at()). 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
      • slice

        Slice[] slice
        The available slices used for bisecting the available injection points declared by at().
        Returns:
        An array of declared slices.
        Default:
        {}