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

com.llamalad7.mixinextras.expression.impl.ast.expressions.BinaryExpression Maven / Gradle / Ivy

package com.llamalad7.mixinextras.expression.impl.ast.expressions;

import com.llamalad7.mixinextras.expression.impl.ExpressionSource;
import com.llamalad7.mixinextras.expression.impl.utils.ExpressionDecorations;
import com.llamalad7.mixinextras.expression.impl.utils.ExpressionUtil;
import com.llamalad7.mixinextras.expression.impl.flow.FlowValue;
import com.llamalad7.mixinextras.expression.impl.flow.postprocessing.StringConcatInfo;
import com.llamalad7.mixinextras.expression.impl.point.ExpressionContext;
import com.llamalad7.mixinextras.expression.impl.utils.FlowDecorations;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.AbstractInsnNode;

public class BinaryExpression extends SimpleExpression {
    public final Expression left;
    public final Operator operator;
    public final Expression right;

    public BinaryExpression(ExpressionSource src, Expression left, Operator operator, Expression right) {
        super(src);
        this.left = left;
        this.operator = operator;
        this.right = right;
    }

    @Override
    public boolean matches(FlowValue node, ExpressionContext ctx) {
        if (operator.matches(node.getInsn()) && inputsMatch(node, ctx, left, right)) {
            return true;
        }
        StringConcatInfo concat = node.getDecoration(FlowDecorations.STRING_CONCAT_INFO);
        if (operator != Operator.PLUS || concat == null) {
            return false;
        }
        if (node == concat.toStringCall) {
            node = node.getInput(0);
        }
        if (!right.matches(node.getInput(1), ctx)) {
            return false;
        }
        if (concat.isFirstConcat) {
            return left.matches(concat.initialComponent, ctx);
        }
        Expression innerLeft = ExpressionUtil.skipCapturesDown(left);
        if (innerLeft instanceof WildcardExpression) {
            if (!(left instanceof CapturingExpression)) {
                return true;
            }
            // The wildcard will match the concatenation to the left, but won't decorate it as a concat, so we do it
            // ourselves.
            checkSupportsStringConcat(ctx.type);
            ctx.decorateInjectorSpecific(node.getInput(0).getInsn(), ExpressionDecorations.IS_STRING_CONCAT_EXPRESSION, true);
            // Do the capture:
            return left.matches(node.getInput(0), ctx);
        }
        if (innerLeft instanceof BinaryExpression && ((BinaryExpression) innerLeft).operator == Operator.PLUS) {
            // Continue matching the concat chain.
            return left.matches(node.getInput(0), ctx);
        }
        return false;
    }

    @Override
    public void capture(FlowValue node, ExpressionContext ctx) {
        StringConcatInfo concat = node.getDecoration(FlowDecorations.STRING_CONCAT_INFO);
        if (concat == null) {
            super.capture(node, ctx);
            return;
        }
        checkSupportsStringConcat(ctx.type);
        if (concat.isLastConcat) {
            // Simple optimization.
            super.capture(concat.toStringCall, ctx);
            return;
        }
        ctx.decorateInjectorSpecific(node.getInsn(), ExpressionDecorations.IS_STRING_CONCAT_EXPRESSION, true);
        super.capture(node, ctx);
    }

    private void checkSupportsStringConcat(ExpressionContext.Type type) {
        switch (type) {
            case SLICE:
            case INJECT:
            case MODIFY_VARIABLE:
                // Tolerate, they don't care about their target instruction.
                return;
            case MODIFY_EXPRESSION_VALUE:
                // Supported.
                return;
        }
        throw new UnsupportedOperationException(
                String.format(
                        "Expression context type %s does not support string concat!",
                        type
                )
        );
    }

    public enum Operator {
        MULT(Opcodes.IMUL, Opcodes.LMUL, Opcodes.FMUL, Opcodes.DMUL),
        DIV(Opcodes.IDIV, Opcodes.LDIV, Opcodes.FDIV, Opcodes.DDIV),
        MOD(Opcodes.IREM, Opcodes.LREM, Opcodes.FREM, Opcodes.DREM),
        PLUS(Opcodes.IADD, Opcodes.LADD, Opcodes.FADD, Opcodes.DADD),
        MINUS(Opcodes.ISUB, Opcodes.LSUB, Opcodes.FSUB, Opcodes.DSUB),
        SHL(Opcodes.ISHL, Opcodes.LSHL),
        SHR(Opcodes.ISHR, Opcodes.LSHR),
        USHR(Opcodes.IUSHR, Opcodes.LUSHR),
        BITWISE_AND(Opcodes.IAND, Opcodes.LAND),
        BITWISE_XOR(Opcodes.IXOR, Opcodes.LXOR),
        BITWISE_OR(Opcodes.IOR, Opcodes.LOR);

        private final int[] opcodes;

        Operator(int... opcodes) {
            this.opcodes = opcodes;
        }

        public boolean matches(AbstractInsnNode insn) {
            for (int opcode : opcodes) {
                if (opcode == insn.getOpcode()) {
                    return true;
                }
            }
            return false;
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy