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

proguard.evaluation.InitializationFinder 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 proguard.classfile.*;
import proguard.classfile.attribute.*;
import proguard.classfile.attribute.visitor.AttributeVisitor;
import proguard.classfile.editor.ClassEstimates;
import proguard.classfile.instruction.InstructionFactory;
import proguard.classfile.instruction.visitor.InstructionVisitor;
import proguard.evaluation.value.*;
import proguard.util.ArrayUtil;

/**
 * This {@link AttributeVisitor} links 'new' instructions and their corresponding initializers in
 * the {@link CodeAttribute} instances that it visits.
 *
 * @author Eric Lafortune
 */
public class InitializationFinder implements AttributeVisitor, InstructionVisitor {
  // *
  private static final boolean DEBUG = false;
  /*/
  private static       boolean DEBUG = System.getProperty("if") != null;
  //*/

  public static final int NONE = -1;

  private final PartialEvaluator partialEvaluator;
  private final boolean runPartialEvaluator;

  private int superInitializationOffset;
  private int[] initializationOffsets = new int[ClassEstimates.TYPICAL_CODE_LENGTH];
  private InstructionOffsetValue[] uninitializedOffsets =
      new InstructionOffsetValue[ClassEstimates.TYPICAL_CODE_LENGTH];

  /** Creates a new InitializationFinder. */
  public InitializationFinder() {
    this(new ReferenceTracingValueFactory(new BasicValueFactory()));
  }

  /**
   * Creates a new InitializationFinder. This private constructor gets around the constraint that
   * it's not allowed to add statements before calling 'this'.
   */
  private InitializationFinder(ReferenceTracingValueFactory referenceTracingValueFactory) {
    this(
        new PartialEvaluator(
            referenceTracingValueFactory,
            new ReferenceTracingInvocationUnit(
                new BasicInvocationUnit(referenceTracingValueFactory)),
            true,
            referenceTracingValueFactory),
        true);
  }

  /**
   * Creates a new InitializationFinder that will use the given partial evaluator.
   *
   * @param partialEvaluator the evaluator to be used for the analysis.
   * @param runPartialEvaluator specifies whether to run this evaluator on every code attribute that
   *     is visited.
   */
  public InitializationFinder(PartialEvaluator partialEvaluator, boolean runPartialEvaluator) {
    this.partialEvaluator = partialEvaluator;
    this.runPartialEvaluator = runPartialEvaluator;
  }

  /**
   * Returns whether the method is an instance initializer, in the CodeAttribute that was visited
   * most recently.
   */
  public boolean isInitializer() {
    return superInitializationOffset != NONE;
  }

  /**
   * Returns the instruction offset at which this initializer is calling the "super" or "this"
   * initializer method, or NONE if it is not an initializer.
   */
  public int superInitializationOffset() {
    return superInitializationOffset;
  }

  //    /**
  //     * Returns whether the instruction at the given offset is a 'new'
  //     * instruction.
  //     */
  //    public boolean isNew(int offset)
  //    {
  //        return initializationOffsets[offset] != NONE;
  //    }
  //
  //
  //    /**
  //     * Returns the instruction offset at which the object instance that is
  //     * created at the given 'new' instruction offset is initialized, or
  //     * NONE if it is not being created.
  //     */
  //    public int initializationOffset(int creationOffset)
  //    {
  //        return initializationOffsets[creationOffset];
  //    }

  /**
   * Returns the 'new' instruction offset at which the object instance is created that is
   * initialized at the given offset.
   */
  public int creationOffset(int initializationOffset) {
    return creationOffsetValue(initializationOffset).instructionOffset(0);
  }

  /** Returns whether the specified stack entry is initialized. */
  public boolean isInitializedBefore(int offset, int stackEntryIndexBottom) {
    InstructionOffsetValue creationOffsetValue = creationOffsetValue(offset, stackEntryIndexBottom);

    return isInitializedBefore(offset, creationOffsetValue);
  }

  /** Returns whether the specified stack entry is initialized. */
  public boolean isTopInitializedBefore(int offset, int stackEntryIndexTop) {
    return isInitializedBefore(
        offset, (partialEvaluator.getStackBefore(offset).size() - 1) - stackEntryIndexTop);
  }

  /** Returns whether the given creation offset is initialized before the given offset. */
  public boolean isInitializedBefore(int offset, InstructionOffsetValue creationOffsetValue) {
    return !uninitializedOffsets[offset].contains(creationOffsetValue.instructionOffset(0));
  }

  /**
   * Returns whether the instruction at the given offset is the special invocation of an instance
   * initializer.
   */
  public boolean isInitializer(int offset) {
    return partialEvaluator.isInitializer(offset);
  }

  // Implementations for AttributeVisitor.

  public void visitAnyAttribute(Clazz clazz, Attribute attribute) {}

