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

proguard.evaluation.Variables 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.evaluation;

import java.util.Arrays;
import proguard.classfile.TypeConstants;
import proguard.evaluation.exception.VariableEmptySlotException;
import proguard.evaluation.exception.VariableIndexOutOfBoundException;
import proguard.evaluation.exception.VariableTypeException;
import proguard.evaluation.value.DoubleValue;
import proguard.evaluation.value.FloatValue;
import proguard.evaluation.value.InstructionOffsetValue;
import proguard.evaluation.value.IntegerValue;
import proguard.evaluation.value.LongValue;
import proguard.evaluation.value.ReferenceValue;
import proguard.evaluation.value.TopValue;
import proguard.evaluation.value.Value;

/**
 * This class represents a local variable frame that contains {@link Value} instances. Values are
 * generalizations of all values that have been stored in the respective variables.
 *
 * @author Eric Lafortune
 */
public class Variables {
  private static final TopValue TOP_VALUE = new TopValue();

  protected Value[] values;
  protected int size;

  /** Creates a new Variables object with a given maximum number of variables. */
  public Variables(int size) {
    this.values = new Value[size];
    this.size = size;
  }

  /** Creates a Variables object that is a copy of the given Variables object. */
  public Variables(Variables variables) {
    // Create the values array.
    this(variables.size);

    // Copy the values.
    initialize(variables);
  }

  /** Resets this Variables object, so that it can be reused. */
  public void reset(int size) {
    // Is the values array large enough?
    if (values.length < size) {
      // Create a new one.
      values = new Value[size];
    } else {
      // Clear the old variables.
      Arrays.fill(values, 0, this.size, null);
    }

    this.size = size;
  }

  /**
   * Initializes the values of this Variables object with the values of the given Variables object.
   * The other object may have fewer values, in which case the remaining values are left unchanged.
   */
  public void initialize(Variables other) {
    if (this.size < other.size) {
      throw new IllegalArgumentException(
          "Variable frame is too small ["
              + this.size
              + "] compared to other frame ["
              + other.size
              + "]");
    }

    // Copy the values.
    System.arraycopy(other.values, 0, this.values, 0, other.size);
  }

  /**
   * Generalizes the values of this Variables object with the values of the given Variables object.
   *
   * @param clearConflictingOtherVariables specifies whether the other variables should be cleared
   *     too, in case of conflicts.
   * @return whether the generalization has made any difference.
   */
  public boolean generalize(Variables other, boolean clearConflictingOtherVariables) {
    if (this.size != other.size) {
      throw new IllegalArgumentException(
          "Variable frames have different sizes [" + this.size + "] and [" + other.size + "]");
    }

    boolean changed = false;

    for (int index = 0; index < size; index++) {
      Value thisValue = this.values[index];
      Value otherValue = other.values[index];

      // Occasionally, two values of different types might be present
      // in the same variable in a variable frame (corresponding to
      // two local variables that share the same index), at some point
      // outside of their scopes. Don't generalize the variable then,
      // but let it clear instead.
      if (thisValue != null
          && otherValue != null
          && thisValue.computationalType() == otherValue.computationalType()) {
        Value newValue = thisValue.generalize(otherValue);

        changed = changed || !thisValue.equals(newValue);

        this.values[index] = newValue;
      } else {
        changed = changed || thisValue != null;

        this.values[index] = null;

        if (clearConflictingOtherVariables) {
          other.values[index] = null;
        }
      }
    }

    return changed;
  }

  /** Returns the number of variables. */
  public int size() {
    return size;
  }

  /** Gets the Value of the variable with the given index, without disturbing it. */
  public Value getValue(int index) {
    if (index < 0 || index >= size) {
      throw new VariableIndexOutOfBoundException(index, size);
    }

    return values[index];
  }

  /** Gets the Values of the variables array, without disturbing it. */
  public Value[] getValues() {
    return values;
  }

  /** Stores the given Value at the given variable index. */
  public void store(int index, Value value) {
    if (index < 0 || index >= size) {
      throw new VariableIndexOutOfBoundException(index, size);
    }

    // Store the value.
    values[index] = value;

    // Account for the extra space required by Category 2 values.
    if (value.isCategory2()) {
      values[index + 1] = TOP_VALUE;
    }
  }

  /** Loads the Value from the variable with the given index. */
  public Value load(int index) {
    if (index < 0 || index >= size) {
      throw new VariableIndexOutOfBoundException(index, size);
    }

    return values[index];
  }

  /**
   * Loads the Value from the variable with the given index.
   *
   * 

Unlike `load`: throws a {@link VariableEmptySlotException} if the value is null. */ private Value loadInternal(int index) { if (index < 0 || index >= size) { throw new VariableIndexOutOfBoundException(index, size); } if (values[index] == null) { throw new VariableEmptySlotException(index); } return values[index]; } // Load methods that provide convenient casts to the expected value types. /** Loads the IntegerValue from the variable with the given index. */ public IntegerValue iload(int index) { Value value = loadInternal(index); try { return value.integerValue(); } catch (IllegalArgumentException e) { throw new VariableTypeException(index, value, Character.toString(TypeConstants.INT), e); } } /** Loads the LongValue from the variable with the given index. */ public LongValue lload(int index) { Value value = loadInternal(index); try { return value.longValue(); } catch (IllegalArgumentException e) { throw new VariableTypeException(index, value, Character.toString(TypeConstants.LONG), e); } } /** Loads the FloatValue from the variable with the given index. */ public FloatValue fload(int index) { Value value = loadInternal(index); try { return value.floatValue(); } catch (IllegalArgumentException e) { throw new VariableTypeException(index, value, Character.toString(TypeConstants.FLOAT), e); } } /** Loads the DoubleValue from the variable with the given index. */ public DoubleValue dload(int index) { Value value = loadInternal(index); try { return value.doubleValue(); } catch (IllegalArgumentException e) { throw new VariableTypeException(index, value, Character.toString(TypeConstants.DOUBLE), e); } } /** Loads the ReferenceValue from the variable with the given index. */ public ReferenceValue aload(int index) { Value value = loadInternal(index); try { return value.referenceValue(); } catch (IllegalArgumentException e) { throw new VariableTypeException( index, value, String.format("%cSOME_REFERENCE%c", TypeConstants.CLASS_START, TypeConstants.CLASS_END), e); } } /** Loads the InstructionOffsetValue from the variable with the given index. */ public InstructionOffsetValue oload(int index) { return load(index).instructionOffsetValue(); } /** Replaces all the references to {@param toReplace} with references to {@param replacement}. */ public void replaceReferences(Value toReplace, Value replacement) { for (int i = 0; i < values.length; i++) { if (values[i] == toReplace) { values[i] = replacement; } } } // Implementations for Object. public boolean equals(Object object) { if (object == null || this.getClass() != object.getClass()) { return false; } Variables other = (Variables) object; if (this.size != other.size) { return false; } for (int index = 0; index < size; index++) { Value thisValue = this.values[index]; Value otherValue = other.values[index]; // Occasionally, two values of different types might be // present in the same variable in a variable frame // (corresponding to two local variables that share the // same index), at some point outside of their scopes. // We'll ignore these. if (thisValue != null && otherValue != null && thisValue.computationalType() == otherValue.computationalType() && !thisValue.equals(otherValue)) { return false; } } return true; } public int hashCode() { int hashCode = size; for (int index = 0; index < size; index++) { Value value = values[index]; if (value != null) { hashCode ^= value.hashCode(); } } return hashCode; } public String toString() { StringBuffer buffer = new StringBuffer(); for (int index = 0; index < size; index++) { Value value = values[index]; buffer = buffer.append('[').append(value == null ? "empty" : value.toString()).append(']'); } return buffer.toString(); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy