/*
* 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.injection;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import org.spongepowered.asm.lib.Opcodes;
/**
* Annotation for specifying the injection point for an {@link ModifyConstant}
* injector. Leaving all values unset causes the injection point to match all
* constants with the same type as the {@link ModifyConstant} handler's return
* type.
*
* <p>To match a specific constant, specify the appropriate value for the
* appropriate argument. Specifying values of different types will cause an
* error to be raised by the injector.</p>
*/
@Retention(RetentionPolicy.RUNTIME)
public @interface Constant {
/**
* Available options for the {@link Constant#expandZeroConditions} setting.
* Each option matches the inverse instructions as well because in the
* compiled code it is not unusual for <tt>if (x > 0)</tt> to be compiled
* as <tt>if (!(x <= 0))</tt>
*
* <p>Note that all of these options assume that <tt>x</tt> is on the <b>
* left-hand side</b> of the expression in question. For expressions where
* zero is on the <b>right-hand side</b> you should choose the inverse.</p>
*/
public enum Condition {
/**
* Match < operators and >= instructions:
*
* <code>x < 0</code>
*/
LESS_THAN_ZERO(Opcodes.IFLT, Opcodes.IFGE),
/**
* Match <= operators and > instructions
*
* <code>x <= 0</code>
*/
LESS_THAN_OR_EQUAL_TO_ZERO(Opcodes.IFLE, Opcodes.IFGT),
/**
* Match >= operators and < instructions, equivalent to
* {@link #LESS_THAN_ZERO}
*
* <code>x >= 0</code>
*/
GREATER_THAN_OR_EQUAL_TO_ZERO(Condition.LESS_THAN_ZERO),
/**
* Match > operators and <= instructions, equivalent to
* {@link #LESS_THAN_OR_EQUAL_TO_ZERO}
*
* <code>x > 0</code>
*/
GREATER_THAN_ZERO(Condition.LESS_THAN_OR_EQUAL_TO_ZERO);
private final int[] opcodes;
private final Condition equivalence;
private Condition(int... opcodes) {
this(null, opcodes);
}
private Condition(Condition equivalence) {
this(equivalence, equivalence.opcodes);
}
private Condition(Condition equivalence, int... opcodes) {
this.equivalence = equivalence != null ? equivalence : this;
this.opcodes = opcodes;
}
/**
* Get the condition which is equivalent to this condition
*/
public Condition getEquivalentCondition() {
return this.equivalence;
}
/**
* Get the opcodes for this condition
*/
public int[] getOpcodes() {
return this.opcodes;
}
}
/**
* Causes this injector to match <tt>ACONST_NULL</tt> (null object) literals
*
* @return true to match <tt>null</tt>
*/
public boolean nullValue() default false;
/**
* Specify an integer constant to match, includes byte and short values.
*
* <p><b>Special note for referencing <tt>0</tt> (zero) which forms part of
* a comparison expression:</b> See the {@link #expandZeroConditions} option
* below.</p>
*
* @return integer value to match
*/
public int intValue() default 0;
/**
* Specify a float constant to match
*
* @return float value to match
*/
public float floatValue() default 0.0F;
/**
* Specify a long constant to match
*
* @return long value to match
*/
public long longValue() default 0L;
/**
* Specify a double constant to match
*
* @return double value to match
*/
public double doubleValue() default 0.0;
/**
* Specify a String constant to match
*
* @return string value to match
*/
public String stringValue() default "";
/**
* Specify a type literal to match
*
* @return type literal to match
*/
public Class<?> classValue() default Object.class;
/**
* Ordinal offset. Many InjectionPoints will return every opcode matching
* their criteria, specifying <em>ordinal</em> allows a particular opcode to
* be identified from the returned list. The default value of -1 does not
* alter the behaviour and returns all matching opcodes. Specifying a value
* of 0 or higher returns <em>only</em> the requested opcode (if one exists:
* for example specifying an ordinal of 4 when only 2 opcodes are matched by
* the InjectionPoint is not going to work particularly well!)
*
* @return ordinal value for supported InjectionPoint types
*/
public int ordinal() default -1;
/**
* Whilst most constants can be located in the compiled method with relative
* ease, there exists a special case when a <tt>zero</tt> is used in a
* conditional expression. For example:
*
* <blockquote><code>if (x >= 0)</code></blockquote>
*
* <p>This special case occurs because java includes explicit instructions
* for this type of comparison, and thus the compiled code might look more
* like this:</p>
*
* <blockquote><code>if (x.isGreaterThanOrEqualToZero())</code></blockquote>
*
* <p>Of course if we know that the constant we are searching for is part of
* a comparison, then we can explicitly search for the
* <tt>isGreaterThanOrEqualToZero</tt> and convert it back to the original
* form in order to redirect it just like any other constant access.</p>
*
* <p>To enable this behaviour, you may specify one or more values for this
* argument based on the type of expression you wish to expand. Since the
* Java compiler is wont to compile certain expressions as the <i>inverse
* </i> of their source-level counterparts (eg. compiling a <em>do this if
* greater than</em> structure to a <em>ignore this if less than or equal
* </em> structure); specifying a particular expression type implicitly
* includes the inverse expression as well.</p>
*
* <p>It is worth noting that the effect on ordinals may be hard to predict,
* and thus care should be taken to ensure that the selected injection
* points match the expected locations.</p>
*
* <p>Specifying this option has the following effects:</p>
*
* <ul>
* <li>Matching conditional opcodes in the target method are identified
* for injection candidacy.</li>
* <li>An <tt>intValue</tt> of <tt>0</tt> is implied and does not need to
* be explicitly defined.</li>
* <li>However, explicitly specifying an <tt>intValue</tt> of <tt>0</tt>
* will cause this selector to also match explicit <tt>0</tt> constants
* in the method body as well.</li>
* </ul>
*/
public Condition[] expandZeroConditions() default {};
/**
* @return true to enable verbose debug logging for this injection point
*/
public boolean log() default false;
}