Annotation Type CanonicalOverwrite


  • @Documented
    @Retention(RUNTIME)
    @Target(METHOD)
    public @interface CanonicalOverwrite

    Abstract

    The CanonicalOverwrite is a micromixin-specific derivation of the traditional Overwrite annotation. This annotation has the same behavioural semantics as Overwrite in the absence of a remapper or refmaps. However, when a remapper is used, the handler method keeps its "canonical" name, and a new helper method is spawned to keep the semantics of Overwrite. Hence, this method is in a sense of hybrid of Unique (which makes a method keep it's name for public methods), and Overwrite.

    It may not be applied on non-public or static methods, see the illegal usecases for further details.

    Semantic approximation

    The semantic approximation is an approximation of how the behaviour of this annotation can be replicated using other annotations, but may not mirror the inner workings of the annotation behind the scenes.

    In it's purest form, the behaviour of this annotation can be replicated using a default/implicit overwrite + explicit Overwrite pair. Henceforth you get the following two methods:

    
     // Note: No annotation here!
     public void methodName() {
         // Method logic goes here.
         // This is the "canonical" part of the handler.
     }
    
     @Overwrite
     public void methodName() {
         ((MyInterface) this).methodName();
     }
     

    The keen might realize that while this cannot be emulated with a single mixin class, this behaviour can be achieved using two or more mixin classes. This effect might make this annotation redundant, and it may be the only way to replicate the behaviour of CanonicalOverwrite if the annotation is not guaranteed to be supported under a given environment (please check out the documentation of your environment and dependencies you rely on for hints about this support). That being said, this requires the Overwrite to be applied before the implicit overwrite. However, as the implicit overwrite does not have strongly defined behaviour, it is possible for this emulation to not work under certain environments. Further, the chance of mistakes is high and the intended behaviour might be obfuscated by the boilerplate, ultimately making maintenance more costly. Henceforth, CanonicalOverwrite should be used for this exact usecase.

    Technical details

    This paragraph is mostly specific to the implementation within micromixin-transformer, other implementations may derive slightly from the implementation details. However, the behaviour must be kept the same. This is especially crucial to overrides in conjunction with subclassing.

    Within the remapper process, the handler method may not be renamed. Instead, only method() and target() are renamed. If neither method() nor target() are defined, then target() will be set to correspond to the method's current name and descriptor, and the new target() value will be remapped from then on. The handler method's name stays constant as established earlier.

    Important limitations & words of warning

    Usages of this annotation that are either discouraged or environments in which this annotation might show unintended behaviour (though this behaviour not being intended per-se but rather being a byproduct of other constraints).

    This annotation is unlikely to behave as intended outside of the stianloader toolchain. Especially, the remapping semantics described by this annotation are unlikely to be supported by tiny-remapper (although tiny-remapper could easily be extended to support this annotation) and are completely ignored when making use of the annotation processor and/or refmaps. In general, only micromixin-remapper can be reliably expected to process this annotation.

    This annotation has the potential of polluting the target class if it is used too frequently for little reason. Use Overwrite instead whenever applicable. Do note that like Overwrite, CanonicalOverwrite completely overwrites the target method. Thus it should not be used when an Overwrite is inappropriate, that is, CanonicalOverwrite ought not replace one or more Inject, ModifyArg, or similar. Further this annotation should not be used as an alternative to Unique.

    Illegal usecases

    Usages of this annotation that are invalid and thus will raise either a remap-time failure or a runtime failure. Implementors are encouraged to check against these uses.

    Methods annotated with CanonicalOverwrite MUST be public. All other visibility modifiers (such as protected, package-private or private) are considered illegal within the scope of this constraint. This constraint is at its core arbitrary, but is necessary to reduce the possibility of misuse of this annotation, as the ABI (application binary interface) guarantees provided are only relevant when this constraint is applied.

    A method annotated with CanonicalOverwrite MUST implement a definition defined by at least one interface implemented in the target class. As a logical consequence, it is invalid to apply this annotation on a static method. This constraint is at its core arbitrary, but is necessary to reduce the possibility of misuse of this annotation, as the ABI (application binary interface) guarantees provided are only relevant when this constraint is applied.

    The handler method (that is the method annotated with CanonicalOverwrite) and the target method (that is the method that should be overwritten) must have the same descriptor. It is not valid for them to differ in the descriptor, which means that the return value and the argument types must match. However, the generic signatures may differ due to generic erasure (but do note that mismatches may be dangerous nonetheless).

    • Optional Element Summary

      Optional Elements 
      Modifier and Type Optional Element Description
      java.lang.String method
      The target selector string defining the method that should be overwritten.
      Desc target
      The @Desc definition referring to the method that should be overwritten.
    • Element Detail

      • method

        java.lang.String method
        The target selector string defining the method that should be overwritten. That is, the selected method will delegate all it's calls to the handler method.

        This parameter is mutually exclusive with target(). Only one of the two may be defined. However, it is also valid for none of the elements to be set, in which case the overwritten method is assumed to have the same descriptor and name as the annotated handler method.

        Note: Like many other mixin annotations, the transformer can differ between explicitly setting the value to "" and not setting the value at all. This is because javac omits undefined element values and ignores the default value. The default value advertised here is only required due to how optional elements need to be defined in java source code but truth be told there is no such default value in practice.

        Returns:
        The target selector string defining the method to overwrite.
        Default:
        ""
      • target

        Desc target
        The @Desc definition referring to the method that should be overwritten. That is, the selected method will delegate all it's calls to the handler method.

        This parameter is mutually exclusive with method(). Only one of the two may be defined. However, it is also valid for none of the elements to be set, in which case the overwritten method is assumed to have the same descriptor and name as the annotated handler method.

        Like in all other annotations, the default descriptor of the Desc is implicitly set to void.class. If a non-void method is overwritten, the descriptor must be explicitly set as a logical consequence of this constant (i.e. blind) default. The descriptor of the overwritten method and the descriptor of the target method must be the same, as defined by the illegal usecases of CanonicalOverwrite.

        Note: Like many other mixin annotations, the transformer can differ between explicitly setting the value to @Desc("") and not setting the value at all. This is because javac omits undefined element values and ignores the default value. The default value advertised here is only required due to how optional elements need to be defined in java source code but truth be told there is no such default value in practice.

        Returns:
        The method to overwrite.
        Default:
        @org.spongepowered.asm.mixin.injection.Desc("")