/*
* 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.transformer;
import org.spongepowered.asm.lib.Opcodes;
import org.spongepowered.asm.lib.tree.FieldInsnNode;
import org.spongepowered.asm.lib.tree.MethodInsnNode;
import org.spongepowered.asm.mixin.transformer.throwables.MixinTransformerError;
import org.spongepowered.asm.util.Bytecode;
/**
* Reference to a field or method that also includes invocation instructions.
*
* <p>To instances are defined to be equal if they both refer to the same method
* and have the same invocation instructions.</p>
*/
public abstract class MemberRef {
/**
* A static reference to a method backed by an invoke instruction
*/
public static final class Method extends MemberRef {
/**
* Method invocation instruction
*/
public final MethodInsnNode insn;
/**
* ctor
*
* @param insn Method instruction of this member reference
*/
public Method(MethodInsnNode insn) {
this.insn = insn;
}
@Override
public boolean isField() {
return false;
}
@Override
public int getOpcode() {
return this.insn.getOpcode();
}
@Override
public String getOwner() {
return this.insn.owner;
}
@Override
public void setOwner(String owner) {
this.insn.owner = owner;
}
@Override
public String getName() {
return this.insn.name;
}
@Override
public void setName(String name) {
this.insn.name = name;
}
@Override
public String getDesc() {
return this.insn.desc;
}
@Override
public void setDesc(String desc) {
this.insn.desc = desc;
}
}
/**
* A static reference to a field backed by field get/put instruction
*/
public static final class Field extends MemberRef {
/**
* Field accessor instruction
*/
public final FieldInsnNode insn;
/**
* ctor
*
* @param insn Field instruction this member reference
*/
public Field(FieldInsnNode insn) {
this.insn = insn;
}
@Override
public boolean isField() {
return true;
}
@Override
public int getOpcode() {
return this.insn.getOpcode();
}
@Override
public String getOwner() {
return this.insn.owner;
}
@Override
public void setOwner(String owner) {
this.insn.owner = owner;
}
@Override
public String getName() {
return this.insn.name;
}
@Override
public void setName(String name) {
this.insn.name = name;
}
@Override
public String getDesc() {
return this.insn.desc;
}
@Override
public void setDesc(String desc) {
this.insn.desc = desc;
}
}
/**
* A reference to a field or method backed by a method handle
*/
public static final class Handle extends MemberRef {
private org.spongepowered.asm.lib.Handle handle;
/**
* Creates a member reference initially referring to the member referred
* to by the method handle and the invocation instruction of the method
* handle.
*
* @param handle Initial method handle.
*/
public Handle(org.spongepowered.asm.lib.Handle handle) {
this.handle = handle;
}
/**
* Gets a method handle for the member this is object is referring to.
*
* @return Method handle representing this object
*/
public org.spongepowered.asm.lib.Handle getMethodHandle() {
return this.handle;
}
@Override
public boolean isField() {
switch (this.handle.getTag()) {
case Opcodes.H_INVOKEVIRTUAL:
case Opcodes.H_INVOKESTATIC:
case Opcodes.H_INVOKEINTERFACE:
case Opcodes.H_INVOKESPECIAL:
case Opcodes.H_NEWINVOKESPECIAL:
return false;
case Opcodes.H_GETFIELD:
case Opcodes.H_GETSTATIC:
case Opcodes.H_PUTFIELD:
case Opcodes.H_PUTSTATIC:
return true;
default:
throw new MixinTransformerError("Invalid tag " + this.handle.getTag() + " for method handle " + this.handle + ".");
}
}
@Override
public int getOpcode() {
switch (this.handle.getTag()) {
case Opcodes.H_INVOKEVIRTUAL:
return Opcodes.INVOKEVIRTUAL;
case Opcodes.H_INVOKESTATIC:
return Opcodes.INVOKESTATIC;
case Opcodes.H_INVOKEINTERFACE:
return Opcodes.INVOKEINTERFACE;
case Opcodes.H_INVOKESPECIAL:
case Opcodes.H_NEWINVOKESPECIAL:
return Opcodes.INVOKESPECIAL;
case Opcodes.H_GETFIELD:
return Opcodes.GETFIELD;
case Opcodes.H_GETSTATIC:
return Opcodes.GETSTATIC;
case Opcodes.H_PUTFIELD:
return Opcodes.PUTFIELD;
case Opcodes.H_PUTSTATIC:
return Opcodes.PUTSTATIC;
default:
throw new MixinTransformerError("Invalid tag " + this.handle.getTag() + " for method handle " + this.handle + ".");
}
}
@Override
public String getOwner() {
return this.handle.getOwner();
}
@Override
public void setOwner(String owner) {
this.handle = new org.spongepowered.asm.lib.Handle(this.handle.getTag(), owner, this.handle.getName(), this.handle.getDesc());
}
@Override
public String getName() {
return this.handle.getName();
}
@Override
public void setName(String name) {
this.handle = new org.spongepowered.asm.lib.Handle(this.handle.getTag(), this.handle.getOwner(), name, this.handle.getDesc());
}
@Override
public String getDesc() {
return this.handle.getDesc();
}
@Override
public void setDesc(String desc) {
this.handle = new org.spongepowered.asm.lib.Handle(this.handle.getTag(), this.handle.getOwner(), this.handle.getName(), desc);
}
}
/**
* Whether this member is a field.
*
* @return If this member is a field, else it is a method
*/
public abstract boolean isField();
/**
* The opcode of the invocation.
*
* @return The opcode of the invocation
*/
public abstract int getOpcode();
/**
* The internal name for the owner of this member.
*
* @return The owners name
*/
public abstract String getOwner();
/**
* Changes the owner of this
*
* @param owner New owner
*/
public abstract void setOwner(String owner);
/**
* Name of this member.
*
* @return Name of this member.
*/
public abstract String getName();
/**
* Rename this member.
*
* @param name New name for this member.
*/
public abstract void setName(String name);
/**
* Descriptor of this member.
*
* @return Descriptor of this member
*/
public abstract String getDesc();
/**
* Changes the descriptor of this member
*
* @param desc New descriptor of this member
*/
public abstract void setDesc(String desc);
@Override
public String toString() {
return Bytecode.getOpcodeName(this.getOpcode()) + " for "
+ this.getOwner() + "." + this.getName() + (this.isField() ? ":" : "") + this.getDesc();
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof MemberRef)) {
return false;
}
MemberRef other = (MemberRef)obj;
return this.getOpcode() == other.getOpcode()
&& this.getOwner().equals(other.getOwner())
&& this.getName().equals(other.getName())
&& this.getDesc().equals(other.getDesc());
}
@Override
public int hashCode() {
return this.toString().hashCode();
}
}