All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.llamalad7.mixinextras.injector.ModifyReceiverInjector Maven / Gradle / Ivy

Go to download

Companion library to Mixin with lots of features to improve the compatibility and concision of your mixins!

There is a newer version: 0.5.0-beta.4
Show newest version
package com.llamalad7.mixinextras.injector;

import com.llamalad7.mixinextras.utils.CompatibilityHelper;
import com.llamalad7.mixinextras.utils.InjectorUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.*;
import org.spongepowered.asm.mixin.injection.code.Injector;
import org.spongepowered.asm.mixin.injection.struct.InjectionInfo;
import org.spongepowered.asm.mixin.injection.struct.InjectionNodes.InjectionNode;
import org.spongepowered.asm.mixin.injection.struct.Target;

public class ModifyReceiverInjector extends Injector {
    public ModifyReceiverInjector(InjectionInfo info) {
        super(info, "@ModifyReceiver");
    }

    @Override
    protected void inject(Target target, InjectionNode node) {
        this.checkTargetIsValid(target, node);
        this.checkTargetModifiers(target, false);
        this.modifyReceiverOfTarget(target, node);
    }

    private void checkTargetIsValid(Target target, InjectionNode node) {
        AbstractInsnNode insn = node.getOriginalTarget();
        switch (insn.getOpcode()) {
            case Opcodes.INVOKEVIRTUAL:
            case Opcodes.INVOKESPECIAL:
            case Opcodes.INVOKEINTERFACE:
            case Opcodes.GETFIELD:
            case Opcodes.PUTFIELD:
                return;
            default:
                throw CompatibilityHelper.makeInvalidInjectionException(this.info, String.format("%s annotation is targeting an invalid insn in %s in %s",
                        this.annotationType, target, this));
        }
    }

    private void modifyReceiverOfTarget(Target target, InjectionNode node) {
        AbstractInsnNode currentTarget = node.getCurrentTarget();
        Type[] originalArgTypes = getEffectiveArgTypes(node.getOriginalTarget());
        Type[] currentArgTypes = getEffectiveArgTypes(currentTarget);
        InsnList insns = new InsnList();
        boolean isVirtualRedirect = InjectorUtils.isVirtualRedirect(node);
        this.injectReceiverModifier(target, originalArgTypes, currentArgTypes, isVirtualRedirect, insns);
        target.insertBefore(node, insns);
    }

    private void injectReceiverModifier(Target target, Type[] originalArgTypes, Type[] currentArgTypes, boolean isVirtualRedirect, InsnList insns) {
        InjectorData handler = new InjectorData(target, "receiver modifier");
        this.validateParams(handler, originalArgTypes[0], originalArgTypes);
        StackExtension stack = new StackExtension(target);

        int[] argMap = this.storeArgs(target, currentArgTypes, insns, 0);
        int[] handlerArgMap = ArrayUtils.addAll(argMap, target.getArgIndices());
        if (isVirtualRedirect) {
            // We need to disregard the extra "this" which will be added for a virtual redirect.
            handlerArgMap = ArrayUtils.remove(handlerArgMap, 0);
            // We also need to ensure it remains on the stack before the receiver
            insns.add(new VarInsnNode(Opcodes.ALOAD, 0));
        }

        stack.receiver(this.isStatic);
        stack.capturedArgs(target.arguments, handler.captureTargetArgs);

        this.invokeHandlerWithArgs(this.methodArgs, insns, handlerArgMap);

        // If this is a virtual redirect, both "this" and the receiver are already on the stack
        this.pushArgs(currentArgTypes, insns, argMap, isVirtualRedirect ? 2 : 1, argMap.length);
    }

    private Type[] getEffectiveArgTypes(AbstractInsnNode node) {
        switch (node.getOpcode()) {
            case Opcodes.INVOKEVIRTUAL:
            case Opcodes.INVOKEINTERFACE: {
                MethodInsnNode methodInsnNode = (MethodInsnNode) node;
                return ArrayUtils.addAll(new Type[]{Type.getObjectType(methodInsnNode.owner)}, Type.getArgumentTypes(methodInsnNode.desc));
            }
            case Opcodes.INVOKESPECIAL: {
                MethodInsnNode methodInsnNode = (MethodInsnNode) node;
                return ArrayUtils.addAll(new Type[]{Type.getObjectType(classNode.name)}, Type.getArgumentTypes(methodInsnNode.desc));
            }
            case Opcodes.GETFIELD: {
                FieldInsnNode fieldInsnNode = ((FieldInsnNode) node);
                return new Type[]{Type.getObjectType(fieldInsnNode.owner)};
            }
            case Opcodes.PUTFIELD: {
                FieldInsnNode fieldInsnNode = ((FieldInsnNode) node);
                return new Type[]{Type.getObjectType(fieldInsnNode.owner), Type.getType(fieldInsnNode.desc)};
            }
        }

        throw new UnsupportedOperationException();
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy