Package org.spongepowered.asm.mixin.injection

## Annotation Type ModifyVariable

-     @Documented
      @Retention(RUNTIME)
      @Target(METHOD)
      public @interface ModifyVariable

  The `ModifyVariable` annotation allows to apply a function on a single local variable at an arbitrary point during execution. More specifically, the mixin implementation will call the variable modifier handler with the original value of the local variable. The return value of the handler is then used to set the value of the local variable. The point in which the function is applied is dictated through `at()`.

  As method arguments are also local variables, `ModifyVariable` can be used to modify incoming (that is those of the injected method) arguments. In order to modify outgoing arguments (that is arguments that are passed to methods invoked by the injected method), `ModifyArg` should be used.

  ### Signature and visibility modifiers

  The `ModifyVariable` handler (also known as the "variable modifier") MUST declare the same return type (subtypes are not supported) as the type of the variable it modifies (supertypes are not supported). 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`.

  Locals capture is not supported when using `ModifyVariable`. However, arguments may be captured by appending them to the list of arguments of the handler method.

  ### Effects on the target method

  This injector annotation, once applied will produce code similar to follows:

  > `ACONST null ASTORE 0 ALOAD 0 INVOKESTATIC TargetClass.modifyVariableHandler(Ljava/lang/Object;)V ASTORE 0`

  which corresponds to following source code:

  > `Object variable = null; variable = modifyVariableHandler(variable);`

  The variable modifier method would look a bit like follows:

  > `@ModifyVariable() private static Object modifyVariableHandler(Object original) { return original; }`

  ### Other notes and potential pitfalls

  A single `ModifyVariable` can modify only a single variable, but can modify it multiple times (as per it's `at()`).

  `ModifyVariable` supports an injection point selector that is strictly unique to `ModifyVariable`: `LOAD` and `STORE`. These injection point selectors target the `xLOAD` or `xSTORE` instructions of the selected local variable (mind you that `ModifyVariable` can only select a single variable at a single time, so it is not possible to select ALL `xLOAD` or `xSTORE` instructions using `LOAD` and `STORE`). These injection point selectors can be used by setting the appropriate `At.value()` to either `LOAD` or `STORE`.

  Unlike any other injection point selectors, `STORE` selects the instruction after the `xSTORE` instruction. Please be aware of this when using an `At.value()` of STORE. This behaviour exists as otherwise modifications to the variable will be immediately overwritten, even in cases of `variable = variable * 4;`. Note that `LOAD` behaves like most other injection point selectors and targets the instruction before the `xLOAD` instruction(-s).

- - ### Required Element Summary

    Required Elements
    Modifier and Type | Required Element | Description
    --- | --- | ---
    `At` | `at` | The injection point where the injection should occur.

  - ### 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.
    `java.lang.String[]` | `method` | The targeted method selectors.
    `java.lang.String[]` | `name` | The names of the local variable that is to be modified by the `ModifyVariable`-annotated variable modifier.
    `int` | `require` | The minimum amount of injection points.
    `Slice` | `slice` | The slice to make use for the injection points defined by `at()`.
    `Desc[]` | `target` | The targeted methods.

- - ### Element Detail

    - #### at

          At at

      The injection point 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 though).

      The injection points of the `ModifyVariable` can target any arbitrary instruction, however attempting to target an instruction that lies outside the lifetime of the local variable may have adverse effects and may cause the resulting class file to fail to validate or even cause transformation failures.

      `ModifyVariable` supports an injection point selector that is strictly unique to `ModifyVariable`: `LOAD` and `STORE`. These injection point selectors target the `xLOAD` or `xSTORE` instructions of the selected local variable (mind you that `ModifyVariable` can only select a single variable at a single time, so it is not possible to select ALL `xLOAD` or `xSTORE` instructions using `LOAD` and `STORE`). These injection point selectors can be used by setting the appropriate `At.value()` to either `LOAD` or `STORE`.

      Unlike any other injection point selectors, `STORE` selects the instruction after the `xSTORE` instruction. Please be aware of this when using an `At.value()` of STORE. This behaviour exists as otherwise modifications to the variable will be immediately overwritten, even in cases of `variable = variable * 4;`. Note that `LOAD` behaves like most other injection point selectors and targets the instruction before the `xLOAD` instruction(-s).

      Returns:
        The injection point targeted by this `ModifyVariable` 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()` or `target()`) 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

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

      It is rather advisable to use `target()` over `method()`, especially for beginners, since latter provides behaviour that can more easily be anticipated.

      Returns:
        The target selectors that define the target method of the handler.
      Default:
        {}

  - - #### name

          java.lang.String[] name

      The names of the local variable that is to be modified by the `ModifyVariable`-annotated variable modifier.

      This property only takes effect if no ordinal or index is defined (ordinals and indices are not supported in micromixin-transformer as of 2024-05-14, nor are they present in micromixin-annotations). Further, this property requires the presence of the LVT (local variable table) and is as such unlikely to be present in obfuscated environments where either the local variable table is outright missing or contains bogus values (in minecraft, the local variable table contains bogus names, where as in galimulator it is stripped). Using decompiler-provided names will not work in these circumstances.

      When providing multiple names, only one will be matched. Neither micromixin-transformer nor the spongeian mixin implementation make any guarantees which name will be picked if multiple names were to exist.

      Micromixin-transformer may not properly handle cases where local variables are defined multiple times under the same name (this is commonly the case for the counter variable of for loops), please exercise caution in this circumstance.

      Both micromixin-transformer and the spongeian implementation support implicit local variable selection, meaning that this property is solely a filter to narrow down the amounts of instructions that is spit out by the `injection point selector`. The type of the local variable and that of the handler method is of course another filter.

      Returns:
        The possible names of the selected variable.
      Default:
        {}

  - - #### require

          int require

      The minimum 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 slice to make use for the injection points defined by `at()`.

      The id of the slice is ignored as per the spongeian documentation, but this behaviour may not be reflected by micromixin-transformer. It is as such not recommended to depend on this behaviour. See `At.slice()` and `Slice.id()` for further information.

      Returns:
        The slice used to filter the possible instructions matched by injection points.
      Default:
        @org.spongepowered.asm.mixin.injection.Slice

  - - #### target

          Desc[] target

      The targeted methods. Only one method is picked from the list of provided methods. As such the list should generally only be used to mark method aliases among others.
      Returns:
        The target selectors that define the target method of the handler.
      Default:
        {}