Annotation Type Intrinsic


  • @Target(METHOD)
    @Retention(CLASS)
    public @interface Intrinsic
    This annotation allows fine-tuning of the overwrite policy for a soft-implemented interface member method.

    By default, all members within a mixin will overwrite (completely replace) matching methods in the target class without prejudice. However this can be a problem when soft-implementing an interface to deal with a method conflict because it may be desirable to only override the method if it does not already exist in the target, for example when running in a development environment.

    This annotation introduces two new behaviours for dealing with target methods which implement a soft interface intrinsically.

    Consider the following code:

    @Shadow public int getSomething();
    
    public int soft$getSomething() {
        return this.getSomething();
    }

    This type of accessor is common when an interface conflict occurs, but has the problem that the injected accessor will be effectively self-referential in development, because the prefix will be removed and the accessor will thus cause a stack overflow.

    The only way to address this issue is to replace the shadow with a copy of the original accessor, like so:

    @Shadow private int something;
     
    public int soft$getSomething() {
        return this.something;
    }

    But this has the drawback of forcing the mixin to re-implement the target method, and thus forces ongoing maintenance of the mixin to include updating the contents of the accessor.

    Intrinsic allows this problem to be circumvented in one of two ways. The first way essentially declares "don't merge this method if it already exists in the target". To use this behaviour, we simply tag our accessor with @Intrinsic:

    @Shadow public int getSomething();
    
    @Intrinsic // don't merge the method if the target already exists
    public int soft$getSomething() {
        return this.getSomething();
    }

    This is ideal if the accessor is simply providing a proxy through to an underlying obfuscated method, as in the example above. However, if the new method contains additional code, such as in this example:

    @Shadow public int getSomething();
    
    public int soft$getSomething() {
        return this.someCondition ? this.someOtherValue : this.getSomething();
    }

    Then we still have the original problem that the method becomes re-entrant without specifying the additional Overwrite. We can solve this with the second Intrinsic behaviour, and set the displace() argument to true. Setting displace instructs the mixin transformer to proxy the method call if the target already exists, but merge the method normally if it does not. This allows our new accessor to call the original method in all circumstances.

    When displace is enabled, if the target method exists then instead of being overwritten it is instead renamed with a temporary name and all references to the original method within the Intrinsic method are updated to call the renamed method. This means that all external code will still reference the Intrinsic accessor (just as it would have done with a regular overwrite) but code within the Intrinsic accessor will call the overwritten method.

    @Shadow public int getSomething();
    
    @Intrinsic(displace = true) // if the target already exists, displace it
    public int soft$getSomething() {
        return this.someCondition ? this.someOtherValue : this.getSomething();
    }
    • Optional Element Summary

      Optional Elements 
      Modifier and Type Optional Element Description
      boolean displace
      If set to true, this intrinsic method will replace any existing method in the target class by renaming it and updating internal references inside the method to reference the displaced method.
    • Element Detail

      • displace

        boolean displace
        If set to true, this intrinsic method will replace any existing method in the target class by renaming it and updating internal references inside the method to reference the displaced method. If false or omitted, instructs this intrinsic method to not overwrite the target method if it exists, contrary to the normal behaviour for mixins
        Returns:
        true if this intrinsic method should displace matching targets
        Default:
        false