/* * 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.invoke; import org.spongepowered.asm.lib.Opcodes; import org.spongepowered.asm.lib.Type; import org.spongepowered.asm.lib.tree.AbstractInsnNode; import org.spongepowered.asm.lib.tree.InsnList; import org.spongepowered.asm.lib.tree.InsnNode; import org.spongepowered.asm.lib.tree.JumpInsnNode; import org.spongepowered.asm.lib.tree.VarInsnNode; import org.spongepowered.asm.mixin.injection.InjectionNodes.InjectionNode; import org.spongepowered.asm.mixin.injection.struct.InjectionInfo; import org.spongepowered.asm.mixin.injection.struct.Target; import org.spongepowered.asm.mixin.injection.throwables.InvalidInjectionException; import org.spongepowered.asm.util.Bytecode; /** * A bytecode injector which allows a specific */ public class ModifyConstantInjector extends RedirectInjector { /** * Offset between "implicit zero" opcodes and "explicit int comparison" * opcodes */ private static final int OPCODE_OFFSET = Opcodes.IF_ICMPLT - Opcodes.IFLT; /** * @param info Injection info */ public ModifyConstantInjector(InjectionInfo info) { super(info, "@ModifyConstant"); } @Override protected void inject(Target target, InjectionNode node) { if (!this.preInject(node)) { return; } if (node.isReplaced()) { throw new UnsupportedOperationException("Target failure for " + this.info); } AbstractInsnNode targetNode = node.getCurrentTarget(); if (targetNode instanceof JumpInsnNode) { this.injectExpandedConstantModifier(target, (JumpInsnNode)targetNode); return; } if (Bytecode.isConstant(targetNode)) { this.injectConstantModifier(target, targetNode); return; } throw new InvalidInjectionException(this.info, this.annotationType + " annotation is targetting an invalid insn in " + target + " in " + this); } /** * Injects a constant modifier at an implied-zero * * @param target target method * @param jumpNode jump instruction (must be IFLT, IFGE, IFGT or IFLE) */ private void injectExpandedConstantModifier(Target target, JumpInsnNode jumpNode) { int opcode = jumpNode.getOpcode(); if (opcode < Opcodes.IFLT || opcode > Opcodes.IFLE) { throw new InvalidInjectionException(this.info, this.annotationType + " annotation selected an invalid opcode " + Bytecode.getOpcodeName(opcode) + " in " + target + " in " + this); } final InsnList insns = new InsnList(); insns.add(new InsnNode(Opcodes.ICONST_0)); AbstractInsnNode invoke = this.invokeConstantHandler(Type.getType("I"), target, insns, insns); insns.add(new JumpInsnNode(opcode + ModifyConstantInjector.OPCODE_OFFSET, jumpNode.label)); target.replaceNode(jumpNode, invoke, insns); target.addToStack(1); } private void injectConstantModifier(Target target, AbstractInsnNode constNode) { final Type constantType = Bytecode.getConstantType(constNode); final InsnList before = new InsnList(); final InsnList after = new InsnList(); AbstractInsnNode invoke = this.invokeConstantHandler(constantType, target, before, after); target.wrapNode(constNode, invoke, before, after); } private AbstractInsnNode invokeConstantHandler(Type constantType, Target target, InsnList before, InsnList after) { final String handlerDesc = Bytecode.generateDescriptor(constantType, constantType); final boolean withArgs = this.checkDescriptor(handlerDesc, target, "getter"); if (!this.isStatic) { before.insert(new VarInsnNode(Opcodes.ALOAD, 0)); target.addToStack(1); } if (withArgs) { this.pushArgs(target.arguments, after, target.argIndices, 0, target.arguments.length); target.addToStack(Bytecode.getArgsSize(target.arguments)); } return this.invokeHandler(after); } }