org.sonar.java.bytecode.cfg.Instruction.java Source code

Java tutorial

Introduction

Here is the source code for org.sonar.java.bytecode.cfg.Instruction.java

Source

/*
 * SonarQube Java
 * Copyright (C) 2012-2018 SonarSource SA
 * mailto:info AT sonarsource DOT com
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 3 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */
package org.sonar.java.bytecode.cfg;

import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableSet;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.util.Printer;

import java.util.Objects;
import java.util.Set;

import static org.objectweb.asm.Opcodes.DADD;
import static org.objectweb.asm.Opcodes.DALOAD;
import static org.objectweb.asm.Opcodes.DCONST_0;
import static org.objectweb.asm.Opcodes.DCONST_1;
import static org.objectweb.asm.Opcodes.DDIV;
import static org.objectweb.asm.Opcodes.DMUL;
import static org.objectweb.asm.Opcodes.DNEG;
import static org.objectweb.asm.Opcodes.DREM;
import static org.objectweb.asm.Opcodes.DSUB;
import static org.objectweb.asm.Opcodes.GETFIELD;
import static org.objectweb.asm.Opcodes.GETSTATIC;
import static org.objectweb.asm.Opcodes.LADD;
import static org.objectweb.asm.Opcodes.LALOAD;
import static org.objectweb.asm.Opcodes.LAND;
import static org.objectweb.asm.Opcodes.LCONST_0;
import static org.objectweb.asm.Opcodes.LCONST_1;
import static org.objectweb.asm.Opcodes.LDIV;
import static org.objectweb.asm.Opcodes.LMUL;
import static org.objectweb.asm.Opcodes.LNEG;
import static org.objectweb.asm.Opcodes.LOR;
import static org.objectweb.asm.Opcodes.LREM;
import static org.objectweb.asm.Opcodes.LSHL;
import static org.objectweb.asm.Opcodes.LSHR;
import static org.objectweb.asm.Opcodes.LSUB;
import static org.objectweb.asm.Opcodes.LUSHR;
import static org.objectweb.asm.Opcodes.LXOR;

/**
 * Bytecode instruction.
 */
public class Instruction {

    private static final Set<Integer> LONG_DOUBLE_OPCODES = ImmutableSet.of(LADD, LSUB, LMUL, LDIV, LAND, LOR, LXOR,
            LREM, LSHL, LSHR, LUSHR, DADD, DSUB, DMUL, DDIV, DREM, DCONST_0, DCONST_1, LCONST_0, LCONST_1, LALOAD,
            DALOAD, DNEG, LNEG);

    public final int opcode;
    public final Integer operand;
    public final String className;
    public final FieldOrMethod fieldOrMethod;

    @VisibleForTesting
    public Instruction(int opcode, int operand) {
        this.opcode = opcode;
        this.operand = operand;
        this.className = null;
        this.fieldOrMethod = null;
    }

    public Instruction(int opcode) {
        this.opcode = opcode;
        this.operand = null;
        this.className = null;
        this.fieldOrMethod = null;
    }

    public Instruction(int opcode, String className) {
        this.opcode = opcode;
        this.className = className;
        this.operand = null;
        this.fieldOrMethod = null;
    }

    public Instruction(int opcode, FieldOrMethod fieldOrMethod) {
        this.opcode = opcode;
        this.fieldOrMethod = fieldOrMethod;
        this.operand = null;
        this.className = null;
    }

    int opcode() {
        return opcode;
    }

    public int arity() {
        Preconditions.checkState(isInvoke(), "Not an INVOKE opcode");
        Type methodType = Type.getMethodType(fieldOrMethod.desc);
        return methodType.getArgumentTypes().length;
    }

    public boolean hasReturnValue() {
        Preconditions.checkState(isInvoke(), "Not an INVOKE opcode");
        return Type.getMethodType(fieldOrMethod.desc).getReturnType() != Type.VOID_TYPE;
    }

    @VisibleForTesting
    public boolean isInvoke() {
        return Opcodes.INVOKEVIRTUAL <= opcode && opcode <= Opcodes.INVOKEDYNAMIC;
    }

    public boolean isLongOrDoubleValue() {
        if (LONG_DOUBLE_OPCODES.contains(opcode)) {
            return true;
        }
        if (opcode == GETFIELD || opcode == GETSTATIC) {
            return Type.getType(fieldOrMethod.desc).getSize() == 2;
        }
        if (isInvoke()) {
            return Type.getReturnType(fieldOrMethod.desc).getSize() == 2;
        }
        return false;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }
        Instruction that = (Instruction) o;
        return opcode == that.opcode && Objects.equals(operand, that.operand)
                && Objects.equals(className, that.className) && Objects.equals(fieldOrMethod, that.fieldOrMethod);
    }

    @Override
    public int hashCode() {
        return Objects.hash(opcode, operand, className, fieldOrMethod);
    }

    @Override
    public String toString() {
        return Printer.OPCODES[opcode];
    }

    public static class FieldOrMethod {
        public final String owner;
        public final String name;
        public final String desc;
        public final boolean ownerIsInterface;

        public FieldOrMethod(String owner, String name, String desc, boolean ownerIsInterface) {
            this.owner = owner;
            this.name = name;
            this.desc = desc;
            this.ownerIsInterface = ownerIsInterface;
        }

        public FieldOrMethod(String owner, String name, String desc) {
            this(owner, name, desc, false);
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) {
                return true;
            }
            if (o == null || getClass() != o.getClass()) {
                return false;
            }
            FieldOrMethod that = (FieldOrMethod) o;
            return ownerIsInterface == that.ownerIsInterface && Objects.equals(owner, that.owner)
                    && Objects.equals(name, that.name) && Objects.equals(desc, that.desc);
        }

        @Override
        public int hashCode() {
            return Objects.hash(owner, name, desc, ownerIsInterface);
        }

        public String completeSignature() {
            return Type.getObjectType(owner).getClassName() + "#" + name + desc;
        }
    }

    public static class MultiANewArrayInsn extends Instruction {

        public final int dim;

        public MultiANewArrayInsn(String className, int dim) {
            super(Opcodes.MULTIANEWARRAY, className);
            this.dim = dim;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) {
                return true;
            }
            if (o == null || getClass() != o.getClass()) {
                return false;
            }
            if (!super.equals(o)) {
                return false;
            }
            MultiANewArrayInsn that = (MultiANewArrayInsn) o;
            return dim == that.dim;
        }

        @Override
        public int hashCode() {
            return Objects.hash(super.hashCode(), dim);
        }
    }

    public static class InvokeDynamicInsn extends Instruction {

        private final String desc;

        public InvokeDynamicInsn(String desc) {
            super(Opcodes.INVOKEDYNAMIC);
            this.desc = desc;
        }

        @Override
        public int arity() {
            Type methodType = Type.getMethodType(desc);
            return methodType.getArgumentTypes().length;
        }

        @Override
        public boolean hasReturnValue() {
            return Type.getMethodType(desc).getReturnType() != Type.VOID_TYPE;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) {
                return true;
            }
            if (o == null || getClass() != o.getClass()) {
                return false;
            }
            if (!super.equals(o)) {
                return false;
            }
            InvokeDynamicInsn that = (InvokeDynamicInsn) o;
            return Objects.equals(desc, that.desc);
        }

        @Override
        public int hashCode() {
            return Objects.hash(super.hashCode(), desc);
        }
    }

    public static class LdcInsn extends Instruction {

        public final Object cst;

        public LdcInsn(Object cst) {
            super(Opcodes.LDC);
            this.cst = cst;
        }

        @Override
        public boolean isLongOrDoubleValue() {
            return cst instanceof Long || cst instanceof Double;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) {
                return true;
            }
            if (o == null || getClass() != o.getClass()) {
                return false;
            }
            if (!super.equals(o)) {
                return false;
            }
            LdcInsn ldcInsn = (LdcInsn) o;
            return Objects.equals(cst, ldcInsn.cst);
        }

        @Override
        public int hashCode() {
            return Objects.hash(super.hashCode(), cst);
        }
    }
}