  public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) {
    //        DEBUG =
    //            clazz.getName().equals("abc/Def") &&
    //            method.getName(clazz).equals("abc");

    int codeLength = codeAttribute.u4codeLength;

    superInitializationOffset = NONE;

    // Make sure the global arrays are sufficiently large.
    initializationOffsets = ArrayUtil.ensureArraySize(initializationOffsets, codeLength, NONE);
    uninitializedOffsets = ArrayUtil.ensureArraySize(uninitializedOffsets, codeLength, null);

    // Evaluate the method.
    if (runPartialEvaluator) {
      partialEvaluator.visitCodeAttribute(clazz, method, codeAttribute);
    }

    // Loop over all instructions. This is sufficient, because the JVM
    // specifications don't allow uninitialized instances on the stack or
    // in variables when branching backward. JVMs without preverification
    // and the Dalvik VM do allow it in practice.
    InstructionOffsetValue currentUninitializedOffsets =
        method.getName(clazz).equals(ClassConstants.METHOD_NAME_INIT)
            ? new InstructionOffsetValue(InstructionOffsetValue.METHOD_PARAMETER)
            : InstructionOffsetValue.EMPTY_VALUE;

    for (int offset = 0; offset < codeLength; offset++) {
      if (partialEvaluator.isTraced(offset)) {
        // Exception handlers start without uninitialized instances
        // (on the stack or in variables).
        if (partialEvaluator.isExceptionHandler(offset)) {
          currentUninitializedOffsets = InstructionOffsetValue.EMPTY_VALUE;
        }

        // Check if the uninitialized creation offsets have been set
        // before (because of a forward branch).
        if (uninitializedOffsets[offset] != null) {
          // Continue using them.
          currentUninitializedOffsets = uninitializedOffsets[offset];
        } else {
          uninitializedOffsets[offset] = currentUninitializedOffsets;
        }

        // Is it a 'new' instruction?
        if (partialEvaluator.isCreation(offset)) {
          // Add its offset to the current list.
          currentUninitializedOffsets = currentUninitializedOffsets.add(offset);
        }
        // Is it an instance initialization?
        else if (partialEvaluator.isInitializer(offset)) {
          // Remove its creation offset from the current list.
          InstructionOffsetValue creationOffsetValue = creationOffsetValue(offset);

          int creationOffset = creationOffsetValue.instructionOffset(0);

          if (creationOffsetValue.isMethodParameter(0)) {
            // Remember the super initialization offset of the
            // initializer method.
            superInitializationOffset = offset;
          } else {
            // Remember the instance initialization for the 'new'
            // instruction.
            initializationOffsets[creationOffset] = offset;
          }

          currentUninitializedOffsets = currentUninitializedOffsets.remove(creationOffset);
        }

        // Propagate the uninitialized creation offsets to the forward
        // branch targets, if any.
        InstructionOffsetValue branchTargets = partialEvaluator.branchTargets(offset);

        if (branchTargets != null) {
          for (int branchIndex = 0;
              branchIndex < branchTargets.instructionOffsetCount();
              branchIndex++) {
            int branchOffset = branchTargets.instructionOffset(branchIndex);
            if (branchOffset > offset) {
              uninitializedOffsets[branchOffset] = currentUninitializedOffsets;
            }
          }

          currentUninitializedOffsets = InstructionOffsetValue.EMPTY_VALUE;
        }
      }
    }

    if (DEBUG) {
      System.out.println();
      System.out.println(
          "InitializationFinder: "
              + clazz.getName()
              + "."
              + method.getName(clazz)
              + method.getDescriptor(clazz));

      for (int offset = 0; offset < codeLength; offset++) {
        if (partialEvaluator.isInstruction(offset)) {
          System.out.println(
              (initializationOffsets[offset] >= 0 ? "i" + initializationOffsets[offset] : "   ")
                  + (uninitializedOffsets[offset] != null
                          && uninitializedOffsets[offset].instructionOffsetCount() > 0
                      ? " u" + uninitializedOffsets[offset]
                      : "    ")
                  + (isInitializer(offset) ? " " + creationOffsetValue(offset) : "    ")
                  + " "
                  + InstructionFactory.create(codeAttribute.code, offset).toString(offset));
        }
      }
    }
  }

  // Small utility methods.

  /**
   * Returns the 'new' instruction offset value (or method parameter) at which the object instance
   * is created that is initialized at the given offset.
   */
  private InstructionOffsetValue creationOffsetValue(int initializationOffset) {
    int stackEntryIndexBottom = partialEvaluator.getStackAfter(initializationOffset).size();

    return creationOffsetValue(initializationOffset, stackEntryIndexBottom);
  }

  /**
   * Returns the 'new' instruction offset value (or method parameter) of the specified stack entry.
   */
  private InstructionOffsetValue creationOffsetValue(
      int instructionOffset, int stackEntryIndexBottom) {
    // Get the reference value of the new instance.
    ReferenceValue newReferenceValue =
        partialEvaluator
            .getStackBefore(instructionOffset)
            .getBottom(stackEntryIndexBottom)
            .referenceValue();

    // It's a traced reference.
    TracedReferenceValue tracedReferenceValue = (TracedReferenceValue) newReferenceValue;

    // Get the trace value.
    return tracedReferenceValue.getTraceValue().instructionOffsetValue();
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy