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

    // The minimum variable size is determined by the arguments.
    codeAttribute.u2maxLocals =
        ClassUtil.internalMethodParameterSize(method.getDescriptor(clazz), method.getAccessFlags());

    if (DEBUG) {
      System.out.println(
          "VariableSizeUpdater: "
              + clazz.getName()
              + "."
              + method.getName(clazz)
              + method.getDescriptor(clazz));
      System.out.println("  Max locals: " + codeAttribute.u2maxLocals + " <- parameters");
    }

    // Go over all instructions.
    codeAttribute.instructionsAccept(clazz, method, this);

    // Remove the unused variables of the attributes.
    codeAttribute.attributesAccept(clazz, method, variableCleaner);
  }
  public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) {
    //        DEBUG =
    //            clazz.getName().equals("abc/Def") &&
    //            method.getName(clazz).equals("abc");

    // TODO: Remove this when the code has stabilized.
    // Catch any unexpected exceptions from the actual visiting method.
    try {
      // Process the code.
      visitCodeAttribute0(clazz, method, codeAttribute);
    } catch (RuntimeException ex) {
      System.err.println("Unexpected error while computing stack sizes:");
      System.err.println("  Class       = [" + clazz.getName() + "]");
      System.err.println(
          "  Method      = [" + method.getName(clazz) + method.getDescriptor(clazz) + "]");
      System.err.println(
          "  Exception   = [" + ex.getClass().getName() + "] (" + ex.getMessage() + ")");

      if (DEBUG) {
        method.accept(clazz, new ClassPrinter());
      }

      throw ex;
    }
  }
  public void visitCodeAttribute0(Clazz clazz, Method method, CodeAttribute codeAttribute) {
    if (DEBUG) {
      System.out.println(
          "StackSizeComputer: "
              + clazz.getName()
              + "."
              + method.getName(clazz)
              + method.getDescriptor(clazz));
    }

    // Try to reuse the previous array.
    int codeLength = codeAttribute.u4codeLength;
    if (evaluated.length < codeLength) {
      evaluated = new boolean[codeLength];
      stackSizes = new int[codeLength];
    } else {
      Arrays.fill(evaluated, 0, codeLength, false);
    }

    // The initial stack is always empty.
    stackSize = 0;
    maxStackSize = 0;

    // Evaluate the instruction block starting at the entry point of the method.
    evaluateInstructionBlock(clazz, method, codeAttribute, 0);

    // Evaluate the exception handlers.
    codeAttribute.exceptionsAccept(clazz, method, this);
  }
Example #4
0
  /**
   * Marks the hierarchy of implementing or overriding methods corresponding to the given method, if
   * any.
   */
  protected void markMethodHierarchy(Clazz clazz, Method method) {
    int accessFlags = method.getAccessFlags();
    if ((accessFlags & (ClassConstants.ACC_PRIVATE | ClassConstants.ACC_STATIC)) == 0
        && !ClassUtil.isInitializer(method.getName(clazz))) {
      // We can skip private and static methods in the hierarchy, and
      // also abstract methods, unless they might widen a current
      // non-public access.
      int requiredUnsetAccessFlags =
          ClassConstants.ACC_PRIVATE
              | ClassConstants.ACC_STATIC
              | ((accessFlags & ClassConstants.ACC_PUBLIC) == 0 ? 0 : ClassConstants.ACC_ABSTRACT);

      clazz.accept(
          new ConcreteClassDownTraveler(
              new ClassHierarchyTraveler(
                  true,
                  true,
                  false,
                  true,
                  new NamedMethodVisitor(
                      method.getName(clazz),
                      method.getDescriptor(clazz),
                      new MemberAccessFilter(0, requiredUnsetAccessFlags, this)))));
    }
  }
  /** Returns a shrunk descriptor or signature of the given method. */
  private String shrinkDescriptor(Method method, String descriptor) {
    // All parameters of non-static methods are shifted by one in the local
    // variable frame.
    int parameterIndex =
        (method.getAccessFlags() & ClassConstants.INTERNAL_ACC_STATIC) != 0 ? 0 : 1;

    // Go over the parameters.
    InternalTypeEnumeration internalTypeEnumeration = new InternalTypeEnumeration(descriptor);

    StringBuffer newDescriptorBuffer = new StringBuffer();

    newDescriptorBuffer.append(internalTypeEnumeration.formalTypeParameters());
    newDescriptorBuffer.append(ClassConstants.INTERNAL_METHOD_ARGUMENTS_OPEN);

    while (internalTypeEnumeration.hasMoreTypes()) {
      String type = internalTypeEnumeration.nextType();
      if (ParameterUsageMarker.isParameterUsed(method, parameterIndex)) {
        newDescriptorBuffer.append(type);
      } else if (DEBUG) {
        System.out.println("  Deleting parameter #" + parameterIndex + " [" + type + "]");
      }

      parameterIndex += ClassUtil.isInternalCategory2Type(type) ? 2 : 1;
    }

    newDescriptorBuffer.append(ClassConstants.INTERNAL_METHOD_ARGUMENTS_CLOSE);
    newDescriptorBuffer.append(internalTypeEnumeration.returnType());

    return newDescriptorBuffer.toString();
  }
  /** Shrinks the array of referenced classes of the given method. */
  private Clazz[] shrinkReferencedClasses(
      Method method, String descriptor, Clazz[] referencedClasses) {
    if (referencedClasses != null) {
      // All parameters of non-static methods are shifted by one in the local
      // variable frame.
      int parameterIndex =
          (method.getAccessFlags() & ClassConstants.INTERNAL_ACC_STATIC) != 0 ? 0 : 1;

      int referencedClassIndex = 0;
      int newReferencedClassIndex = 0;

      // Go over the parameters.
      InternalTypeEnumeration internalTypeEnumeration = new InternalTypeEnumeration(descriptor);

      // Also look at the formal type parameters.
      String type = internalTypeEnumeration.formalTypeParameters();
      int count = new DescriptorClassEnumeration(type).classCount();
      for (int counter = 0; counter < count; counter++) {
        referencedClasses[newReferencedClassIndex++] = referencedClasses[referencedClassIndex++];
      }

      while (internalTypeEnumeration.hasMoreTypes()) {
        // Consider the classes referenced by this parameter type.
        type = internalTypeEnumeration.nextType();
        count = new DescriptorClassEnumeration(type).classCount();

        if (ParameterUsageMarker.isParameterUsed(method, parameterIndex)) {
          // Copy the referenced classes.
          for (int counter = 0; counter < count; counter++) {
            referencedClasses[newReferencedClassIndex++] =
                referencedClasses[referencedClassIndex++];
          }
        } else {
          // Skip the referenced classes.
          referencedClassIndex += count;
        }

        parameterIndex += ClassUtil.isInternalCategory2Type(type) ? 2 : 1;
      }

      // Also look at the return value.
      type = internalTypeEnumeration.returnType();
      count = new DescriptorClassEnumeration(type).classCount();
      for (int counter = 0; counter < count; counter++) {
        referencedClasses[newReferencedClassIndex++] = referencedClasses[referencedClassIndex++];
      }

      // Clear the unused entries.
      while (newReferencedClassIndex < referencedClassIndex) {
        referencedClasses[newReferencedClassIndex++] = null;
      }
    }

    return referencedClasses;
  }
Example #7
0
 /**
  * Marks the hierarchy of implementing or overriding methods corresponding to the given method, if
  * any.
  */
 protected void markMethodHierarchy(Clazz clazz, Method method) {
   if ((method.getAccessFlags()
           & (ClassConstants.INTERNAL_ACC_PRIVATE | ClassConstants.INTERNAL_ACC_STATIC))
       == 0) {
     clazz.accept(
         new ConcreteClassDownTraveler(
             new ClassHierarchyTraveler(
                 true,
                 true,
                 false,
                 true,
                 new NamedMethodVisitor(
                     method.getName(clazz),
                     method.getDescriptor(clazz),
                     new MemberAccessFilter(
                         0,
                         ClassConstants.INTERNAL_ACC_PRIVATE
                             | ClassConstants.INTERNAL_ACC_STATIC
                             | ClassConstants.INTERNAL_ACC_ABSTRACT,
                         this)))));
   }
 }
  public void visitAnyParameterAnnotationsAttribute(
      Clazz clazz, Method method, ParameterAnnotationsAttribute parameterAnnotationsAttribute) {
    int[] annotationsCounts = parameterAnnotationsAttribute.u2parameterAnnotationsCount;
    Annotation[][] annotations = parameterAnnotationsAttribute.parameterAnnotations;

    // All parameters of non-static methods are shifted by one in the local
    // variable frame.
    int parameterIndex =
        (method.getAccessFlags() & ClassConstants.INTERNAL_ACC_STATIC) != 0 ? 0 : 1;

    int annotationIndex = 0;
    int newAnnotationIndex = 0;

    // Go over the parameters.
    String descriptor = method.getDescriptor(clazz);
    InternalTypeEnumeration internalTypeEnumeration = new InternalTypeEnumeration(descriptor);

    while (internalTypeEnumeration.hasMoreTypes()) {
      String type = internalTypeEnumeration.nextType();
      if (ParameterUsageMarker.isParameterUsed(method, parameterIndex)) {
        annotationsCounts[newAnnotationIndex] = annotationsCounts[annotationIndex];
        annotations[newAnnotationIndex++] = annotations[annotationIndex];
      }

      annotationIndex++;

      parameterIndex += ClassUtil.isInternalCategory2Type(type) ? 2 : 1;
    }

    // Update the number of parameters.
    parameterAnnotationsAttribute.u2parametersCount = newAnnotationIndex;

    // Clear the unused entries.
    while (newAnnotationIndex < annotationIndex) {
      annotationsCounts[newAnnotationIndex] = 0;
      annotations[newAnnotationIndex++] = null;
    }
  }
Example #9
0
  /**
   * Marks the hierarchy of implementing or overriding methods corresponding to the given method, if
   * any.
   */
  protected void markMethodHierarchy(Clazz clazz, Method method) {
    int accessFlags = method.getAccessFlags();
    if ((accessFlags & (ClassConstants.ACC_PRIVATE | ClassConstants.ACC_STATIC)) == 0
        && !ClassUtil.isInitializer(method.getName(clazz))) {
      // We can skip private and static methods in the hierarchy, and
      // also abstract methods, unless they might widen a current
      // non-public access.
      int requiredUnsetAccessFlags =
          ClassConstants.ACC_PRIVATE
              | ClassConstants.ACC_STATIC
              | ((accessFlags & ClassConstants.ACC_PUBLIC) == 0 ? 0 : ClassConstants.ACC_ABSTRACT);

      // Mark default implementations in interfaces down the hierarchy.
      // TODO: This may be premature if there aren't any concrete implementing classes.
      clazz.accept(
          new ClassAccessFilter(
              ClassConstants.ACC_ABSTRACT,
              0,
              new ClassHierarchyTraveler(
                  false,
                  false,
                  false,
                  true,
                  new ProgramClassFilter(
                      new ClassAccessFilter(
                          ClassConstants.ACC_ABSTRACT,
                          0,
                          new NamedMethodVisitor(
                              method.getName(clazz),
                              method.getDescriptor(clazz),
                              new MemberAccessFilter(
                                  0, requiredUnsetAccessFlags, defaultMethodUsageMarker)))))));

      // Mark other implementations.
      clazz.accept(
          new ConcreteClassDownTraveler(
              new ClassHierarchyTraveler(
                  true,
                  true,
                  false,
                  true,
                  new NamedMethodVisitor(
                      method.getName(clazz),
                      method.getDescriptor(clazz),
                      new MemberAccessFilter(0, requiredUnsetAccessFlags, this)))));
    }
  }
  /**
   * Evaluates a block of instructions that hasn't been handled before, starting at the given offset
   * and ending at a branch instruction, a return instruction, or a throw instruction. Branch
   * instructions are handled recursively.
   */
  private void evaluateInstructionBlock(
      Clazz clazz, Method method, CodeAttribute codeAttribute, int instructionOffset) {
    if (DEBUG) {
      if (evaluated[instructionOffset]) {
        System.out.println("-- (instruction block at " + instructionOffset + " already evaluated)");
      } else {
        System.out.println("-- instruction block:");
      }
    }

    // Remember the initial stack size.
    int initialStackSize = stackSize;

    // Remember the maximum stack size.
    if (maxStackSize < stackSize) {
      maxStackSize = stackSize;
    }

    // Evaluate any instructions that haven't been evaluated before.
    while (!evaluated[instructionOffset]) {
      // Mark the instruction as evaluated.
      evaluated[instructionOffset] = true;

      Instruction instruction = InstructionFactory.create(codeAttribute.code, instructionOffset);

      if (DEBUG) {
        int stackPushCount = instruction.stackPushCount(clazz);
        int stackPopCount = instruction.stackPopCount(clazz);
        System.out.println(
            "["
                + instructionOffset
                + "]: "
                + stackSize
                + " - "
                + stackPopCount
                + " + "
                + stackPushCount
                + " = "
                + (stackSize + stackPushCount - stackPopCount)
                + ": "
                + instruction.toString(instructionOffset));
      }

      // Compute the instruction's effect on the stack size.
      stackSize -= instruction.stackPopCount(clazz);

      if (stackSize < 0) {
        throw new IllegalArgumentException(
            "Stack size becomes negative after instruction "
                + instruction.toString(instructionOffset)
                + " in ["
                + clazz.getName()
                + "."
                + method.getName(clazz)
                + method.getDescriptor(clazz)
                + "]");
      }

      stackSizes[instructionOffset] = stackSize += instruction.stackPushCount(clazz);

      // Remember the maximum stack size.
      if (maxStackSize < stackSize) {
        maxStackSize = stackSize;
      }

      // Remember the next instruction offset.
      int nextInstructionOffset = instructionOffset + instruction.length(instructionOffset);

      // Visit the instruction, in order to handle branches.
      instruction.accept(clazz, method, codeAttribute, instructionOffset, this);

      // Stop evaluating after a branch.
      if (exitInstructionBlock) {
        break;
      }

      // Continue with the next instruction.
      instructionOffset = nextInstructionOffset;

      if (DEBUG) {
        if (evaluated[instructionOffset]) {
          System.out.println("-- (instruction at " + instructionOffset + " already evaluated)");
        }
      }
    }

    // Restore the stack size for possible subsequent instruction blocks.
    this.stackSize = initialStackSize;
  }
  public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) {
    //        DEBUG =
    //            clazz.getName().equals("abc/Def") &&
    //            method.getName(clazz).equals("abc");

    if (DEBUG) {
      method.accept(clazz, new ClassPrinter());
    }

    branchTargetFinder.visitCodeAttribute(clazz, method, codeAttribute);

    // Don't bother if there aren't any subroutines anyway.
    if (!containsSubroutines(codeAttribute)) {
      return;
    }

    if (DEBUG) {
      System.out.println(
          "SubroutineInliner: processing ["
              + clazz.getName()
              + "."
              + method.getName(clazz)
              + method.getDescriptor(clazz)
              + "]");
    }

    // Append the body of the code.
    codeAttributeComposer.reset();
    codeAttributeComposer.beginCodeFragment(codeAttribute.u4codeLength);

    // Copy the non-subroutine instructions.
    int offset = 0;
    while (offset < codeAttribute.u4codeLength) {
      Instruction instruction = InstructionFactory.create(codeAttribute.code, offset);
      int instructionLength = instruction.length(offset);

      // Is this returning subroutine?
      if (branchTargetFinder.isSubroutine(offset)
          && branchTargetFinder.isSubroutineReturning(offset)) {
        // Skip the subroutine.
        if (DEBUG) {
          System.out.println(
              "  Skipping original subroutine instruction " + instruction.toString(offset));
        }

        // Append a label at this offset instead.
        codeAttributeComposer.appendLabel(offset);
      } else {
        // Copy the instruction, inlining any subroutine call recursively.
        instruction.accept(clazz, method, codeAttribute, offset, this);
      }

      offset += instructionLength;
    }

    // Copy the exceptions. Note that exceptions with empty try blocks
    // are automatically removed.
    codeAttribute.exceptionsAccept(clazz, method, subroutineExceptionInliner);

    if (DEBUG) {
      System.out.println("  Appending label after code at [" + offset + "]");
    }

    // Append a label just after the code.
    codeAttributeComposer.appendLabel(codeAttribute.u4codeLength);

    // End and update the code attribute.
    codeAttributeComposer.endCodeFragment();
    codeAttributeComposer.visitCodeAttribute(clazz, method, codeAttribute);

    if (DEBUG) {
      method.accept(clazz, new ClassPrinter());
    }
  }
Example #12
0
  public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) {
    // Get the original parameter size that was saved.
    int oldParameterSize = ParameterUsageMarker.getParameterSize(method);

    // Compute the new parameter size from the shrunk descriptor.
    int newParameterSize =
        ClassUtil.internalMethodParameterSize(method.getDescriptor(clazz), method.getAccessFlags());

    if (oldParameterSize > newParameterSize) {
      // Get the total size of the local variable frame.
      int maxLocals = codeAttribute.u2maxLocals;

      if (DEBUG) {
        System.out.println(
            "ParameterShrinker: "
                + clazz.getName()
                + "."
                + method.getName(clazz)
                + method.getDescriptor(clazz));
        System.out.println("  Old parameter size = " + oldParameterSize);
        System.out.println("  New parameter size = " + newParameterSize);
        System.out.println("  Max locals         = " + maxLocals);
      }

      // Create a variable map.
      int[] variableMap = new int[maxLocals];

      // Move unused parameters right after the parameter block.
      int usedParameterIndex = 0;
      int unusedParameterIndex = newParameterSize;
      for (int parameterIndex = 0; parameterIndex < oldParameterSize; parameterIndex++) {
        // Is the variable required as a parameter?
        if (ParameterUsageMarker.isParameterUsed(method, parameterIndex)) {
          // Keep the variable as a parameter.
          variableMap[parameterIndex] = usedParameterIndex++;
        } else {
          if (DEBUG) {
            System.out.println("  Deleting parameter #" + parameterIndex);
          }

          // Shift the variable to the unused parameter block,
          // in case it is still used as a variable.
          variableMap[parameterIndex] = unusedParameterIndex++;

          // Visit the method, if required.
          if (extraVariableMemberVisitor != null) {
            method.accept(clazz, extraVariableMemberVisitor);
          }
        }
      }

      // Fill out the remainder of the map.
      for (int variableIndex = oldParameterSize; variableIndex < maxLocals; variableIndex++) {
        variableMap[variableIndex] = variableIndex;
      }

      // Set the map.
      variableRemapper.setVariableMap(variableMap);

      // Remap the variables.
      variableRemapper.visitCodeAttribute(clazz, method, codeAttribute);
    }
  }
Example #13
0
 public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) {
   if (codeAttribute.u4codeLength > 1) {
     method.accept(clazz, UsageMarker.this);
   }
 }