/* * This file is part of Mixin, licensed under the MIT License (MIT). * * Copyright (c) SpongePowered * 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 java.lang.annotation.Target; import org.objectweb.asm.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. * *

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.

*/ @Target({ /* No targets allowed */ }) @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 if (x > 0) to be compiled * as if (!(x <= 0)) * *

Note that all of these options assume that x is on the * left-hand side of the expression in question. For expressions where * zero is on the right-hand side you should choose the inverse.

*/ public enum Condition { /** * Match < operators and >= instructions: * * x < 0 */ LESS_THAN_ZERO(Opcodes.IFLT, Opcodes.IFGE), /** * Match <= operators and > instructions * * x <= 0 */ LESS_THAN_OR_EQUAL_TO_ZERO(Opcodes.IFLE, Opcodes.IFGT), /** * Match >= operators and < instructions, equivalent to * {@link #LESS_THAN_ZERO} * * x >= 0 */ GREATER_THAN_OR_EQUAL_TO_ZERO(Condition.LESS_THAN_ZERO), /** * Match > operators and <= instructions, equivalent to * {@link #LESS_THAN_OR_EQUAL_TO_ZERO} * * x > 0 */ 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 ACONST_NULL (null object) literals * * @return true to match null */ public boolean nullValue() default false; /** * Specify an integer constant to match, includes byte and short values. * *

Special note for referencing 0 (zero) which forms part of * a comparison expression: See the {@link #expandZeroConditions} option * below.

* * @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 ordinal 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 only 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; /** * This specifies the ID of the slice to use for this query. * *

For more details see the {@link Slice#id}

* * @return the slice identifier, or empty string to use the default slice */ public String slice() default ""; /** * Whilst most constants can be located in the compiled method with relative * ease, there exists a special case when a zero is used in a * conditional expression. For example: * *
if (x >= 0)
* *

This special case occurs because java includes explicit instructions * for this type of comparison, and thus the compiled code might look more * like this:

* *
if (x.isGreaterThanOrEqualToZero())
* *

Of course if we know that the constant we are searching for is part of * a comparison, then we can explicitly search for the * isGreaterThanOrEqualToZero and convert it back to the original * form in order to redirect it just like any other constant access.

* *

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 inverse * of their source-level counterparts (eg. compiling a do this if * greater than structure to a ignore this if less than or equal * structure); specifying a particular expression type implicitly * includes the inverse expression as well.

* *

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.

* *

Specifying this option has the following effects:

* * */ public Condition[] expandZeroConditions() default {}; /** * @return true to enable verbose debug logging for this injection point */ public boolean log() default false; }