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

scala.tools.asm.tree.analysis.Frame Maven / Gradle / Ivy

There is a newer version: 9.7.1-scala-1
Show newest version
// ASM: a very small and fast Java bytecode manipulation framework
// Copyright (c) 2000-2011 INRIA, France Telecom
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// 1. Redistributions of source code must retain the above copyright
//    notice, this list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright
//    notice, this list of conditions and the following disclaimer in the
//    documentation and/or other materials provided with the distribution.
// 3. Neither the name of the copyright holders nor the names of its
//    contributors may be used to endorse or promote products derived from
//    this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
// THE POSSIBILITY OF SUCH DAMAGE.
package scala.tools.asm.tree.analysis;

import java.util.ArrayList;
import java.util.List;
import scala.tools.asm.Opcodes;
import scala.tools.asm.Type;
import scala.tools.asm.tree.AbstractInsnNode;
import scala.tools.asm.tree.IincInsnNode;
import scala.tools.asm.tree.InvokeDynamicInsnNode;
import scala.tools.asm.tree.LabelNode;
import scala.tools.asm.tree.MethodInsnNode;
import scala.tools.asm.tree.MultiANewArrayInsnNode;
import scala.tools.asm.tree.VarInsnNode;

/**
 * A symbolic execution stack frame. A stack frame contains a set of local variable slots, and an
 * operand stack. Warning: long and double values are represented with two slots in local
 * variables, and with one slot in the operand stack.
 *
 * @param  type of the Value used for the analysis.
 * @author Eric Bruneton
 */
public class Frame {

  /** The maximum size of the operand stack of any method. */
  private static final int MAX_STACK_SIZE = 65536;

  /**
   * The expected return type of the analyzed method, or {@literal null} if the method returns void.
   */
  private V returnValue;

  /**
   * The local variables and the operand stack of this frame. The first {@link #numLocals} elements
   * correspond to the local variables. The following {@link #numStack} elements correspond to the
   * operand stack. Long and double values are represented with two elements in the local variables
   * section, and with one element in the operand stack section.
   */
  private V[] values;

  /**
   * The number of local variables of this frame. Long and double values are represented with two
   * elements.
   */
  private int numLocals;

  /**
   * The number of elements in the operand stack. Long and double values are represented with a
   * single element.
   */
  private int numStack;

  /**
   * The maximum number of elements in the operand stack. Long and double values are represented
   * with a single element.
   */
  private int maxStack;

  /**
   * Constructs a new frame with the given size.
   *
   * @param numLocals the number of local variables of the frame. Long and double values are
   *     represented with two elements.
   * @param maxStack the maximum number of elements in the operand stack, or -1 if there is no
   *     maximum value. Long and double values are represented with a single element.
   */
  @SuppressWarnings("unchecked")
  public Frame(final int numLocals, final int maxStack) {
    this.values = (V[]) new Value[numLocals + (maxStack >= 0 ? maxStack : 4)];
    this.numLocals = numLocals;
    this.numStack = 0;
    this.maxStack = maxStack >= 0 ? maxStack : MAX_STACK_SIZE;
  }

  /**
   * Constructs a copy of the given Frame.
   *
   * @param frame a frame.
   */
  public Frame(final Frame frame) {
    this(frame.numLocals, frame.values.length - frame.numLocals);
    init(frame); // NOPMD(ConstructorCallsOverridableMethod): can't fix for backward compatibility.
  }

  /**
   * Copies the state of the given frame into this frame.
   *
   * @param frame a frame.
   * @return this frame.
   */
  public Frame init(final Frame frame) {
    returnValue = frame.returnValue;
    if (values.length < frame.values.length) {
      values = frame.values.clone();
    } else {
      System.arraycopy(frame.values, 0, values, 0, frame.values.length);
    }
    numLocals = frame.numLocals;
    numStack = frame.numStack;
    maxStack = frame.maxStack;
    return this;
  }

  /**
   * Initializes a frame corresponding to the target or to the successor of a jump instruction. This
   * method is called by {@link Analyzer#analyze(String, scala.tools.asm.tree.MethodNode)} while
   * interpreting jump instructions. It is called once for each possible target of the jump
   * instruction, and once for its successor instruction (except for GOTO and JSR), before the frame
   * is merged with the existing frame at this location. The default implementation of this method
   * does nothing.
   *
   * 

Overriding this method and changing the frame values allows implementing branch-sensitive * analyses. * * @param opcode the opcode of the jump instruction. Can be IFEQ, IFNE, IFLT, IFGE, IFGT, IFLE, * IF_ICMPEQ, IF_ICMPNE, IF_ICMPLT, IF_ICMPGE, IF_ICMPGT, IF_ICMPLE, IF_ACMPEQ, IF_ACMPNE, * GOTO, JSR, IFNULL, IFNONNULL, TABLESWITCH or LOOKUPSWITCH. * @param target a target of the jump instruction this frame corresponds to, or {@literal null} if * this frame corresponds to the successor of the jump instruction (i.e. the next instruction * in the instructions sequence). */ public void initJumpTarget(final int opcode, final LabelNode target) { // Does nothing by default. } /** * Sets the expected return type of the analyzed method. * * @param v the expected return type of the analyzed method, or {@literal null} if the method * returns void. */ public void setReturn(final V v) { returnValue = v; } /** * Returns the maximum number of local variables of this frame. Long and double values are * represented with two variables. * * @return the maximum number of local variables of this frame. */ public int getLocals() { return numLocals; } /** * Returns the maximum number of elements in the operand stack of this frame. Long and double * values are represented with a single element. * * @return the maximum number of elements in the operand stack of this frame. */ public int getMaxStackSize() { return maxStack; } /** * Returns the value of the given local variable. Long and double values are represented with two * variables. * * @param index a local variable index. * @return the value of the given local variable. * @throws IndexOutOfBoundsException if the variable does not exist. */ public V getLocal(final int index) { if (index >= numLocals) { throw new IndexOutOfBoundsException("Trying to get an inexistant local variable " + index); } return values[index]; } /** * Sets the value of the given local variable. Long and double values are represented with two * variables. * * @param index a local variable index. * @param value the new value of this local variable. * @throws IndexOutOfBoundsException if the variable does not exist. */ public void setLocal(final int index, final V value) { if (index >= numLocals) { throw new IndexOutOfBoundsException("Trying to set an inexistant local variable " + index); } values[index] = value; } /** * Returns the number of elements in the operand stack of this frame. Long and double values are * represented with a single element. * * @return the number of elements in the operand stack of this frame. */ public int getStackSize() { return numStack; } /** * Returns the value of the given operand stack slot. * * @param index the index of an operand stack slot. * @return the value of the given operand stack slot. * @throws IndexOutOfBoundsException if the operand stack slot does not exist. */ public V getStack(final int index) { return values[numLocals + index]; } /** * Sets the value of the given stack slot. * * @param index the index of an operand stack slot. * @param value the new value of the stack slot. * @throws IndexOutOfBoundsException if the stack slot does not exist. */ public void setStack(final int index, final V value) { values[numLocals + index] = value; } /** Clears the operand stack of this frame. */ public void clearStack() { numStack = 0; } /** * Pops a value from the operand stack of this frame. * * @return the value that has been popped from the stack. * @throws IndexOutOfBoundsException if the operand stack is empty. */ public V pop() { if (numStack == 0) { throw new IndexOutOfBoundsException("Cannot pop operand off an empty stack."); } return values[numLocals + (--numStack)]; } /** * Pushes a value into the operand stack of this frame. * * @param value the value that must be pushed into the stack. * @throws IndexOutOfBoundsException if the operand stack is full. */ @SuppressWarnings("unchecked") public void push(final V value) { if (numLocals + numStack >= values.length) { if (numLocals + numStack >= maxStack) { throw new IndexOutOfBoundsException("Insufficient maximum stack size."); } V[] oldValues = values; values = (V[]) new Value[2 * values.length]; System.arraycopy(oldValues, 0, values, 0, oldValues.length); } values[numLocals + (numStack++)] = value; } /** * Simulates the execution of the given instruction on this execution stack frame. * * @param insn the instruction to execute. * @param interpreter the interpreter to use to compute values from other values. * @throws AnalyzerException if the instruction cannot be executed on this execution frame (e.g. a * POP on an empty operand stack). */ public void execute(final AbstractInsnNode insn, final Interpreter interpreter) throws AnalyzerException { V value1; V value2; V value3; V value4; int varIndex; switch (insn.getOpcode()) { case Opcodes.NOP: break; case Opcodes.ACONST_NULL: case Opcodes.ICONST_M1: case Opcodes.ICONST_0: case Opcodes.ICONST_1: case Opcodes.ICONST_2: case Opcodes.ICONST_3: case Opcodes.ICONST_4: case Opcodes.ICONST_5: case Opcodes.LCONST_0: case Opcodes.LCONST_1: case Opcodes.FCONST_0: case Opcodes.FCONST_1: case Opcodes.FCONST_2: case Opcodes.DCONST_0: case Opcodes.DCONST_1: case Opcodes.BIPUSH: case Opcodes.SIPUSH: case Opcodes.LDC: push(interpreter.newOperation(insn)); break; case Opcodes.ILOAD: case Opcodes.LLOAD: case Opcodes.FLOAD: case Opcodes.DLOAD: case Opcodes.ALOAD: push(interpreter.copyOperation(insn, getLocal(((VarInsnNode) insn).var))); break; case Opcodes.ISTORE: case Opcodes.LSTORE: case Opcodes.FSTORE: case Opcodes.DSTORE: case Opcodes.ASTORE: value1 = interpreter.copyOperation(insn, pop()); varIndex = ((VarInsnNode) insn).var; setLocal(varIndex, value1); if (value1.getSize() == 2) { setLocal(varIndex + 1, interpreter.newEmptyValue(varIndex + 1)); } if (varIndex > 0) { Value local = getLocal(varIndex - 1); if (local != null && local.getSize() == 2) { setLocal(varIndex - 1, interpreter.newEmptyValue(varIndex - 1)); } } break; case Opcodes.IASTORE: case Opcodes.LASTORE: case Opcodes.FASTORE: case Opcodes.DASTORE: case Opcodes.AASTORE: case Opcodes.BASTORE: case Opcodes.CASTORE: case Opcodes.SASTORE: value3 = pop(); value2 = pop(); value1 = pop(); interpreter.ternaryOperation(insn, value1, value2, value3); break; case Opcodes.POP: if (pop().getSize() == 2) { throw new AnalyzerException(insn, "Illegal use of POP"); } break; case Opcodes.POP2: if (pop().getSize() == 1 && pop().getSize() != 1) { throw new AnalyzerException(insn, "Illegal use of POP2"); } break; case Opcodes.DUP: value1 = pop(); if (value1.getSize() != 1) { throw new AnalyzerException(insn, "Illegal use of DUP"); } push(interpreter.copyOperation(insn, value1)); push(interpreter.copyOperation(insn, value1)); break; case Opcodes.DUP_X1: value1 = pop(); value2 = pop(); if (value1.getSize() != 1 || value2.getSize() != 1) { throw new AnalyzerException(insn, "Illegal use of DUP_X1"); } push(interpreter.copyOperation(insn, value1)); push(interpreter.copyOperation(insn, value2)); push(interpreter.copyOperation(insn, value1)); break; case Opcodes.DUP_X2: value1 = pop(); if (value1.getSize() == 1 && executeDupX2(insn, value1, interpreter)) { break; } throw new AnalyzerException(insn, "Illegal use of DUP_X2"); case Opcodes.DUP2: value1 = pop(); if (value1.getSize() == 1) { value2 = pop(); if (value2.getSize() == 1) { push(interpreter.copyOperation(insn, value2)); push(interpreter.copyOperation(insn, value1)); push(interpreter.copyOperation(insn, value2)); push(interpreter.copyOperation(insn, value1)); break; } } else { push(interpreter.copyOperation(insn, value1)); push(interpreter.copyOperation(insn, value1)); break; } throw new AnalyzerException(insn, "Illegal use of DUP2"); case Opcodes.DUP2_X1: value1 = pop(); if (value1.getSize() == 1) { value2 = pop(); if (value2.getSize() == 1) { value3 = pop(); if (value3.getSize() == 1) { push(interpreter.copyOperation(insn, value2)); push(interpreter.copyOperation(insn, value1)); push(interpreter.copyOperation(insn, value3)); push(interpreter.copyOperation(insn, value2)); push(interpreter.copyOperation(insn, value1)); break; } } } else { value2 = pop(); if (value2.getSize() == 1) { push(interpreter.copyOperation(insn, value1)); push(interpreter.copyOperation(insn, value2)); push(interpreter.copyOperation(insn, value1)); break; } } throw new AnalyzerException(insn, "Illegal use of DUP2_X1"); case Opcodes.DUP2_X2: value1 = pop(); if (value1.getSize() == 1) { value2 = pop(); if (value2.getSize() == 1) { value3 = pop(); if (value3.getSize() == 1) { value4 = pop(); if (value4.getSize() == 1) { push(interpreter.copyOperation(insn, value2)); push(interpreter.copyOperation(insn, value1)); push(interpreter.copyOperation(insn, value4)); push(interpreter.copyOperation(insn, value3)); push(interpreter.copyOperation(insn, value2)); push(interpreter.copyOperation(insn, value1)); break; } } else { push(interpreter.copyOperation(insn, value2)); push(interpreter.copyOperation(insn, value1)); push(interpreter.copyOperation(insn, value3)); push(interpreter.copyOperation(insn, value2)); push(interpreter.copyOperation(insn, value1)); break; } } } else if (executeDupX2(insn, value1, interpreter)) { break; } throw new AnalyzerException(insn, "Illegal use of DUP2_X2"); case Opcodes.SWAP: value2 = pop(); value1 = pop(); if (value1.getSize() != 1 || value2.getSize() != 1) { throw new AnalyzerException(insn, "Illegal use of SWAP"); } push(interpreter.copyOperation(insn, value2)); push(interpreter.copyOperation(insn, value1)); break; case Opcodes.IALOAD: case Opcodes.LALOAD: case Opcodes.FALOAD: case Opcodes.DALOAD: case Opcodes.AALOAD: case Opcodes.BALOAD: case Opcodes.CALOAD: case Opcodes.SALOAD: case Opcodes.IADD: case Opcodes.LADD: case Opcodes.FADD: case Opcodes.DADD: case Opcodes.ISUB: case Opcodes.LSUB: case Opcodes.FSUB: case Opcodes.DSUB: case Opcodes.IMUL: case Opcodes.LMUL: case Opcodes.FMUL: case Opcodes.DMUL: case Opcodes.IDIV: case Opcodes.LDIV: case Opcodes.FDIV: case Opcodes.DDIV: case Opcodes.IREM: case Opcodes.LREM: case Opcodes.FREM: case Opcodes.DREM: case Opcodes.ISHL: case Opcodes.LSHL: case Opcodes.ISHR: case Opcodes.LSHR: case Opcodes.IUSHR: case Opcodes.LUSHR: case Opcodes.IAND: case Opcodes.LAND: case Opcodes.IOR: case Opcodes.LOR: case Opcodes.IXOR: case Opcodes.LXOR: case Opcodes.LCMP: case Opcodes.FCMPL: case Opcodes.FCMPG: case Opcodes.DCMPL: case Opcodes.DCMPG: value2 = pop(); value1 = pop(); push(interpreter.binaryOperation(insn, value1, value2)); break; case Opcodes.INEG: case Opcodes.LNEG: case Opcodes.FNEG: case Opcodes.DNEG: push(interpreter.unaryOperation(insn, pop())); break; case Opcodes.IINC: varIndex = ((IincInsnNode) insn).var; setLocal(varIndex, interpreter.unaryOperation(insn, getLocal(varIndex))); break; case Opcodes.I2L: case Opcodes.I2F: case Opcodes.I2D: case Opcodes.L2I: case Opcodes.L2F: case Opcodes.L2D: case Opcodes.F2I: case Opcodes.F2L: case Opcodes.F2D: case Opcodes.D2I: case Opcodes.D2L: case Opcodes.D2F: case Opcodes.I2B: case Opcodes.I2C: case Opcodes.I2S: push(interpreter.unaryOperation(insn, pop())); break; case Opcodes.IFEQ: case Opcodes.IFNE: case Opcodes.IFLT: case Opcodes.IFGE: case Opcodes.IFGT: case Opcodes.IFLE: interpreter.unaryOperation(insn, pop()); break; case Opcodes.IF_ICMPEQ: case Opcodes.IF_ICMPNE: case Opcodes.IF_ICMPLT: case Opcodes.IF_ICMPGE: case Opcodes.IF_ICMPGT: case Opcodes.IF_ICMPLE: case Opcodes.IF_ACMPEQ: case Opcodes.IF_ACMPNE: case Opcodes.PUTFIELD: value2 = pop(); value1 = pop(); interpreter.binaryOperation(insn, value1, value2); break; case Opcodes.GOTO: break; case Opcodes.JSR: push(interpreter.newOperation(insn)); break; case Opcodes.RET: break; case Opcodes.TABLESWITCH: case Opcodes.LOOKUPSWITCH: interpreter.unaryOperation(insn, pop()); break; case Opcodes.IRETURN: case Opcodes.LRETURN: case Opcodes.FRETURN: case Opcodes.DRETURN: case Opcodes.ARETURN: value1 = pop(); interpreter.unaryOperation(insn, value1); interpreter.returnOperation(insn, value1, returnValue); break; case Opcodes.RETURN: if (returnValue != null) { throw new AnalyzerException(insn, "Incompatible return type"); } break; case Opcodes.GETSTATIC: push(interpreter.newOperation(insn)); break; case Opcodes.PUTSTATIC: interpreter.unaryOperation(insn, pop()); break; case Opcodes.GETFIELD: push(interpreter.unaryOperation(insn, pop())); break; case Opcodes.INVOKEVIRTUAL: case Opcodes.INVOKESPECIAL: case Opcodes.INVOKESTATIC: case Opcodes.INVOKEINTERFACE: executeInvokeInsn(insn, ((MethodInsnNode) insn).desc, interpreter); break; case Opcodes.INVOKEDYNAMIC: executeInvokeInsn(insn, ((InvokeDynamicInsnNode) insn).desc, interpreter); break; case Opcodes.NEW: push(interpreter.newOperation(insn)); break; case Opcodes.NEWARRAY: case Opcodes.ANEWARRAY: case Opcodes.ARRAYLENGTH: push(interpreter.unaryOperation(insn, pop())); break; case Opcodes.ATHROW: interpreter.unaryOperation(insn, pop()); break; case Opcodes.CHECKCAST: case Opcodes.INSTANCEOF: push(interpreter.unaryOperation(insn, pop())); break; case Opcodes.MONITORENTER: case Opcodes.MONITOREXIT: interpreter.unaryOperation(insn, pop()); break; case Opcodes.MULTIANEWARRAY: List valueList = new ArrayList<>(); for (int i = ((MultiANewArrayInsnNode) insn).dims; i > 0; --i) { valueList.add(0, pop()); } push(interpreter.naryOperation(insn, valueList)); break; case Opcodes.IFNULL: case Opcodes.IFNONNULL: interpreter.unaryOperation(insn, pop()); break; default: throw new AnalyzerException(insn, "Illegal opcode " + insn.getOpcode()); } } private boolean executeDupX2( final AbstractInsnNode insn, final V value1, final Interpreter interpreter) throws AnalyzerException { V value2 = pop(); if (value2.getSize() == 1) { V value3 = pop(); if (value3.getSize() == 1) { push(interpreter.copyOperation(insn, value1)); push(interpreter.copyOperation(insn, value3)); push(interpreter.copyOperation(insn, value2)); push(interpreter.copyOperation(insn, value1)); return true; } } else { push(interpreter.copyOperation(insn, value1)); push(interpreter.copyOperation(insn, value2)); push(interpreter.copyOperation(insn, value1)); return true; } return false; } private void executeInvokeInsn( final AbstractInsnNode insn, final String methodDescriptor, final Interpreter interpreter) throws AnalyzerException { ArrayList valueList = new ArrayList<>(); for (int i = Type.getArgumentTypes(methodDescriptor).length; i > 0; --i) { valueList.add(0, pop()); } if (insn.getOpcode() != Opcodes.INVOKESTATIC && insn.getOpcode() != Opcodes.INVOKEDYNAMIC) { valueList.add(0, pop()); } if (Type.getReturnType(methodDescriptor) == Type.VOID_TYPE) { interpreter.naryOperation(insn, valueList); } else { push(interpreter.naryOperation(insn, valueList)); } } /** * Merges the given frame into this frame. * * @param frame a frame. This frame is left unchanged by this method. * @param interpreter the interpreter used to merge values. * @return {@literal true} if this frame has been changed as a result of the merge operation, or * {@literal false} otherwise. * @throws AnalyzerException if the frames have incompatible sizes. */ public boolean merge(final Frame frame, final Interpreter interpreter) throws AnalyzerException { if (numStack != frame.numStack) { throw new AnalyzerException(null, "Incompatible stack heights"); } boolean changed = false; for (int i = 0; i < numLocals + numStack; ++i) { V v = interpreter.merge(values[i], frame.values[i]); if (!v.equals(values[i])) { values[i] = v; changed = true; } } return changed; } /** * Merges the given frame into this frame (case of a subroutine). The operand stacks are not * merged, and only the local variables that have not been used by the subroutine are merged. * * @param frame a frame. This frame is left unchanged by this method. * @param localsUsed the local variables that are read or written by the subroutine. The i-th * element is true if and only if the local variable at index i is read or written by the * subroutine. * @return {@literal true} if this frame has been changed as a result of the merge operation, or * {@literal false} otherwise. */ public boolean merge(final Frame frame, final boolean[] localsUsed) { boolean changed = false; for (int i = 0; i < numLocals; ++i) { if (!localsUsed[i] && !values[i].equals(frame.values[i])) { values[i] = frame.values[i]; changed = true; } } return changed; } /** * Returns a string representation of this frame. * * @return a string representation of this frame. */ @Override public String toString() { StringBuilder stringBuilder = new StringBuilder(); for (int i = 0; i < getLocals(); ++i) { stringBuilder.append(getLocal(i)); } stringBuilder.append(' '); for (int i = 0; i < getStackSize(); ++i) { stringBuilder.append(getStack(i).toString()); } return stringBuilder.toString(); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy