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

proguard.classfile.instruction.ConstantInstruction Maven / Gradle / Ivy

Go to download

ProGuardCORE is a free library to read, analyze, modify, and write Java class files.

There is a newer version: 9.1.6
Show newest version
/*
 * ProGuardCORE -- library to process Java bytecode.
 *
 * Copyright (c) 2002-2020 Guardsquare NV
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package proguard.classfile.instruction;

import proguard.classfile.*;
import proguard.classfile.attribute.CodeAttribute;
import proguard.classfile.constant.*;
import proguard.classfile.constant.visitor.*;
import proguard.classfile.instruction.visitor.InstructionVisitor;
import proguard.classfile.util.ClassUtil;

import java.util.Objects;

/**
 * This {@link Instruction} represents an instruction that refers to an entry in the
 * constant pool.
 *
 * @author Eric Lafortune
 */
public class ConstantInstruction extends Instruction
implements   ConstantVisitor
{
    public int constantIndex;
    public int constant;


    // Fields acting as return parameters for the ConstantVisitor methods.
    private int parameterStackDelta;
    private int typeStackDelta;


    /**
     * Creates an uninitialized ConstantInstruction.
     */
    public ConstantInstruction() {}


    /**
     * Creates a new ConstantInstruction with the given opcode and constant pool
     * index.
     */
    public ConstantInstruction(byte opcode, int constantIndex)
    {
        this(opcode, constantIndex, 0);
    }


    /**
     * Creates a new ConstantInstruction with the given opcode, constant pool
     * index, and constant.
     */
    public ConstantInstruction(byte opcode, int constantIndex, int constant)
    {
        this.opcode        = opcode;
        this.constantIndex = constantIndex;
        this.constant      = constant;
    }


    /**
     * Copies the given instruction into this instruction.
     * @param constantInstruction the instruction to be copied.
     * @return this instruction.
     */
    public ConstantInstruction copy(ConstantInstruction constantInstruction)
    {
        this.opcode        = constantInstruction.opcode;
        this.constantIndex = constantInstruction.constantIndex;
        this.constant      = constantInstruction.constant;

        return this;
    }


    // Implementations for Instruction.


    /**
     * Returns whether a particular instance of an instruction may throw
     * an exception.
     *
     * In particular, this takes into account that an ldc with a class,
     * method type or method ref constant operand may throw exceptions
     * while other constants may not.
     */
    @Override
    public boolean mayInstanceThrowExceptions(Clazz clazz)
    {
        if (this.canonicalOpcode() == Instruction.OP_LDC)
        {
            ConstantMayThrowChecker classConstantChecker = new ConstantMayThrowChecker();
            clazz.constantPoolEntryAccept(this.constantIndex, classConstantChecker);
            return classConstantChecker.mayThrow;
        }
        else
        {
            return super.mayInstanceThrowExceptions(clazz);
        }
    }


    public byte canonicalOpcode()
    {
        // Remove the _w extension, if any.
        return
            opcode == Instruction.OP_LDC_W ? Instruction.OP_LDC :
                                                      opcode;
    }

    public Instruction shrink()
    {
        // Do we need a short index or a long index?
        if (requiredConstantIndexSize() == 1)
        {
            // Can we replace the long instruction by a short instruction?
            if (opcode == Instruction.OP_LDC_W)
            {
                opcode = Instruction.OP_LDC;
            }
        }
        else
        {
            // Should we replace the short instruction by a long instruction?
            if (opcode == Instruction.OP_LDC)
            {
                opcode = Instruction.OP_LDC_W;
            }
        }

        return this;
    }

    protected void readInfo(byte[] code, int offset)
    {
        int constantIndexSize = constantIndexSize();
        int constantSize      = constantSize();

        constantIndex = readValue(code, offset, constantIndexSize);  offset += constantIndexSize;
        constant      = readValue(code, offset, constantSize);
    }


    protected void writeInfo(byte[] code, int offset)
    {
        int constantIndexSize = constantIndexSize();
        int constantSize      = constantSize();

        if (requiredConstantIndexSize() > constantIndexSize)
        {
            throw new IllegalArgumentException("Instruction has invalid constant index size ("+this.toString(offset)+")");
        }

        writeValue(code, offset, constantIndex, constantIndexSize); offset += constantIndexSize;
        writeValue(code, offset, constant,      constantSize);
    }


    public int length(int offset)
    {
        return 1 + constantIndexSize() + constantSize();
    }


    public void accept(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, InstructionVisitor instructionVisitor)
    {
        instructionVisitor.visitConstantInstruction(clazz, method, codeAttribute, offset, this);
    }


    public String toString(Clazz clazz, int offset)
    {
        StringBuilder builder = new StringBuilder("[" + offset + "] " + this.toString()+" = ");

        // Append the constant in readable form.
        clazz.constantPoolEntryAccept(constantIndex, new ConstantStringBuilder(builder));

        return builder.toString();
    }


    public int stackPopCount(Clazz clazz)
    {
        int stackPopCount = super.stackPopCount(clazz);

        // Some special cases.
        switch (opcode)
        {
            case Instruction.OP_MULTIANEWARRAY:
                // For each dimension, an integer size is popped from the stack.
                stackPopCount += constant;
                break;

            case Instruction.OP_PUTSTATIC:
            case Instruction.OP_PUTFIELD:
                // The field value is be popped from the stack.
                clazz.constantPoolEntryAccept(constantIndex, this);
                stackPopCount += typeStackDelta;
                break;

            case Instruction.OP_INVOKEVIRTUAL:
            case Instruction.OP_INVOKESPECIAL:
            case Instruction.OP_INVOKESTATIC:
            case Instruction.OP_INVOKEINTERFACE:
            case Instruction.OP_INVOKEDYNAMIC:
                // Some parameters may be popped from the stack.
                clazz.constantPoolEntryAccept(constantIndex, this);
                stackPopCount += parameterStackDelta;
                break;
        }

        return stackPopCount;
    }


    public int stackPushCount(Clazz clazz)
    {
        int stackPushCount = super.stackPushCount(clazz);

        // Some special cases.
        switch (opcode)
        {
            case Instruction.OP_GETSTATIC:
            case Instruction.OP_GETFIELD:
            case Instruction.OP_INVOKEVIRTUAL:
            case Instruction.OP_INVOKESPECIAL:
            case Instruction.OP_INVOKESTATIC:
            case Instruction.OP_INVOKEINTERFACE:
            case Instruction.OP_INVOKEDYNAMIC:
                // The field value or a return value may be pushed onto the stack.
                clazz.constantPoolEntryAccept(constantIndex, this);
                stackPushCount += typeStackDelta;
                break;
        }

        return stackPushCount;
    }


    // Implementations for ConstantVisitor.

    public void visitIntegerConstant(Clazz clazz, IntegerConstant integerConstant) {}
    public void visitLongConstant(Clazz clazz, LongConstant longConstant) {}
    public void visitFloatConstant(Clazz clazz, FloatConstant floatConstant) {}
    public void visitDoubleConstant(Clazz clazz, DoubleConstant doubleConstant) {}
    public void visitPrimitiveArrayConstant(Clazz clazz, PrimitiveArrayConstant primitiveArrayConstant) {}
    public void visitStringConstant(Clazz clazz, StringConstant stringConstant) {}
    public void visitUtf8Constant(Clazz clazz, Utf8Constant utf8Constant) {}
    public void visitMethodHandleConstant(Clazz clazz, MethodHandleConstant methodHandleConstant) {}
    public void visitClassConstant(Clazz clazz, ClassConstant classConstant) {}
    public void visitMethodTypeConstant(Clazz clazz, MethodTypeConstant methodTypeConstant) {}
    public void visitModuleConstant(Clazz clazz, ModuleConstant moduleConstant) {}
    public void visitPackageConstant(Clazz clazz, PackageConstant packageConstant) {}


    public void visitFieldrefConstant(Clazz clazz, FieldrefConstant fieldrefConstant)
    {
        String type = fieldrefConstant.getType(clazz);

        typeStackDelta = ClassUtil.internalTypeSize(ClassUtil.internalMethodReturnType(type));
    }


    public void visitDynamicConstant(Clazz clazz, DynamicConstant dynamicConstant)
    {
        clazz.constantPoolEntryAccept(dynamicConstant.u2nameAndTypeIndex, this);
    }


    public void visitInvokeDynamicConstant(Clazz clazz, InvokeDynamicConstant invokeDynamicConstant)
    {
        clazz.constantPoolEntryAccept(invokeDynamicConstant.u2nameAndTypeIndex, this);
    }


    public void visitInterfaceMethodrefConstant(Clazz clazz, InterfaceMethodrefConstant interfaceMethodrefConstant)
    {
        clazz.constantPoolEntryAccept(interfaceMethodrefConstant.u2nameAndTypeIndex, this);
    }


    public void visitMethodrefConstant(Clazz clazz, MethodrefConstant methodrefConstant)
    {
        clazz.constantPoolEntryAccept(methodrefConstant.u2nameAndTypeIndex, this);
    }


    public void visitNameAndTypeConstant(Clazz clazz, NameAndTypeConstant nameAndTypeConstant)
    {
        String type = nameAndTypeConstant.getType(clazz);

        parameterStackDelta = ClassUtil.internalMethodParameterSize(type);
        typeStackDelta      = ClassUtil.internalTypeSize(ClassUtil.internalMethodReturnType(type));
    }


    // Implementations for Object.

    @Override
    public String toString()
    {
        return getName()+" #"+constantIndex+(constantSize() == 0 ? "" : ", "+constant);
    }


    @Override
    public boolean equals(Object o)
    {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        ConstantInstruction that = (ConstantInstruction)o;
        return opcode == that.opcode &&
               constantIndex == that.constantIndex &&
               constant == that.constant;
    }


    @Override
    public int hashCode()
    {
        return Objects.hash(opcode, constantIndex, constant);
    }


    // Small utility methods.

    /**
     * Returns the constant pool index size for this instruction.
     */
    private int constantIndexSize()
    {
        return opcode == Instruction.OP_LDC ? 1 :
                                                       2;
    }


    /**
     * Returns the constant size for this instruction.
     */
    private int constantSize()
    {
        return opcode == Instruction.OP_MULTIANEWARRAY  ? 1 :
               opcode == Instruction.OP_INVOKEDYNAMIC ||
               opcode == Instruction.OP_INVOKEINTERFACE ? 2 :
                                                                   0;
    }


    /**
     * Computes the required constant pool index size for this instruction's
     * constant pool index.
     */
    private int requiredConstantIndexSize()
    {
        return (constantIndex &   0xff) == constantIndex ? 1 :
               (constantIndex & 0xffff) == constantIndex ? 2 :
                                                           4;
    }


    private static class ConstantMayThrowChecker implements ConstantVisitor
    {
        public boolean mayThrow = false;

        @Override
        public void visitAnyConstant(Clazz clazz, Constant constant) { }


        @Override
        public void visitClassConstant(Clazz clazz, ClassConstant classConstant)
        {
            this.mayThrow = true;
        }


        @Override
        public void visitMethodHandleConstant(Clazz clazz, MethodHandleConstant methodHandleConstant)
        {
            this.mayThrow = true;
        }


        @Override
        public void visitMethodTypeConstant(Clazz clazz, MethodTypeConstant methodTypeConstant)
        {
            this.mayThrow = true;
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy