Package com.llamalad7.mixinextras.injector.wrapoperation

## Annotation Type WrapOperation

    @Documented
    @Retention(RUNTIME)
    @Target(METHOD)
    public @interface WrapOperation

The `WrapOperation` annotation defines an injector which is capable of wrapping a single instruction and modify it.

### 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).

### WrapOperation versus `Redirect`

In a sense, `WrapOperation` behaves similar to `Redirect`, except that the original instruction callsite is preserved and can be used by other injectors, enabling chaining. Further, because the original call is preserved through an `Operation`, it can be reused in the mixin method without having to write the call yourself. This is especially useful when targeting different method calls that still share the same signature. Now, a single injector can target different calls while still being able to call the original call manually. This also means that `WrapOperation` injectors are not mutually exclusive to each other and thus are compatible, as long as the original operation is being called.

### Signature and visibility modifiers

The `WrapOperation` handler MUST declare the same return type (subtypes are not supported) as the return type of the targeted instruction (if the instruction does not return an operand, use `void`).

The `WrapOperation` handler's argument list must be prefixed by the input operands of the targeted instruction - including the receiver operand - in the order they are used in the targeted instruction. The argument list is followed by and argument of type `Operation`. The `Operation` argument should have the same generic type as the result operand of the targeted instruction. For a method call returning void, use `Void` - for a primitive such as `int`, use the boxed equivalent (here `Integer`). While the generic signature of the `Operation` argument is technically not enforced by the mixin transformer, failing to use the correct generic signature can cause runtime issues (specifically, `ClassCastException`). All arguments following the `Operation` argument are available for argument capture (or similar, for example for `Cancellable`).

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`.

### Compatible instructions

Micromixin-transformer's implementation of `WrapOperation` may transform any of the following instructions:

- InvokeInterface
- InvokeSpecial (except for superconstructor calls)
- InvokeStatic
- InvokeVirtual

However, stock MixinExtras can transform other instructions, too - for example (not exhaustive):

- AAStore
- AALoad
- AStore
- ALoad
- BAStore
- BALoad
- CAStore
- CALoad
- CheckCast
- DAStore
- DALoad
- DStore
- DLoad
- IAStore
- IALoad
- InstanceOf
- GetField
- GetStatic
- PutField
- PutStatic
- etc. etc.

These instructions will be supported by micromixin-transformer one by one on a as-needed basis. The fact that they are unsupported at this point does not mean that they will never be supported.

### Argument and local capture

Local capture is not supported as-is, use the MixinExtras Local sugar annotation instead to capture local variables.

Argument capture for `WrapOperation` behaves similar to argument capture in `ModifyConstant`, with parameters eligible for argument capture being after the argument of type `Operation`. 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 `WrapOperation` handler method.

When running micromixin-transformer with the micromixin or lenient vendor compatibility mode, it is permissible to intersperse ordinary argument capture with MixinExtras sugars (e.g. `Cancellable`). Otherwise (e.g. under sponge/fabric vendor compatibility mode or when using the spongeian mixin implementation), sugars must come after non-sugar argument capture.

### Generated pseudocode

The `WrapOperation` handler method:

>      @WrapOperation(at = { @At(value = "INVOKE", target = "setSignal(I)V") }, method = { "testWrapOp()V" }, require = 1, allow = 1)
>      private static void onSignalTestWrapOp(int arg0, Operation<Void> op) {
>          op.call(1);
>      }

The target method:

>      public static void testWrapOp() {
>          Signaller.setSignal(0);
>      }

The generated/transformed target method would look comparable to follows:

>      public static void testWrapOp() {
>          onSignalTestWrapOp(1, (argv) -> Signaller.setSignal((Integer) argv[0]));
>      }

Keep in mind that this is not exactly how it will look like in practice due to `INVOKEDYNAMIC` requiring the presence of a bridge method and also the fact that argument count is verified under the hood through within the bridge method. That being said, effectively the resulting code is the same.

The generated bytecode for the transformed target method should look comparable to follows:

>      iconst_0
>      invokedynamic call ()Lcom/llamalad7/mixinextras/injector/wrapoperation/Operation; LambdaMetafactory.metafactory { ([Ljava/lang/Object;)Ljava/lang/Object;, { invokestatic, targetClass.$mixinextras$bridge$setSignal$2, ([Ljava/lang/Object;)Ljava/lang/Void; }, ([Ljava/lang/Object;)Ljava/lang/Void; }
>      invokestatic targetClass.$handler$2$onSignalTestWrapOp (ILcom/llamalad7/mixinextras/injector/wrapoperation/Operation;)V

with the bytecode of the generated bridge method being effectively follows:

>      aload v0
>      iconst_1
>      ldc "[int]"
>      invokestatic com/llamalad7/mixinextras/injector/wrapoperation/WrapOperationRuntime.checkArgumentCount ([Ljava/lang/Object;ILjava/lang/String;)V
>      aload v0
>      iconst_0
>      aaload
>      checkcast java/lang/Integer
>      invokevirtual java/lang/Integer.intValue ()I
>      invokestatic Signaller.setSignal (I)V
>      aconst_null
>      checkcast java/lang/Void
>      areturn

### Chaining

Micromixin-transformer's architecture was not built with mixin collisions in mind, which is why `WrapOperation` cannot be chained at this point in time. However, it is not impossible to implement `WrapOperation` handler chaining within micromixin-transformer, but it'll only be done on a as-needed basis (as most things are done here).

Since:
  0.9.0

### Required Element Summary

Required Elements
`At[]` `at` The injection points where the injection should occur.
`java.lang.String[]` `method` The targeted method selectors.

### Optional Element Summary

Optional Elements
`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 `WrapOperation` the injection points MUST target one of the following instructions:

- `INVOKESTATIC`
- `INVOKESPECIAL` (but constructor calls are not supported)
- `INVOKEVIRTUAL`
- `INVOKEINTERFACE`

If that is not the case, the class will fail to transform.

Note that at this point in time, micromixin-transformer only supports `WrapOperation` on MethodInsnNodes, which means that micromixin-transformer supports modifying less operations than stock MixinExtras. In a similar vein, targeting `INVOKEDYNAMIC` instructions is not supported either.

When targeting an instruction, the instruction immediately before must be selected, i.e. using `INVOKE` injection point (see `At.value()`). A `INVOKE_ASSIGN` injection point is unsupported however (at least at this point), unless `At.Shift.BEFORE` is used.

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.

MixinExtras does not support target matching via `Desc`. It is likely that this feature will never be implemented by MixinExtras 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:
  {}