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

proguard.normalize.LargeStringSplitter 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
package proguard.normalize;

import proguard.classfile.ClassConstants;
import proguard.classfile.ClassPool;
import proguard.classfile.Clazz;
import proguard.classfile.Method;
import proguard.classfile.ProgramClass;
import proguard.classfile.attribute.CodeAttribute;
import proguard.classfile.attribute.visitor.AllAttributeVisitor;
import proguard.classfile.constant.Constant;
import proguard.classfile.constant.StringConstant;
import proguard.classfile.constant.visitor.ConstantVisitor;
import proguard.classfile.editor.CodeAttributeEditor;
import proguard.classfile.editor.ConstantPoolShrinker;
import proguard.classfile.editor.InstructionSequenceBuilder;
import proguard.classfile.editor.PeepholeEditor;
import proguard.classfile.instruction.ConstantInstruction;
import proguard.classfile.instruction.Instruction;
import proguard.classfile.instruction.visitor.InstructionVisitor;
import proguard.classfile.visitor.AllMethodVisitor;
import proguard.classfile.visitor.ClassVisitor;
import proguard.classfile.visitor.MultiClassVisitor;
import proguard.util.StringUtil;

/** This class visitor splits any strings longer than 65535 bytes into smaller strings. */
public class LargeStringSplitter implements ClassVisitor, InstructionVisitor, ConstantVisitor {
  private static final int MAX_STRING_SIZE = 0xffff;

  private final ClassPool programClassPool, libraryClassPool;

  private final CodeAttributeEditor codeAttributeEditor;

  private int offset;

  public LargeStringSplitter(ClassPool programClassPool, ClassPool libraryClassPool) {
    this.programClassPool = programClassPool;
    this.libraryClassPool = libraryClassPool;
    this.codeAttributeEditor = new CodeAttributeEditor();
  }

  // Implementations for ClassVisitor.

  @Override
  public void visitAnyClass(Clazz clazz) {}

  @Override
  public void visitProgramClass(ProgramClass programClass) {
    programClass.accept(
        new MultiClassVisitor(
            new AllMethodVisitor(
                new AllAttributeVisitor(new PeepholeEditor(codeAttributeEditor, this))),
            new ConstantPoolShrinker()));
  }

  // Implementations for InstructionVisitor.

  @Override
  public void visitAnyInstruction(
      Clazz clazz,
      Method method,
      CodeAttribute codeAttribute,
      int offset,
      Instruction instruction) {}

  @Override
  public void visitConstantInstruction(
      Clazz clazz,
      Method method,
      CodeAttribute codeAttribute,
      int offset,
      ConstantInstruction constantInstruction) {
    this.offset = offset;
    clazz.constantPoolEntryAccept(constantInstruction.constantIndex, this);
  }

  // Implementations for ConstantVisitor.

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

  @Override
  public void visitStringConstant(Clazz clazz, StringConstant stringConstant) {
    String fullString = stringConstant.getString(clazz);
    if (StringUtil.getModifiedUtf8Length(fullString) > MAX_STRING_SIZE) {
      InstructionSequenceBuilder ____ =
          new InstructionSequenceBuilder((ProgramClass) clazz, programClassPool, libraryClassPool);

      ____.new_(ClassConstants.NAME_JAVA_LANG_STRING_BUILDER)
          .dup()
          .invokespecial(
              ClassConstants.NAME_JAVA_LANG_STRING_BUILDER,
              ClassConstants.METHOD_NAME_INIT,
              ClassConstants.METHOD_TYPE_INIT);

      // Iterate through substrings, of at most 65535 bytes, until we reach the end of the string.
      int substringStart = 0;
      while (substringStart < fullString.length()) {
        int substringEnd = nextSubstringEnd(fullString, substringStart);

        ____.ldc(fullString.substring(substringStart, substringEnd))
            .invokevirtual(
                ClassConstants.NAME_JAVA_LANG_STRING_BUILDER,
                ClassConstants.METHOD_NAME_APPEND,
                ClassConstants.METHOD_TYPE_STRING_STRING_BUILDER);
        substringStart = substringEnd;
      }

      ____.invokevirtual(
          ClassConstants.NAME_JAVA_LANG_OBJECT,
          ClassConstants.METHOD_NAME_TOSTRING,
          ClassConstants.METHOD_TYPE_TOSTRING);
      codeAttributeEditor.replaceInstruction(offset, ____.__());
    }
  }

  /**
   * Returns the size of the largest substring of the given sequence that encodes to modified UTF-8
   * in 65535 bytes or fewer, starting at the given position.
   *
   * @see StringUtil#getModifiedUtf8Length(String)
   */
  private static int nextSubstringEnd(String fullString, int start) {
    // Highly optimized implementation inspired by Guava's Utf8.encodedLength:
    // https://github.com/google/guava/blob/8a24204a29275636b5bd30136ef289e9f6651bb1/guava/src/com/google/common/base/Utf8.java#L49-L77
    int end = start;
    int maxLength = Integer.min(fullString.length(), start + MAX_STRING_SIZE);

    // If we have pure ASCII at the start, skip past it.
    while (end < maxLength && fullString.charAt(end) < 0x80) {
      end++;
    }

    int utf8Length = end - start;

    while (end < maxLength) {
      char c = fullString.charAt(end);
      if (c < 0x800) {
        utf8Length += 1 + ((0x7f - c) >>> 31); // Branch free!
      } else {
        utf8Length += 3;
      }
      // If the new length is bigger than the maximum, exit before we extend.
      if (utf8Length > MAX_STRING_SIZE) {
        break;
      }
      end += 1;
    }
    return end;
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy