/*
* This file is part of Mixin, licensed under the MIT License (MIT).
*
* Copyright (c) SpongePowered <https://www.spongepowered.org>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.spongepowered.asm.mixin;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* This annotation allows fine-tuning of the overwrite policy for a
* soft-implemented interface member method.
*
* <p>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 <b>if it does not
* already exist</b> in the target, for example when running in a development
* environment.</p>
*
* <p>This annotation introduces two new behaviours for dealing with target
* methods which implement a soft interface intrinsically.</p>
*
* <p>Consider the following code:</p>
*
* <blockquote><pre>@Shadow public int getSomething();
*
* public int soft$getSomething() {
* return this.getSomething();
* }</pre></blockquote>
*
* <p>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.</p>
*
* <p>The only way to address this issue is to replace the shadow with a copy of
* the original accessor, like so:
*
* <blockquote><pre>@Shadow private int something;
*
* public int soft$getSomething() {
* return this.something;
* }</pre></blockquote>
*
* <p>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.</p>
*
* <p><code>Intrinsic</code> 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 {@link Intrinsic @Intrinsic}:</p>
*
* <blockquote><pre>@Shadow public int getSomething();
*
* @Intrinsic // don't merge the method if the target already exists
* public int soft$getSomething() {
* return this.getSomething();
* }</pre></blockquote>
*
* <p>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:</p>
*
* <blockquote><pre>@Shadow public int getSomething();
*
* public int soft$getSomething() {
* return this.someCondition ? this.someOtherValue : this.getSomething();
* }</pre></blockquote>
*
* <p>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 {@link Intrinsic} behaviour, and set the {@link #displace} argument to
* <code>true</code>. Setting <code>displace</code> instructs the mixin
* transformer to <em>proxy</em> the method call <em>if</em> 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.</p>
*
* <p>When <code>displace</code> is enabled, if the target method exists then
* instead of being overwritten it is instead <em>renamed</em> with a temporary
* name and all references to the original method <em>within the Intrinsic
* method</em> 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 <em>within</em> the Intrinsic
* accessor will call the overwritten method.</p>
*
* <blockquote><pre>@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();
* }</pre></blockquote>
*
*/
@Target({ ElementType.METHOD })
@Retention(RetentionPolicy.CLASS)
public @interface Intrinsic {
/**
* 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 <b>not</b> overwrite the target method
* if it exists, contrary to the normal behaviour for mixins
*
* @return true if this intrinsic method should displace matching targets
*/
public boolean displace() default false;
}