/** Performs obfuscation of the given program class pool. */
  public void execute(ClassPool programClassPool, ClassPool libraryClassPool) throws IOException {
    // Check if we have at least some keep commands.
    if (configuration.keep == null
        && configuration.applyMapping == null
        && configuration.printMapping == null) {
      throw new IOException("You have to specify '-keep' options for the obfuscation step.");
    }

    // Clean up any old visitor info.
    programClassPool.classesAccept(new ClassCleaner());
    libraryClassPool.classesAccept(new ClassCleaner());

    // If the class member names have to correspond globally,
    // link all class members in all classes, otherwise
    // link all non-private methods in all class hierarchies.
    ClassVisitor memberInfoLinker =
        configuration.useUniqueClassMemberNames
            ? (ClassVisitor) new AllMemberVisitor(new MethodLinker())
            : (ClassVisitor) new BottomClassFilter(new MethodLinker());

    programClassPool.classesAccept(memberInfoLinker);
    libraryClassPool.classesAccept(memberInfoLinker);

    // Create a visitor for marking the seeds.
    NameMarker nameMarker = new NameMarker();
    ClassPoolVisitor classPoolvisitor =
        ClassSpecificationVisitorFactory.createClassPoolVisitor(
            configuration.keep, nameMarker, nameMarker, false, false, true);
    // Mark the seeds.
    programClassPool.accept(classPoolvisitor);
    libraryClassPool.accept(classPoolvisitor);

    // All library classes and library class members keep their names.
    libraryClassPool.classesAccept(nameMarker);
    libraryClassPool.classesAccept(new AllMemberVisitor(nameMarker));

    // Mark attributes that have to be kept.
    AttributeUsageMarker requiredAttributeUsageMarker = new AttributeUsageMarker();

    AttributeVisitor optionalAttributeUsageMarker =
        configuration.keepAttributes == null
            ? null
            : new AttributeNameFilter(
                new ListParser(new NameParser()).parse(configuration.keepAttributes),
                requiredAttributeUsageMarker);

    programClassPool.classesAccept(
        new AllAttributeVisitor(
            true,
            new RequiredAttributeFilter(
                requiredAttributeUsageMarker, optionalAttributeUsageMarker)));

    // Remove the attributes that can be discarded. Note that the attributes
    // may only be discarded after the seeds have been marked, since the
    // configuration may rely on annotations.
    programClassPool.classesAccept(new AttributeShrinker());

    // Apply the mapping, if one has been specified. The mapping can
    // override the names of library classes and of library class members.
    if (configuration.applyMapping != null) {
      WarningPrinter warningPrinter = new WarningPrinter(System.err, configuration.warn);

      MappingReader reader = new MappingReader(configuration.applyMapping);

      MappingProcessor keeper =
          new MultiMappingProcessor(
              new MappingProcessor[] {
                new MappingKeeper(programClassPool, warningPrinter),
                new MappingKeeper(libraryClassPool, null),
              });

      reader.pump(keeper);

      // Print out a summary of the warnings if necessary.
      int mappingWarningCount = warningPrinter.getWarningCount();
      if (mappingWarningCount > 0) {
        System.err.println(
            "Warning: there were "
                + mappingWarningCount
                + " kept classes and class members that were remapped anyway.");
        System.err.println(
            "         You should adapt your configuration or edit the mapping file.");

        if (!configuration.ignoreWarnings) {
          System.err.println("         If you are sure this remapping won't hurt,");
          System.err.println(
              "         you could try your luck using the '-ignorewarnings' option.");
          throw new IOException("Please correct the above warnings first.");
        }
      }
    }

    // Come up with new names for all classes.
    DictionaryNameFactory classNameFactory =
        configuration.classObfuscationDictionary != null
            ? new DictionaryNameFactory(configuration.classObfuscationDictionary, null)
            : null;

    DictionaryNameFactory packageNameFactory =
        configuration.packageObfuscationDictionary != null
            ? new DictionaryNameFactory(configuration.packageObfuscationDictionary, null)
            : null;

    programClassPool.classesAccept(
        new ClassObfuscator(
            programClassPool,
            classNameFactory,
            packageNameFactory,
            configuration.useMixedCaseClassNames,
            configuration.keepPackageNames,
            configuration.flattenPackageHierarchy,
            configuration.repackageClasses,
            configuration.allowAccessModification));

    // Come up with new names for all class members.
    NameFactory nameFactory = new SimpleNameFactory();

    if (configuration.obfuscationDictionary != null) {
      nameFactory = new DictionaryNameFactory(configuration.obfuscationDictionary, nameFactory);
    }

    WarningPrinter warningPrinter = new WarningPrinter(System.err, configuration.warn);

    // Maintain a map of names to avoid [descriptor - new name - old name].
    Map descriptorMap = new HashMap();

    // Do the class member names have to be globally unique?
    if (configuration.useUniqueClassMemberNames) {
      // Collect all member names in all classes.
      programClassPool.classesAccept(
          new AllMemberVisitor(
              new MemberNameCollector(configuration.overloadAggressively, descriptorMap)));

      // Assign new names to all members in all classes.
      programClassPool.classesAccept(
          new AllMemberVisitor(
              new MemberObfuscator(
                  configuration.overloadAggressively, nameFactory, descriptorMap)));
    } else {
      // Come up with new names for all non-private class members.
      programClassPool.classesAccept(
          new MultiClassVisitor(
              new ClassVisitor[] {
                // Collect all private member names in this class and down
                // the hierarchy.
                new ClassHierarchyTraveler(
                    true,
                    false,
                    false,
                    true,
                    new AllMemberVisitor(
                        new MemberAccessFilter(
                            ClassConstants.INTERNAL_ACC_PRIVATE,
                            0,
                            new MemberNameCollector(
                                configuration.overloadAggressively, descriptorMap)))),

                // Collect all non-private member names anywhere in the hierarchy.
                new ClassHierarchyTraveler(
                    true,
                    true,
                    true,
                    true,
                    new AllMemberVisitor(
                        new MemberAccessFilter(
                            0,
                            ClassConstants.INTERNAL_ACC_PRIVATE,
                            new MemberNameCollector(
                                configuration.overloadAggressively, descriptorMap)))),

                // Assign new names to all non-private members in this class.
                new AllMemberVisitor(
                    new MemberAccessFilter(
                        0,
                        ClassConstants.INTERNAL_ACC_PRIVATE,
                        new MemberObfuscator(
                            configuration.overloadAggressively, nameFactory, descriptorMap))),

                // Clear the collected names.
                new MapCleaner(descriptorMap)
              }));

      // Come up with new names for all private class members.
      programClassPool.classesAccept(
          new MultiClassVisitor(
              new ClassVisitor[] {
                // Collect all member names in this class.
                new AllMemberVisitor(
                    new MemberNameCollector(configuration.overloadAggressively, descriptorMap)),

                // Collect all non-private member names higher up the hierarchy.
                new ClassHierarchyTraveler(
                    false,
                    true,
                    true,
                    false,
                    new AllMemberVisitor(
                        new MemberAccessFilter(
                            0,
                            ClassConstants.INTERNAL_ACC_PRIVATE,
                            new MemberNameCollector(
                                configuration.overloadAggressively, descriptorMap)))),

                // Assign new names to all private members in this class.
                new AllMemberVisitor(
                    new MemberAccessFilter(
                        ClassConstants.INTERNAL_ACC_PRIVATE,
                        0,
                        new MemberObfuscator(
                            configuration.overloadAggressively, nameFactory, descriptorMap))),

                // Clear the collected names.
                new MapCleaner(descriptorMap)
              }));
    }

    // Some class members may have ended up with conflicting names.
    // Come up with new, globally unique names for them.
    NameFactory specialNameFactory = new SpecialNameFactory(new SimpleNameFactory());

    // Collect a map of special names to avoid
    // [descriptor - new name - old name].
    Map specialDescriptorMap = new HashMap();

    programClassPool.classesAccept(
        new AllMemberVisitor(
            new MemberSpecialNameFilter(
                new MemberNameCollector(
                    configuration.overloadAggressively, specialDescriptorMap))));

    libraryClassPool.classesAccept(
        new AllMemberVisitor(
            new MemberSpecialNameFilter(
                new MemberNameCollector(
                    configuration.overloadAggressively, specialDescriptorMap))));

    // Replace conflicting non-private member names with special names.
    programClassPool.classesAccept(
        new MultiClassVisitor(
            new ClassVisitor[] {
              // Collect all private member names in this class and down
              // the hierarchy.
              new ClassHierarchyTraveler(
                  true,
                  false,
                  false,
                  true,
                  new AllMemberVisitor(
                      new MemberAccessFilter(
                          ClassConstants.INTERNAL_ACC_PRIVATE,
                          0,
                          new MemberNameCollector(
                              configuration.overloadAggressively, descriptorMap)))),

              // Collect all non-private member names in this class and
              // higher up the hierarchy.
              new ClassHierarchyTraveler(
                  true,
                  true,
                  true,
                  false,
                  new AllMemberVisitor(
                      new MemberAccessFilter(
                          0,
                          ClassConstants.INTERNAL_ACC_PRIVATE,
                          new MemberNameCollector(
                              configuration.overloadAggressively, descriptorMap)))),

              // Assign new names to all conflicting non-private members
              // in this class and higher up the hierarchy.
              new ClassHierarchyTraveler(
                  true,
                  true,
                  true,
                  false,
                  new AllMemberVisitor(
                      new MemberAccessFilter(
                          0,
                          ClassConstants.INTERNAL_ACC_PRIVATE,
                          new MemberNameConflictFixer(
                              configuration.overloadAggressively,
                              descriptorMap,
                              warningPrinter,
                              new MemberObfuscator(
                                  configuration.overloadAggressively,
                                  specialNameFactory,
                                  specialDescriptorMap))))),

              // Clear the collected names.
              new MapCleaner(descriptorMap)
            }));

    // Replace conflicting private member names with special names.
    // This is only possible if those names were kept or mapped.
    programClassPool.classesAccept(
        new MultiClassVisitor(
            new ClassVisitor[] {
              // Collect all member names in this class.
              new AllMemberVisitor(
                  new MemberNameCollector(configuration.overloadAggressively, descriptorMap)),

              // Collect all non-private member names higher up the hierarchy.
              new ClassHierarchyTraveler(
                  false,
                  true,
                  true,
                  false,
                  new AllMemberVisitor(
                      new MemberAccessFilter(
                          0,
                          ClassConstants.INTERNAL_ACC_PRIVATE,
                          new MemberNameCollector(
                              configuration.overloadAggressively, descriptorMap)))),

              // Assign new names to all conflicting private members in this
              // class.
              new AllMemberVisitor(
                  new MemberAccessFilter(
                      ClassConstants.INTERNAL_ACC_PRIVATE,
                      0,
                      new MemberNameConflictFixer(
                          configuration.overloadAggressively,
                          descriptorMap,
                          warningPrinter,
                          new MemberObfuscator(
                              configuration.overloadAggressively,
                              specialNameFactory,
                              specialDescriptorMap)))),

              // Clear the collected names.
              new MapCleaner(descriptorMap)
            }));

    // Print out any warnings about member name conflicts.
    int warningCount = warningPrinter.getWarningCount();
    if (warningCount > 0) {
      System.err.println(
          "Warning: there were " + warningCount + " conflicting class member name mappings.");
      System.err.println("         Your configuration may be inconsistent.");

      if (!configuration.ignoreWarnings) {
        System.err.println("         If you are sure the conflicts are harmless,");
        System.err.println("         you could try your luck using the '-ignorewarnings' option.");
        throw new IOException("Please correct the above warnings first.");
      }
    }

    // Print out the mapping, if requested.
    if (configuration.printMapping != null) {
      PrintStream ps =
          isFile(configuration.printMapping)
              ? new PrintStream(
                  new BufferedOutputStream(new FileOutputStream(configuration.printMapping)))
              : System.out;

      // Print out items that will be removed.
      programClassPool.classesAcceptAlphabetically(new MappingPrinter(ps));

      if (ps != System.out) {
        ps.close();
      }
    }

    // Actually apply the new names.
    programClassPool.classesAccept(new ClassRenamer());
    libraryClassPool.classesAccept(new ClassRenamer());

    // Update all references to these new names.
    programClassPool.classesAccept(new ClassReferenceFixer(false));
    libraryClassPool.classesAccept(new ClassReferenceFixer(false));
    programClassPool.classesAccept(new MemberReferenceFixer());

    // Make package visible elements public or protected, if obfuscated
    // classes are being repackaged aggressively.
    if (configuration.repackageClasses != null && configuration.allowAccessModification) {
      programClassPool.classesAccept(new AllConstantVisitor(new AccessFixer()));
    }

    // Rename the source file attributes, if requested.
    if (configuration.newSourceFileAttribute != null) {
      programClassPool.classesAccept(new SourceFileRenamer(configuration.newSourceFileAttribute));
    }

    // Mark NameAndType constant pool entries that have to be kept
    // and remove the other ones.
    programClassPool.classesAccept(new NameAndTypeUsageMarker());
    programClassPool.classesAccept(new NameAndTypeShrinker());

    // Mark Utf8 constant pool entries that have to be kept
    // and remove the other ones.
    programClassPool.classesAccept(new Utf8UsageMarker());
    programClassPool.classesAccept(new Utf8Shrinker());
  }
  private static CtClass makeExpressionClass(
      Source.Staged staged, String superClassName, String memberClassName) {
    // Here we make the language class accessible!
    if (!Modifier.isPublic(staged.getLanguage().getModifiers())) {
      staged.getLanguage().setModifiers(Modifier.setPublic(staged.getLanguage().getModifiers()));
    }

    CtClass clazz;
    try {
      ClassPool cp = ClassPool.getDefault();

      CtClass superClass = cp.getCtClass(superClassName);
      clazz =
          cp.makeClass(
              superClass.getName()
                  + "$Generated"
                  + id
                  + "$"
                  + staged.getMember().getDeclaringClass().getName().replace(".", "_")
                  + "$"
                  + staged.getMember().getName().replace(".", "_"),
              superClass);
      id++;

      // Make accepted language static fields
      HashMap<CtClass, String> acceptedLanguageMap = new HashMap<>();
      for (Use.Argument a : staged.getArguments()) {
        for (CtClass l : a.getAcceptedLanguages()) {
          if (!acceptedLanguageMap.containsKey(l) && !l.equals(Util.LANGUAGE_CLASS)) {
            String lName = "acceptedLanguage" + acceptedLanguageMap.size();
            int lPersistentId = Dispatcher.addPersistent(l);
            CtField lField =
                CtField.make(
                    "private static final "
                        + CtClass.class.getName()
                        + " "
                        + lName
                        + " = ("
                        + CtClass.class.getName()
                        + ") "
                        + Util.DISPATCHER_CLASS.getName()
                        + ".removePersistent("
                        + lPersistentId
                        + ");",
                    clazz);
            clazz.addField(lField);

            acceptedLanguageMap.put(l, lName);
          }
        }
      }

      // Make language field
      String languageName = "language";
      int languagePersistentId = Dispatcher.addPersistent(staged.getLanguage());
      CtField languageField =
          CtField.make(
              "private static final "
                  + CtClass.class.getName()
                  + " "
                  + languageName
                  + " = ("
                  + CtClass.class.getName()
                  + ") "
                  + Util.DISPATCHER_CLASS.getName()
                  + ".removePersistent("
                  + languagePersistentId
                  + ");",
              clazz);
      clazz.addField(languageField);

      // Make member field
      String memberName = "member";
      int memberPersistentId = Dispatcher.addPersistent(staged.getMember());
      CtField memberField =
          CtField.make(
              "private static final "
                  + memberClassName
                  + " "
                  + memberName
                  + " = ("
                  + memberClassName
                  + ")"
                  + Util.DISPATCHER_CLASS.getName()
                  + ".removePersistent("
                  + memberPersistentId
                  + ");",
              clazz);
      clazz.addField(memberField);

      // Make constructor
      String constructorSource =
          "private constructor("
              + Util.EXPRESSION_CLASS.getName()
              + "[] arguments, "
              + Util.STATIC_INFO_CLASS.getName()
              + " staticInfo, "
              + Util.CLOSURE_HOLDER_CLASS.getName()
              + " closureHolder) {\n"
              + "    super(arguments, staticInfo, closureHolder);\n"
              + "}";
      CtConstructor constructor = CtNewConstructor.make(constructorSource, clazz);
      clazz.addConstructor(constructor);

      // Make factory-like invoke methods
      ImmutableList<Use.Argument> args = staged.getArguments();
      StringBuilder invokeSource = new StringBuilder();
      if (staged.isStrict() || Util.isCarrier(staged.getType())) {
        invokeSource.append(
            "public static " + staged.getType().getCtClass().getName() + " invoke(");
      } else if (staged.getType().isReference()) {
        invokeSource.append("public static " + Util.LOCAL_CARRIER_CLASS.getName() + " invoke(");
      } else {
        invokeSource.append("public static " + clazz.getName() + " invoke(");
      }

      if (staged.getArguments().size() > 253) {
        invokeSource.append("Object[] objectArguments, ");
        if (!staged.getStaticInfoElements().isEmpty()) {
          invokeSource.append(Util.STATIC_INFO_CLASS.getName() + " staticInfo, ");
        }
        invokeSource.append(Util.CLOSURE_HOLDER_CLASS.getName() + " closureHolder) {\n");
        for (int i = 0; i < args.size(); i++) {
          if (Util.isGlobalCarrier(args.get(i).getType())) {
            invokeSource
                .append("    ")
                .append(Util.GLOBAL_CARRIER_CLASS.getName())
                .append(" argument")
                .append(i)
                .append(" = (")
                .append(Util.GLOBAL_CARRIER_CLASS.getName())
                .append(") objectArguments[")
                .append(i)
                .append("];\n");
          } else if (Util.isLocalCarrier(args.get(i).getType())
              || (args.get(i).getType().isReference()
                  && !Util.couldBeGlobalCarrier(args.get(i).getType()))) {
            invokeSource
                .append("    ")
                .append(Util.LOCAL_CARRIER_CLASS.getName())
                .append(" argument")
                .append(i)
                .append(" = (")
                .append(Util.LOCAL_CARRIER_CLASS.getName())
                .append(") objectArguments[")
                .append(i)
                .append("];\n");
          } else if (args.get(i).getType().isReference()) {
            invokeSource
                .append("    ")
                .append(Type.OBJECT.getCtClass().getName())
                .append(" argument")
                .append(i)
                .append(" = objectArguments[")
                .append(i)
                .append("];\n");
          } else {
            invokeSource
                .append("    ")
                .append(Util.EXPRESSION_CLASS.getName())
                .append(" argument")
                .append(i)
                .append(" = (")
                .append(Util.EXPRESSION_CLASS.getName())
                .append(") objectArguments[")
                .append(i)
                .append("];\n");
          }
        }
      } else {
        for (int i = 0; i < args.size(); i++) {
          if (Util.isGlobalCarrier(args.get(i).getType())) {
            invokeSource.append(Util.GLOBAL_CARRIER_CLASS.getName()).append(" argument").append(i);
          } else if (Util.isLocalCarrier(args.get(i).getType())
              || (args.get(i).getType().isReference()
                  && !Util.couldBeGlobalCarrier(args.get(i).getType()))) {
            invokeSource.append(Util.LOCAL_CARRIER_CLASS.getName()).append(" argument").append(i);
          } else if (args.get(i).getType().isReference()) {
            invokeSource.append(Type.OBJECT.getCtClass().getName()).append(" argument").append(i);
          } else {
            invokeSource.append(Util.EXPRESSION_CLASS.getName()).append(" argument").append(i);
          }
          invokeSource.append(", ");
        }

        if (!staged.getStaticInfoElements().isEmpty()) {
          invokeSource.append(Util.STATIC_INFO_CLASS.getName() + " staticInfo, ");
        }
        invokeSource.append(Util.CLOSURE_HOLDER_CLASS.getName() + " closureHolder) {\n");
      }

      invokeSource.append(
          "    "
              + Util.EXPRESSION_CLASS.getName()
              + " payload;\n"
              + "    "
              + Util.EXPRESSION_CLASS.getName()
              + "[] arguments = new "
              + Util.EXPRESSION_CLASS.getName()
              + "["
              + args.size()
              + "];\n");
      for (int i = 0; i < args.size(); i++) {
        if (Util.isGlobalCarrier(args.get(i).getType())) {
          invokeSource.append(
              "    payload = "
                  + Util.DISPATCHER_CLASS.getName()
                  + ".unloadGlobalCarrierChecked(argument"
                  + i
                  + ");\n"
                  + "    if (!("
                  + generateLanguageAcceptCheck(
                      "payload", args.get(i).getAcceptedLanguages(), acceptedLanguageMap)
                  + ")) {\n"
                  + "        arguments["
                  + i
                  + "] = "
                  + Util.DISPATCHER_CLASS.getName()
                  + ".selfLiftGlobalCarrier(argument"
                  + i
                  + ");\n"
                  + "    } else {\n"
                  + "        arguments["
                  + i
                  + "] = payload;\n"
                  + "    }\n");
        } else if (Util.isLocalCarrier(args.get(i).getType())
            || (args.get(i).getType().isReference()
                && !Util.couldBeGlobalCarrier(args.get(i).getType()))) {
          invokeSource.append(
              "    payload = "
                  + Util.DISPATCHER_CLASS.getName()
                  + ".unloadLocalCarrierChecked(argument"
                  + i
                  + ");\n"
                  + "    if ("
                  + generateLanguageAcceptCheck(
                      "payload", args.get(i).getAcceptedLanguages(), acceptedLanguageMap)
                  + ") {\n"
                  + "        arguments["
                  + i
                  + "] = payload.getRaw();\n"
                  + "    } else {\n"
                  + "        payload.evaluate();\n"

                  // This handles the weird case when the materialized argument could be globally
                  // carried and might need unloading and checking
                  // We know that it is an ObjectValue instance
                  + "        "
                  + Util.EXPRESSION_CLASS.getName()
                  + " value = payload.asValueIfEvaluated();\n"
                  + "        Object obj = value.materializeAsObject(); \n"
                  + "        if (obj instanceof "
                  + Util.GLOBAL_CARRIER_CLASS.getName()
                  + ") {\n"
                  + "            payload = "
                  + Util.DISPATCHER_CLASS.getName()
                  + ".unloadGlobalCarrier(("
                  + Util.GLOBAL_CARRIER_CLASS.getName()
                  + ") obj);\n"
                  + "            if (payload == null || !("
                  + generateLanguageAcceptCheck(
                      "payload", args.get(i).getAcceptedLanguages(), acceptedLanguageMap)
                  + ")) {\n"
                  + "                arguments["
                  + i
                  + "] = value;\n"
                  + "            } else {\n"
                  + "                arguments["
                  + i
                  + "] = payload;\n"
                  + "            }\n"
                  + "        } else {\n"
                  + "            arguments["
                  + i
                  + "] = value;\n"
                  + "        }\n"
                  + "    }\n");
        } else if (args.get(i).getType().isReference()) {
          invokeSource.append(
              "    if (argument"
                  + i
                  + " instanceof "
                  + Util.LOCAL_CARRIER_CLASS.getName()
                  + ") {\n"
                  + "        payload = "
                  + Util.DISPATCHER_CLASS.getName()
                  + ".unloadLocalCarrierChecked(("
                  + Util.LOCAL_CARRIER_CLASS.getName()
                  + ") argument"
                  + i
                  + ");\n"
                  + "        if ("
                  + generateLanguageAcceptCheck(
                      "payload", args.get(i).getAcceptedLanguages(), acceptedLanguageMap)
                  + ") {\n"
                  + "            arguments["
                  + i
                  + "] = payload.getRaw();\n"
                  + "        } else {\n"
                  + "            payload.evaluate();\n"

                  // This handles the weird case when the materialized argument could be globally
                  // carried and might need unloading and checking
                  // We know that it is an ObjectValue instance
                  + "            "
                  + Util.EXPRESSION_CLASS.getName()
                  + " value = payload.asValueIfEvaluated();\n"
                  + "            Object obj = value.materializeAsObject(); \n"
                  + "            if (obj instanceof "
                  + Util.GLOBAL_CARRIER_CLASS.getName()
                  + ") {\n"
                  + "                payload = "
                  + Util.DISPATCHER_CLASS.getName()
                  + ".unloadGlobalCarrier(("
                  + Util.GLOBAL_CARRIER_CLASS.getName()
                  + ") obj);\n"
                  + "                if (payload == null || !("
                  + generateLanguageAcceptCheck(
                      "payload", args.get(i).getAcceptedLanguages(), acceptedLanguageMap)
                  + ")) {\n"
                  + "                    arguments["
                  + i
                  + "] = value;\n"
                  + "                } else {\n"
                  + "                    arguments["
                  + i
                  + "] = payload;\n"
                  + "                }\n"
                  + "            } else {\n"
                  + "                arguments["
                  + i
                  + "] = value;\n"
                  + "            }\n"
                  + "        }\n"
                  + "    } else if (argument"
                  + i
                  + " instanceof "
                  + Util.GLOBAL_CARRIER_CLASS.getName()
                  + ") {\n"
                  + "        payload = "
                  + Util.DISPATCHER_CLASS.getName()
                  + ".unloadGlobalCarrierChecked(("
                  + Util.GLOBAL_CARRIER_CLASS.getName()
                  + ") argument"
                  + i
                  + ");\n"
                  + "        if (!("
                  + generateLanguageAcceptCheck(
                      "payload", args.get(i).getAcceptedLanguages(), acceptedLanguageMap)
                  + ")) {\n"
                  + "            arguments["
                  + i
                  + "] = "
                  + Util.DISPATCHER_CLASS.getName()
                  + ".selfLiftGlobalCarrier(("
                  + Util.GLOBAL_CARRIER_CLASS.getName()
                  + ") argument"
                  + i
                  + ");\n"
                  + "        } else {\n"
                  + "            arguments["
                  + i
                  + "] = payload;\n"
                  + "        }\n"
                  + "    } else {\n"
                  + "        arguments["
                  + i
                  + "] = "
                  + Util.DISPATCHER_CLASS.getName()
                  + "."
                  + Util.getLiftMethodName(Type.OBJECT)
                  + "(argument"
                  + i
                  + ");\n"
                  + "    }\n");
        } else {
          invokeSource.append(
              "    if ("
                  + generateLanguageAcceptCheck(
                      "argument" + i, args.get(i).getAcceptedLanguages(), acceptedLanguageMap)
                  + ") {\n"
                  + "        arguments["
                  + i
                  + "] = argument"
                  + i
                  + generateConversionSuffix(args.get(i).getType())
                  + ".getRaw();\n"
                  + "    } else {\n"
                  + "        argument"
                  + i
                  + ".evaluate();\n"
                  + "        arguments["
                  + i
                  + "] = argument"
                  + i
                  + ".asValueIfEvaluated();\n"
                  + "    }\n");
        }
      }
      String construction;
      if (!staged.getStaticInfoElements().isEmpty()) {
        construction = "new " + clazz.getName() + "(arguments, staticInfo, closureHolder)";
      } else {
        construction = "new " + clazz.getName() + "(arguments, null, closureHolder)";
      }
      if (staged.isStrict()) {
        Type type = staged.getType();
        if (type.isReference()) {
          invokeSource.append(
              "    return ("
                  + type.getCtClass().getName()
                  + ") ("
                  + construction
                  + ").materializeAsObject();\n");
        } else if (type.equals(Type.BOOLEAN)) {
          invokeSource.append("    return (" + construction + ").materializeAsBoolean();\n");
        } else if (type.equals(Type.INT)) {
          invokeSource.append("    return (" + construction + ").materializeAsInteger();\n");
        } else if (type.equals(Type.LONG)) {
          invokeSource.append("    return (" + construction + ").materializeAsLong();\n");
        } else if (type.equals(Type.FLOAT)) {
          invokeSource.append("    return (" + construction + ").materializeAsFloat();\n");
        } else if (type.equals(Type.DOUBLE)) {
          invokeSource.append("    return (" + construction + ").materializeAsDouble();\n");
        } else if (type.equals(Type.BYTE)) {
          invokeSource.append("    return (" + construction + ").materializeAsByte();\n");
        } else if (type.equals(Type.CHAR)) {
          invokeSource.append("    return (" + construction + ").materializeAsCharacter();\n");
        } else if (type.equals(Type.SHORT)) {
          invokeSource.append("    return (" + construction + ").materializeAsShort();\n");
        } else if (type.equals(Type.VOID)) {
          invokeSource.append("    return (" + construction + ").evaluate();\n");
        }
      } else {
        if (Util.isGlobalCarrier(staged.getType())) {
          if (staged.getType().getCtClass().equals(Util.GLOBAL_CARRIER_CLASS)) {
            invokeSource.append(
                "    return new "
                    + Util.GLOBAL_CARRIER_CLASS.getName()
                    + "("
                    + construction
                    + ");\n");
          } else {
            CarrierTransformer.transformCarrierChecked(staged.getType().getCtClass());
            invokeSource.append(
                "    return new "
                    + staged.getType().getCtClass().getName()
                    + "(("
                    + Util.DISAMBIGUATION_PARAMETER_CLASS.getName()
                    + ") null, "
                    + construction
                    + ");\n");
          }
        } else if (Util.isLocalCarrier(staged.getType())) {
          if (staged.getType().getCtClass().equals(Util.LOCAL_CARRIER_CLASS)) {
            invokeSource.append(
                "    return new "
                    + Util.LOCAL_CARRIER_CLASS.getName()
                    + "("
                    + construction
                    + ");\n");
          } else {
            CarrierTransformer.transformCarrierChecked(staged.getType().getCtClass());
            invokeSource.append(
                "    return new "
                    + staged.getType().getCtClass().getName()
                    + "(("
                    + Util.DISAMBIGUATION_PARAMETER_CLASS.getName()
                    + ") null, "
                    + construction
                    + ");\n");
          }
        } else if (staged.getType().isReference()) {
          invokeSource.append(
              "    return new " + Util.LOCAL_CARRIER_CLASS.getName() + "(" + construction + ");\n");
        } else {
          invokeSource.append("    return " + construction + ";\n");
        }
      }
      invokeSource.append("}");
      CtMethod invoke = CtMethod.make(invokeSource.toString(), clazz);
      clazz.addMethod(invoke);

      // Make isomorphic hash code method
      String isomorphicHashCodeSource =
          "int isomorphicHashCode() {\n"
              + "    if (!$0.isomorphicHashCodeHasBeenCalculated) {\n"
              + "        super.isomorphicHashCode("
              + staged.getMember().hashCode()
              + ");\n"
              + "    }"
              + "    return $0.isomorphicHashCode;"
              + "}";
      CtMethod isomorphicHashCode = CtMethod.make(isomorphicHashCodeSource, clazz);
      clazz.addMethod(isomorphicHashCode);

      // Make isomorphism check method
      String isIsomorphicToSource =
          "boolean isIsomorphicTo(java.util.IdentityHashMap identityMap, "
              + Util.EXPRESSION_CLASS.getName()
              + " expression) {\n"
              + "    if (!(expression instanceof "
              + clazz.getName()
              + ")) { return false; }\n"
              + "    return super.isIsomorphicTo(identityMap, expression);\n"
              + "}";
      CtMethod isIsomorphicTo = CtMethod.make(isIsomorphicToSource, clazz);
      clazz.addMethod(isIsomorphicTo);

      // Make cache clone (with empty leaves to save memory) creation method
      String cacheCloneSource =
          Util.EXPRESSION_CLASS.getName()
              + " cacheClone(java.util.IdentityHashMap identityMap) {\n"
              + "    "
              + Util.EXPRESSION_CLASS.getName()
              + " e = ("
              + Util.EXPRESSION_CLASS.getName()
              + ") identityMap.get(this);\n"
              + "    if (e != null) {\n"
              + "        return e;\n"
              + "    } else {\n"
              + "        "
              + Util.EXPRESSION_CLASS.getName()
              + "[] clonedArguments = super.cacheCloneArguments(identityMap);\n"
              + "        if (clonedArguments != null) {\n"
              + "            "
              + clazz.getName()
              + " s = new "
              + clazz.getName()
              + "(clonedArguments, $0.staticInfo, null);\n"
              + "            s.isomorphicHashCode = $0.isomorphicHashCode();\n"
              + "            s.isomorphicHashCodeHasBeenCalculated = true;\n"
              + "            identityMap.put(this, s);\n"
              + "            return s;\n"
              + "        } else {\n"
              + "            identityMap.put(this, this);\n"
              + "            return this;\n"
              + "        }\n"
              + "    }\n"
              + "}";
      CtMethod cacheClone = CtMethod.make(cacheCloneSource, clazz);
      clazz.addMethod(cacheClone);

      // Make language acceptance check method
      String isAcceptedBySource =
          "boolean isAcceptedBy("
              + CtClass.class.getName()
              + " language) { return $0.language.equals(language); }";
      CtMethod isAcceptedBy = CtMethod.make(isAcceptedBySource, clazz);
      clazz.addMethod(isAcceptedBy);

      // Make polymorphic member retrieval method
      String getMemberSource =
          "public " + memberClassName + " getMember() { return $0." + memberName + "; }";
      CtMethod getMember = CtMethod.make(getMemberSource, clazz);
      clazz.addMethod(getMember);

      // Make evaluation method
      Class<?> closureInterface = getClosureInterface(staged.getType());
      String evaluateSource;
      if (Util.isGlobalCarrier(staged.getType()) && !staged.isStrict()) {
        evaluateSource = "public void evaluate() { throw new UnsupportedOperationException(); }";
      } else {
        evaluateSource =
            "public void evaluate() {\n"
                + "    if ($0.value != null) { return; }\n"
                + "    "
                + closureInterface.getName()
                + " closure;\n"
                + "    "
                + Util.ENVIRONMENT_CLASS.getName()
                + " environment;\n"
                + "    if ($0.closureHolder == null) {\n"
                + "        "
                + Util.CLOSURE_HOLDER_CLASS.getName()
                + " cachedClosureHolder = "
                + GlobalCache.class.getName()
                + ".getCachedClosureHolder($0);\n"
                + "        if (cachedClosureHolder == null) {\n"
                + "            "
                + Util.BINDER_CLASS.getName()
                + " binder = new "
                + Util.BINDER_CLASS.getName()
                + "($0);\n"
                + "            closure = "
                + staged.getLanguage().getName()
                + ".make"
                + closureInterface.getSimpleName()
                + "($0, binder, false);\n"
                + "            environment = new "
                + Util.ENVIRONMENT_CLASS.getName()
                + "($0, binder.getBoundCount());\n"
                + "            if (!binder.inspectionOccurred()) {\n"
                + "                "
                + GlobalCache.class.getName()
                + ".cache($0, closure, binder.getBoundCount());\n"
                + "            }\n"
                + "        } else {\n"
                + "            closure = ("
                + closureInterface.getName()
                + ") cachedClosureHolder.getClosure();\n"
                + "            environment = new "
                + Util.ENVIRONMENT_CLASS.getName()
                + "($0, cachedClosureHolder.getEnvironmentSize());\n"
                + "        }\n"
                + "    } else {\n"
                + "        closure = ("
                + closureInterface.getName()
                + ") $0.closureHolder.getClosure();\n"
                + "        if (closure == null) {\n"
                + "            "
                + Util.CLOSURE_HOLDER_CLASS.getName()
                + " cachedClosureHolder = "
                + GlobalCache.class.getName()
                + ".getCachedClosureHolder($0);\n"
                + "            if (cachedClosureHolder == null) {\n"
                + "                "
                + Util.BINDER_CLASS.getName()
                + " binder = new "
                + Util.BINDER_CLASS.getName()
                + "($0);\n"
                + "                closure = "
                + staged.getLanguage().getName()
                + ".make"
                + closureInterface.getSimpleName()
                + "($0, binder, $0.closureHolder.isPermanent());\n"
                + "                environment = new "
                + Util.ENVIRONMENT_CLASS.getName()
                + "($0, binder.getBoundCount());\n"
                + "                if (!binder.inspectionOccurred()) {\n"
                + "                    $0.closureHolder.set(closure, binder.getBoundCount());\n"
                + "                    "
                + GlobalCache.class.getName()
                + ".cache($0, closure, binder.getBoundCount());\n"
                + "                }\n"
                + "            } else {\n"
                + "                closure = ("
                + closureInterface.getName()
                + ") cachedClosureHolder.getClosure();\n"
                + "                environment = new "
                + Util.ENVIRONMENT_CLASS.getName()
                + "($0, cachedClosureHolder.getEnvironmentSize());\n"
                + "                $0.closureHolder.set(closure, cachedClosureHolder.getEnvironmentSize());\n"
                + "            }\n"
                + "        } else {\n"
                + "            environment = new "
                + Util.ENVIRONMENT_CLASS.getName()
                + "($0, $0.closureHolder.getEnvironmentSize());\n"
                + "        }\n"
                + "    }\n"
                + (staged.getType().equals(Type.VOID)
                    ? "    closure.evaluate(environment);\n"
                    : "    $0.value = "
                        + Util.DISPATCHER_CLASS.getName()
                        + "."
                        + Util.getLiftMethodName(staged.getType())
                        + "(closure.evaluate(environment));\n")
                + "}";
      }
      CtMethod evaluate = CtMethod.make(evaluateSource, clazz);
      clazz.addMethod(evaluate);

      clazz.toClass();
    } catch (CannotCompileException | NotFoundException e) {
      throw new RuntimeException(e);
    }

    return clazz;
  }
 private void createClass(String classname) throws CannotCompileException, IOException {
   ClassPool pool = ClassPool.getDefault();
   CtClass foobarClass = pool.makeClass(classname);
   foobarClass.writeFile(newDir.getAbsolutePath());
 }
示例#4
0
  /** Performs optimization of the given program class pool. */
  public boolean execute(ClassPool programClassPool, ClassPool libraryClassPool)
      throws IOException {
    // Check if we have at least some keep commands.
    if (configuration.keep == null
        && configuration.applyMapping == null
        && configuration.printMapping == null) {
      throw new IOException("You have to specify '-keep' options for the optimization step.");
    }

    // Create a matcher for filtering optimizations.
    StringMatcher filter =
        configuration.optimizations != null
            ? new ListParser(new NameParser()).parse(configuration.optimizations)
            : new ConstantMatcher(true);

    boolean classMarkingFinal = StringMatcherUtil.matchesString(filter, CLASS_MARKING_FINAL);
    boolean classUnboxingEnum = StringMatcherUtil.matchesString(filter, CLASS_UNBOXING_ENUM);
    boolean classMergingVertical = StringMatcherUtil.matchesString(filter, CLASS_MERGING_VERTICAL);
    boolean classMergingHorizontal =
        StringMatcherUtil.matchesString(filter, CLASS_MERGING_HORIZONTAL);
    boolean fieldRemovalWriteonly =
        StringMatcherUtil.matchesString(filter, FIELD_REMOVAL_WRITEONLY);
    boolean fieldMarkingPrivate = StringMatcherUtil.matchesString(filter, FIELD_MARKING_PRIVATE);
    boolean fieldPropagationValue =
        StringMatcherUtil.matchesString(filter, FIELD_PROPAGATION_VALUE);
    boolean methodMarkingPrivate = StringMatcherUtil.matchesString(filter, METHOD_MARKING_PRIVATE);
    boolean methodMarkingStatic = StringMatcherUtil.matchesString(filter, METHOD_MARKING_STATIC);
    boolean methodMarkingFinal = StringMatcherUtil.matchesString(filter, METHOD_MARKING_FINAL);
    boolean methodRemovalParameter =
        StringMatcherUtil.matchesString(filter, METHOD_REMOVAL_PARAMETER);
    boolean methodPropagationParameter =
        StringMatcherUtil.matchesString(filter, METHOD_PROPAGATION_PARAMETER);
    boolean methodPropagationReturnvalue =
        StringMatcherUtil.matchesString(filter, METHOD_PROPAGATION_RETURNVALUE);
    boolean methodInliningShort = StringMatcherUtil.matchesString(filter, METHOD_INLINING_SHORT);
    boolean methodInliningUnique = StringMatcherUtil.matchesString(filter, METHOD_INLINING_UNIQUE);
    boolean methodInliningTailrecursion =
        StringMatcherUtil.matchesString(filter, METHOD_INLINING_TAILRECURSION);
    boolean codeMerging = StringMatcherUtil.matchesString(filter, CODE_MERGING);
    boolean codeSimplificationVariable =
        StringMatcherUtil.matchesString(filter, CODE_SIMPLIFICATION_VARIABLE);
    boolean codeSimplificationArithmetic =
        StringMatcherUtil.matchesString(filter, CODE_SIMPLIFICATION_ARITHMETIC);
    boolean codeSimplificationCast =
        StringMatcherUtil.matchesString(filter, CODE_SIMPLIFICATION_CAST);
    boolean codeSimplificationField =
        StringMatcherUtil.matchesString(filter, CODE_SIMPLIFICATION_FIELD);
    boolean codeSimplificationBranch =
        StringMatcherUtil.matchesString(filter, CODE_SIMPLIFICATION_BRANCH);
    boolean codeSimplificationString =
        StringMatcherUtil.matchesString(filter, CODE_SIMPLIFICATION_STRING);
    boolean codeSimplificationAdvanced =
        StringMatcherUtil.matchesString(filter, CODE_SIMPLIFICATION_ADVANCED);
    boolean codeRemovalAdvanced = StringMatcherUtil.matchesString(filter, CODE_REMOVAL_ADVANCED);
    boolean codeRemovalSimple = StringMatcherUtil.matchesString(filter, CODE_REMOVAL_SIMPLE);
    boolean codeRemovalVariable = StringMatcherUtil.matchesString(filter, CODE_REMOVAL_VARIABLE);
    boolean codeRemovalException = StringMatcherUtil.matchesString(filter, CODE_REMOVAL_EXCEPTION);
    boolean codeAllocationVariable =
        StringMatcherUtil.matchesString(filter, CODE_ALLOCATION_VARIABLE);

    // Create counters to count the numbers of optimizations.
    ClassCounter classMarkingFinalCounter = new ClassCounter();
    ClassCounter classUnboxingEnumCounter = new ClassCounter();
    ClassCounter classMergingVerticalCounter = new ClassCounter();
    ClassCounter classMergingHorizontalCounter = new ClassCounter();
    MemberCounter fieldRemovalWriteonlyCounter = new MemberCounter();
    MemberCounter fieldMarkingPrivateCounter = new MemberCounter();
    MemberCounter fieldPropagationValueCounter = new MemberCounter();
    MemberCounter methodMarkingPrivateCounter = new MemberCounter();
    MemberCounter methodMarkingStaticCounter = new MemberCounter();
    MemberCounter methodMarkingFinalCounter = new MemberCounter();
    MemberCounter methodRemovalParameterCounter = new MemberCounter();
    MemberCounter methodPropagationParameterCounter = new MemberCounter();
    MemberCounter methodPropagationReturnvalueCounter = new MemberCounter();
    InstructionCounter methodInliningShortCounter = new InstructionCounter();
    InstructionCounter methodInliningUniqueCounter = new InstructionCounter();
    InstructionCounter methodInliningTailrecursionCounter = new InstructionCounter();
    InstructionCounter codeMergingCounter = new InstructionCounter();
    InstructionCounter codeSimplificationVariableCounter = new InstructionCounter();
    InstructionCounter codeSimplificationArithmeticCounter = new InstructionCounter();
    InstructionCounter codeSimplificationCastCounter = new InstructionCounter();
    InstructionCounter codeSimplificationFieldCounter = new InstructionCounter();
    InstructionCounter codeSimplificationBranchCounter = new InstructionCounter();
    InstructionCounter codeSimplificationStringCounter = new InstructionCounter();
    InstructionCounter codeSimplificationAdvancedCounter = new InstructionCounter();
    InstructionCounter deletedCounter = new InstructionCounter();
    InstructionCounter addedCounter = new InstructionCounter();
    MemberCounter codeRemovalVariableCounter = new MemberCounter();
    ExceptionCounter codeRemovalExceptionCounter = new ExceptionCounter();
    MemberCounter codeAllocationVariableCounter = new MemberCounter();
    MemberCounter initializerFixCounter1 = new MemberCounter();
    MemberCounter initializerFixCounter2 = new MemberCounter();

    // Some optimizations are required by other optimizations.
    codeSimplificationAdvanced =
        codeSimplificationAdvanced
            || fieldPropagationValue
            || methodPropagationParameter
            || methodPropagationReturnvalue;

    codeRemovalAdvanced =
        codeRemovalAdvanced
            || fieldRemovalWriteonly
            || methodMarkingStatic
            || methodRemovalParameter;

    codeRemovalSimple = codeRemovalSimple || codeSimplificationBranch;

    codeRemovalException = codeRemovalException || codeRemovalAdvanced || codeRemovalSimple;

    // Clean up any old visitor info.
    programClassPool.classesAccept(new ClassCleaner());
    libraryClassPool.classesAccept(new ClassCleaner());

    // Link all methods that should get the same optimization info.
    programClassPool.classesAccept(new BottomClassFilter(new MethodLinker()));
    libraryClassPool.classesAccept(new BottomClassFilter(new MethodLinker()));

    // Create a visitor for marking the seeds.
    KeepMarker keepMarker = new KeepMarker();
    ClassPoolVisitor classPoolvisitor =
        ClassSpecificationVisitorFactory.createClassPoolVisitor(
            configuration.keep, keepMarker, keepMarker, false, true, false);
    // Mark the seeds.
    programClassPool.accept(classPoolvisitor);
    libraryClassPool.accept(classPoolvisitor);

    // All library classes and library class members remain unchanged.
    libraryClassPool.classesAccept(keepMarker);
    libraryClassPool.classesAccept(new AllMemberVisitor(keepMarker));

    // We also keep all classes that are involved in .class constructs.
    // We're not looking at enum classes though, so they can be simplified.
    programClassPool.classesAccept(
        new ClassAccessFilter(
            0,
            ClassConstants.ACC_ENUM,
            new AllMethodVisitor(
                new AllAttributeVisitor(
                    new AllInstructionVisitor(new DotClassClassVisitor(keepMarker))))));

    // We also keep all classes that are accessed dynamically.
    programClassPool.classesAccept(
        new AllConstantVisitor(
            new ConstantTagFilter(
                ClassConstants.CONSTANT_String, new ReferencedClassVisitor(keepMarker))));

    // We also keep all class members that are accessed dynamically.
    programClassPool.classesAccept(
        new AllConstantVisitor(
            new ConstantTagFilter(
                ClassConstants.CONSTANT_String, new ReferencedMemberVisitor(keepMarker))));

    // We also keep all bootstrap method signatures.
    programClassPool.classesAccept(
        new ClassVersionFilter(
            ClassConstants.CLASS_VERSION_1_7,
            new AllAttributeVisitor(
                new AttributeNameFilter(
                    ClassConstants.ATTR_BootstrapMethods,
                    new AllBootstrapMethodInfoVisitor(
                        new BootstrapMethodHandleTraveler(
                            new MethodrefTraveler(new ReferencedMemberVisitor(keepMarker))))))));

    // We also keep all bootstrap method arguments that point to methods.
    // These arguments are typically the method handles for
    // java.lang.invoke.LambdaMetafactory#metafactory, which provides the
    // implementations for closures.
    programClassPool.classesAccept(
        new ClassVersionFilter(
            ClassConstants.CLASS_VERSION_1_7,
            new AllAttributeVisitor(
                new AttributeNameFilter(
                    ClassConstants.ATTR_BootstrapMethods,
                    new AllBootstrapMethodInfoVisitor(
                        new BootstrapMethodArgumentVisitor(
                            new MethodrefTraveler(new ReferencedMemberVisitor(keepMarker))))))));

    // We also keep all classes (and their methods) returned by dynamic
    // method invocations. They may return dynamic implementations of
    // interfaces that otherwise appear unused.
    programClassPool.classesAccept(
        new ClassVersionFilter(
            ClassConstants.CLASS_VERSION_1_7,
            new AllConstantVisitor(
                new DynamicReturnedClassVisitor(
                    new MultiClassVisitor(
                        new ClassVisitor[] {keepMarker, new AllMemberVisitor(keepMarker)})))));

    // Attach some optimization info to all classes and class members, so
    // it can be filled out later.
    programClassPool.classesAccept(new ClassOptimizationInfoSetter());

    programClassPool.classesAccept(new AllMemberVisitor(new MemberOptimizationInfoSetter()));

    if (configuration.assumeNoSideEffects != null) {
      // Create a visitor for marking methods that don't have any side effects.
      NoSideEffectMethodMarker noSideEffectMethodMarker = new NoSideEffectMethodMarker();
      ClassPoolVisitor noClassPoolvisitor =
          ClassSpecificationVisitorFactory.createClassPoolVisitor(
              configuration.assumeNoSideEffects, null, noSideEffectMethodMarker);

      // Mark the seeds.
      programClassPool.accept(noClassPoolvisitor);
      libraryClassPool.accept(noClassPoolvisitor);
    }

    if (classMarkingFinal) {
      // Make classes final, whereever possible.
      programClassPool.classesAccept(new ClassFinalizer(classMarkingFinalCounter));
    }

    if (methodMarkingFinal) {
      // Make methods final, whereever possible.
      programClassPool.classesAccept(
          new ClassAccessFilter(
              0,
              ClassConstants.ACC_INTERFACE,
              new AllMethodVisitor(new MethodFinalizer(methodMarkingFinalCounter))));
    }

    if (fieldRemovalWriteonly) {
      // Mark all fields that are write-only.
      programClassPool.classesAccept(
          new AllMethodVisitor(
              new AllAttributeVisitor(new AllInstructionVisitor(new ReadWriteFieldMarker()))));

      // Count the write-only fields.
      programClassPool.classesAccept(
          new AllFieldVisitor(new WriteOnlyFieldFilter(fieldRemovalWriteonlyCounter)));
    } else {
      // Mark all fields as read/write.
      programClassPool.classesAccept(new AllFieldVisitor(new ReadWriteFieldMarker()));
    }

    if (classUnboxingEnum) {
      ClassCounter counter = new ClassCounter();

      // Mark all final enums that qualify as simple enums.
      programClassPool.classesAccept(
          new ClassAccessFilter(
              ClassConstants.ACC_FINAL | ClassConstants.ACC_ENUM, 0, new SimpleEnumClassChecker()));

      // Count the preliminary number of simple enums.
      programClassPool.classesAccept(new SimpleEnumFilter(counter));

      // Only continue checking simple enums if there are any candidates.
      if (counter.getCount() > 0) {
        // Unmark all simple enums that are explicitly used as objects.
        programClassPool.classesAccept(new SimpleEnumUseChecker());

        // Count the definitive number of simple enums.
        programClassPool.classesAccept(new SimpleEnumFilter(classUnboxingEnumCounter));

        // Only start handling simple enums if there are any.
        if (classUnboxingEnumCounter.getCount() > 0) {
          // Simplify the use of the enum classes in code.
          programClassPool.classesAccept(
              new AllMethodVisitor(new AllAttributeVisitor(new SimpleEnumUseSimplifier())));

          // Simplify the static initializers of simple enum classes.
          programClassPool.classesAccept(new SimpleEnumFilter(new SimpleEnumClassSimplifier()));

          // Simplify the use of the enum classes in descriptors.
          programClassPool.classesAccept(new SimpleEnumDescriptorSimplifier());

          // Update references to class members with simple enum classes.
          programClassPool.classesAccept(new MemberReferenceFixer());
        }
      }
    }

    // Mark all used parameters, including the 'this' parameters.
    programClassPool.classesAccept(
        new AllMethodVisitor(
            new OptimizationInfoMemberFilter(
                new ParameterUsageMarker(!methodMarkingStatic, !methodRemovalParameter))));

    // Mark all classes that have static initializers.
    programClassPool.classesAccept(new StaticInitializerContainingClassMarker());

    // Mark all methods that have side effects.
    programClassPool.accept(new SideEffectMethodMarker());

    //        System.out.println("Optimizer.execute: before evaluation simplification");
    //        programClassPool.classAccept("abc/Def", new NamedMethodVisitor("abc", null, new
    // ClassPrinter()));

    // Perform partial evaluation for filling out fields, method parameters,
    // and method return values, so they can be propagated.
    if (fieldPropagationValue || methodPropagationParameter || methodPropagationReturnvalue) {
      // We'll create values to be stored with fields, method parameters,
      // and return values.
      ValueFactory valueFactory = new ParticularValueFactory();
      ValueFactory detailedValueFactory = new DetailedValueFactory();

      InvocationUnit storingInvocationUnit =
          new StoringInvocationUnit(
              valueFactory,
              fieldPropagationValue,
              methodPropagationParameter,
              methodPropagationReturnvalue);

      // Evaluate synthetic classes in more detail, notably to propagate
      // the arrays of the classes generated for enum switch statements.
      programClassPool.classesAccept(
          new ClassAccessFilter(
              ClassConstants.ACC_SYNTHETIC,
              0,
              new AllMethodVisitor(
                  new AllAttributeVisitor(
                      new PartialEvaluator(detailedValueFactory, storingInvocationUnit, false)))));

      // Evaluate non-synthetic classes.
      programClassPool.classesAccept(
          new ClassAccessFilter(
              0,
              ClassConstants.ACC_SYNTHETIC,
              new AllMethodVisitor(
                  new AllAttributeVisitor(
                      new PartialEvaluator(valueFactory, storingInvocationUnit, false)))));

      if (fieldPropagationValue) {
        // Count the constant fields.
        programClassPool.classesAccept(
            new AllFieldVisitor(new ConstantMemberFilter(fieldPropagationValueCounter)));
      }

      if (methodPropagationParameter) {
        // Count the constant method parameters.
        programClassPool.classesAccept(
            new AllMethodVisitor(new ConstantParameterFilter(methodPropagationParameterCounter)));
      }

      if (methodPropagationReturnvalue) {
        // Count the constant method return values.
        programClassPool.classesAccept(
            new AllMethodVisitor(new ConstantMemberFilter(methodPropagationReturnvalueCounter)));
      }

      if (classUnboxingEnumCounter.getCount() > 0) {
        // Propagate the simple enum constant counts.
        programClassPool.classesAccept(new SimpleEnumFilter(new SimpleEnumArrayPropagator()));
      }

      if (codeSimplificationAdvanced) {
        // Fill out constants into the arrays of synthetic classes,
        // notably the arrays of the classes generated for enum switch
        // statements.
        InvocationUnit loadingInvocationUnit =
            new LoadingInvocationUnit(
                valueFactory,
                fieldPropagationValue,
                methodPropagationParameter,
                methodPropagationReturnvalue);

        programClassPool.classesAccept(
            new ClassAccessFilter(
                ClassConstants.ACC_SYNTHETIC,
                0,
                new AllMethodVisitor(
                    new AllAttributeVisitor(
                        new PartialEvaluator(valueFactory, loadingInvocationUnit, false)))));
      }
    }

    // Perform partial evaluation again, now loading any previously stored
    // values for fields, method parameters, and method return values.
    ValueFactory valueFactory = new IdentifiedValueFactory();

    InvocationUnit loadingInvocationUnit =
        new LoadingInvocationUnit(
            valueFactory,
            fieldPropagationValue,
            methodPropagationParameter,
            methodPropagationReturnvalue);

    if (codeSimplificationAdvanced) {
      // Simplify based on partial evaluation, propagating constant
      // field values, method parameter values, and return values.
      programClassPool.classesAccept(
          new AllMethodVisitor(
              new AllAttributeVisitor(
                  new EvaluationSimplifier(
                      new PartialEvaluator(valueFactory, loadingInvocationUnit, false),
                      codeSimplificationAdvancedCounter))));
    }

    if (codeRemovalAdvanced) {
      // Remove code based on partial evaluation, also removing unused
      // parameters from method invocations, and making methods static
      // if possible.
      programClassPool.classesAccept(
          new AllMethodVisitor(
              new AllAttributeVisitor(
                  new EvaluationShrinker(
                      new PartialEvaluator(
                          valueFactory, loadingInvocationUnit, !codeSimplificationAdvanced),
                      deletedCounter,
                      addedCounter))));
    }

    if (methodRemovalParameter) {
      // Shrink the parameters in the method descriptors.
      programClassPool.classesAccept(
          new AllMethodVisitor(new OptimizationInfoMemberFilter(new MethodDescriptorShrinker())));
    }

    if (methodMarkingStatic) {
      // Make all non-static methods that don't require the 'this'
      // parameter static.
      programClassPool.classesAccept(
          new AllMethodVisitor(
              new OptimizationInfoMemberFilter(
                  new MemberAccessFilter(
                      0,
                      ClassConstants.ACC_STATIC,
                      new MethodStaticizer(methodMarkingStaticCounter)))));
    }

    if (methodRemovalParameter) {
      // Fix all references to class members.
      // This operation also updates the stack sizes.
      programClassPool.classesAccept(new MemberReferenceFixer());

      // Remove unused bootstrap method arguments.
      programClassPool.classesAccept(
          new AllAttributeVisitor(
              new AllBootstrapMethodInfoVisitor(new BootstrapMethodArgumentShrinker())));
    }

    if (methodRemovalParameter || methodMarkingPrivate || methodMarkingStatic) {
      // Remove all unused parameters from the byte code, shifting all
      // remaining variables.
      // This operation also updates the local variable frame sizes.
      programClassPool.classesAccept(
          new AllMethodVisitor(
              new AllAttributeVisitor(new ParameterShrinker(methodRemovalParameterCounter))));
    } else if (codeRemovalAdvanced) {
      // Just update the local variable frame sizes.
      programClassPool.classesAccept(
          new AllMethodVisitor(new AllAttributeVisitor(new StackSizeUpdater())));
    }

    if (methodRemovalParameter && methodRemovalParameterCounter.getCount() > 0) {
      // Tweak the descriptors of duplicate initializers, due to removed
      // method parameters.
      programClassPool.classesAccept(
          new AllMethodVisitor(new DuplicateInitializerFixer(initializerFixCounter1)));

      if (initializerFixCounter1.getCount() > 0) {
        // Fix all invocations of tweaked initializers.
        programClassPool.classesAccept(
            new AllMethodVisitor(
                new AllAttributeVisitor(new DuplicateInitializerInvocationFixer(addedCounter))));

        // Fix all references to tweaked initializers.
        programClassPool.classesAccept(new MemberReferenceFixer());
      }
    }

    //// Specializing the class member descriptors seems to increase the
    //// class file size, on average.
    //// Specialize all class member descriptors.
    // programClassPool.classesAccept(new AllMemberVisitor(
    //                               new OptimizationInfoMemberFilter(
    //                               new MemberDescriptorSpecializer())));
    //
    //// Fix all references to classes, for MemberDescriptorSpecializer.
    // programClassPool.classesAccept(new AllMemberVisitor(
    //                               new OptimizationInfoMemberFilter(
    //                               new ClassReferenceFixer(true))));

    // Mark all classes with package visible members.
    // Mark all exception catches of methods.
    // Count all method invocations.
    // Mark super invocations and other access of methods.
    programClassPool.classesAccept(
        new MultiClassVisitor(
            new ClassVisitor[] {
              new PackageVisibleMemberContainingClassMarker(),
              new AllConstantVisitor(new PackageVisibleMemberInvokingClassMarker()),
              new AllMethodVisitor(
                  new MultiMemberVisitor(
                      new MemberVisitor[] {
                        new AllAttributeVisitor(
                            new MultiAttributeVisitor(
                                new AttributeVisitor[] {
                                  new CatchExceptionMarker(),
                                  new AllInstructionVisitor(
                                      new MultiInstructionVisitor(
                                          new InstructionVisitor[] {
                                            new InstantiationClassMarker(),
                                            new InstanceofClassMarker(),
                                            new DotClassMarker(),
                                            new MethodInvocationMarker(),
                                            new SuperInvocationMarker(),
                                            new DynamicInvocationMarker(),
                                            new BackwardBranchMarker(),
                                            new AccessMethodMarker(),
                                          })),
                                  new AllExceptionInfoVisitor(
                                      new ExceptionHandlerConstantVisitor(
                                          new ReferencedClassVisitor(new CaughtClassMarker()))),
                                })),
                      })),
            }));

    if (classMergingVertical) {
      // Merge subclasses up into their superclasses or
      // merge interfaces down into their implementing classes.
      programClassPool.classesAccept(
          new VerticalClassMerger(
              configuration.allowAccessModification,
              configuration.mergeInterfacesAggressively,
              classMergingVerticalCounter));
    }

    if (classMergingHorizontal) {
      // Merge classes into their sibling classes.
      programClassPool.classesAccept(
          new HorizontalClassMerger(
              configuration.allowAccessModification,
              configuration.mergeInterfacesAggressively,
              classMergingHorizontalCounter));
    }

    if (classMergingVerticalCounter.getCount() > 0
        || classMergingHorizontalCounter.getCount() > 0) {
      // Clean up inner class attributes to avoid loops.
      programClassPool.classesAccept(new RetargetedInnerClassAttributeRemover());

      // Update references to merged classes.
      programClassPool.classesAccept(new TargetClassChanger());
      programClassPool.classesAccept(new ClassReferenceFixer(true));
      programClassPool.classesAccept(new MemberReferenceFixer());

      if (configuration.allowAccessModification) {
        // Fix the access flags of referenced merged classes and their
        // class members.
        programClassPool.classesAccept(new AccessFixer());
      }

      // Fix the access flags of the inner classes information.
      programClassPool.classesAccept(
          new AllAttributeVisitor(new AllInnerClassesInfoVisitor(new InnerClassesAccessFixer())));

      // Tweak the descriptors of duplicate initializers, due to merged
      // parameter classes.
      programClassPool.classesAccept(
          new AllMethodVisitor(new DuplicateInitializerFixer(initializerFixCounter2)));

      if (initializerFixCounter2.getCount() > 0) {
        // Fix all invocations of tweaked initializers.
        programClassPool.classesAccept(
            new AllMethodVisitor(
                new AllAttributeVisitor(new DuplicateInitializerInvocationFixer(addedCounter))));

        // Fix all references to tweaked initializers.
        programClassPool.classesAccept(new MemberReferenceFixer());
      }
    }

    if (methodInliningUnique) {
      // Inline methods that are only invoked once.
      programClassPool.classesAccept(
          new AllMethodVisitor(
              new AllAttributeVisitor(
                  new MethodInliner(
                      configuration.microEdition,
                      configuration.allowAccessModification,
                      true,
                      methodInliningUniqueCounter))));
    }

    if (methodInliningShort) {
      // Inline short methods.
      programClassPool.classesAccept(
          new AllMethodVisitor(
              new AllAttributeVisitor(
                  new MethodInliner(
                      configuration.microEdition,
                      configuration.allowAccessModification,
                      false,
                      methodInliningShortCounter))));
    }

    if (methodInliningTailrecursion) {
      // Simplify tail recursion calls.
      programClassPool.classesAccept(
          new AllMethodVisitor(
              new AllAttributeVisitor(
                  new TailRecursionSimplifier(methodInliningTailrecursionCounter))));
    }

    if (fieldMarkingPrivate || methodMarkingPrivate) {
      // Mark all class members that can not be made private.
      programClassPool.classesAccept(new NonPrivateMemberMarker());
    }

    if (fieldMarkingPrivate) {
      // Make all non-private fields private, whereever possible.
      programClassPool.classesAccept(
          new ClassAccessFilter(
              0,
              ClassConstants.ACC_INTERFACE,
              new AllFieldVisitor(
                  new MemberAccessFilter(
                      0,
                      ClassConstants.ACC_PRIVATE,
                      new MemberPrivatizer(fieldMarkingPrivateCounter)))));
    }

    if (methodMarkingPrivate) {
      // Make all non-private methods private, whereever possible.
      programClassPool.classesAccept(
          new ClassAccessFilter(
              0,
              ClassConstants.ACC_INTERFACE,
              new AllMethodVisitor(
                  new MemberAccessFilter(
                      0,
                      ClassConstants.ACC_PRIVATE,
                      new MemberPrivatizer(methodMarkingPrivateCounter)))));
    }

    if ((methodInliningUniqueCounter.getCount() > 0
            || methodInliningShortCounter.getCount() > 0
            || methodInliningTailrecursionCounter.getCount() > 0)
        && configuration.allowAccessModification) {
      // Fix the access flags of referenced classes and class members,
      // for MethodInliner.
      programClassPool.classesAccept(new AccessFixer());
    }

    if (methodRemovalParameterCounter.getCount() > 0
        || classMergingVerticalCounter.getCount() > 0
        || classMergingHorizontalCounter.getCount() > 0
        || methodMarkingPrivateCounter.getCount() > 0) {
      // Fix invocations of interface methods, of methods that have become
      // non-abstract or private, and of methods that have moved to a
      // different package.
      programClassPool.classesAccept(
          new AllMemberVisitor(new AllAttributeVisitor(new MethodInvocationFixer())));
    }

    if (codeMerging) {
      // Share common blocks of code at branches.
      programClassPool.classesAccept(
          new AllMethodVisitor(
              new AllAttributeVisitor(new GotoCommonCodeReplacer(codeMergingCounter))));
    }

    // Create a branch target marker and a code attribute editor that can
    // be reused for all code attributes.
    BranchTargetFinder branchTargetFinder = new BranchTargetFinder();
    CodeAttributeEditor codeAttributeEditor = new CodeAttributeEditor();

    List peepholeOptimizations = new ArrayList();
    if (codeSimplificationVariable) {
      // Peephole optimizations involving local variables.
      peepholeOptimizations.add(
          new InstructionSequencesReplacer(
              InstructionSequenceConstants.CONSTANTS,
              InstructionSequenceConstants.VARIABLE,
              branchTargetFinder,
              codeAttributeEditor,
              codeSimplificationVariableCounter));
    }

    if (codeSimplificationArithmetic) {
      // Peephole optimizations involving arithmetic operations.
      peepholeOptimizations.add(
          new InstructionSequencesReplacer(
              InstructionSequenceConstants.CONSTANTS,
              InstructionSequenceConstants.ARITHMETIC,
              branchTargetFinder,
              codeAttributeEditor,
              codeSimplificationArithmeticCounter));
    }

    if (codeSimplificationCast) {
      // Peephole optimizations involving cast operations.
      peepholeOptimizations.add(
          new InstructionSequencesReplacer(
              InstructionSequenceConstants.CONSTANTS,
              InstructionSequenceConstants.CAST,
              branchTargetFinder,
              codeAttributeEditor,
              codeSimplificationCastCounter));
    }

    if (codeSimplificationField) {
      // Peephole optimizations involving fields.
      peepholeOptimizations.add(
          new InstructionSequencesReplacer(
              InstructionSequenceConstants.CONSTANTS,
              InstructionSequenceConstants.FIELD,
              branchTargetFinder,
              codeAttributeEditor,
              codeSimplificationFieldCounter));
    }

    if (codeSimplificationBranch) {
      // Peephole optimizations involving branches.
      peepholeOptimizations.add(
          new InstructionSequencesReplacer(
              InstructionSequenceConstants.CONSTANTS,
              InstructionSequenceConstants.BRANCH,
              branchTargetFinder,
              codeAttributeEditor,
              codeSimplificationBranchCounter));

      // Include optimization of branches to branches and returns.
      peepholeOptimizations.add(
          new GotoGotoReplacer(codeAttributeEditor, codeSimplificationBranchCounter));
      peepholeOptimizations.add(
          new GotoReturnReplacer(codeAttributeEditor, codeSimplificationBranchCounter));
    }

    if (codeSimplificationString) {
      // Peephole optimizations involving branches.
      peepholeOptimizations.add(
          new InstructionSequencesReplacer(
              InstructionSequenceConstants.CONSTANTS,
              InstructionSequenceConstants.STRING,
              branchTargetFinder,
              codeAttributeEditor,
              codeSimplificationStringCounter));
    }

    if (!peepholeOptimizations.isEmpty()) {
      // Convert the list into an array.
      InstructionVisitor[] peepholeOptimizationsArray =
          new InstructionVisitor[peepholeOptimizations.size()];
      peepholeOptimizations.toArray(peepholeOptimizationsArray);

      // Perform the peephole optimisations.
      programClassPool.classesAccept(
          new AllMethodVisitor(
              new AllAttributeVisitor(
                  new PeepholeOptimizer(
                      branchTargetFinder,
                      codeAttributeEditor,
                      new MultiInstructionVisitor(peepholeOptimizationsArray)))));
    }

    if (codeRemovalException) {
      // Remove unnecessary exception handlers.
      programClassPool.classesAccept(
          new AllMethodVisitor(
              new AllAttributeVisitor(
                  new UnreachableExceptionRemover(codeRemovalExceptionCounter))));
    }

    if (codeRemovalSimple) {
      // Remove unreachable code.
      programClassPool.classesAccept(
          new AllMethodVisitor(
              new AllAttributeVisitor(new UnreachableCodeRemover(deletedCounter))));
    }

    if (codeRemovalVariable) {
      // Remove all unused local variables.
      programClassPool.classesAccept(
          new AllMethodVisitor(
              new AllAttributeVisitor(new VariableShrinker(codeRemovalVariableCounter))));
    }

    if (codeAllocationVariable) {
      // Optimize the variables.
      programClassPool.classesAccept(
          new AllMethodVisitor(
              new AllAttributeVisitor(
                  new VariableOptimizer(false, codeAllocationVariableCounter))));
    }

    // Remove unused constants.
    programClassPool.classesAccept(new ConstantPoolShrinker());

    int classMarkingFinalCount = classMarkingFinalCounter.getCount();
    int classUnboxingEnumCount = classUnboxingEnumCounter.getCount();
    int classMergingVerticalCount = classMergingVerticalCounter.getCount();
    int classMergingHorizontalCount = classMergingHorizontalCounter.getCount();
    int fieldRemovalWriteonlyCount = fieldRemovalWriteonlyCounter.getCount();
    int fieldMarkingPrivateCount = fieldMarkingPrivateCounter.getCount();
    int fieldPropagationValueCount = fieldPropagationValueCounter.getCount();
    int methodMarkingPrivateCount = methodMarkingPrivateCounter.getCount();
    int methodMarkingStaticCount = methodMarkingStaticCounter.getCount();
    int methodMarkingFinalCount = methodMarkingFinalCounter.getCount();
    int methodRemovalParameterCount =
        methodRemovalParameterCounter.getCount()
            - methodMarkingStaticCounter.getCount()
            - initializerFixCounter1.getCount()
            - initializerFixCounter2.getCount();
    int methodPropagationParameterCount = methodPropagationParameterCounter.getCount();
    int methodPropagationReturnvalueCount = methodPropagationReturnvalueCounter.getCount();
    int methodInliningShortCount = methodInliningShortCounter.getCount();
    int methodInliningUniqueCount = methodInliningUniqueCounter.getCount();
    int methodInliningTailrecursionCount = methodInliningTailrecursionCounter.getCount();
    int codeMergingCount = codeMergingCounter.getCount();
    int codeSimplificationVariableCount = codeSimplificationVariableCounter.getCount();
    int codeSimplificationArithmeticCount = codeSimplificationArithmeticCounter.getCount();
    int codeSimplificationCastCount = codeSimplificationCastCounter.getCount();
    int codeSimplificationFieldCount = codeSimplificationFieldCounter.getCount();
    int codeSimplificationBranchCount = codeSimplificationBranchCounter.getCount();
    int codeSimplificationStringCount = codeSimplificationStringCounter.getCount();
    int codeSimplificationAdvancedCount = codeSimplificationAdvancedCounter.getCount();
    int codeRemovalCount = deletedCounter.getCount() - addedCounter.getCount();
    int codeRemovalVariableCount = codeRemovalVariableCounter.getCount();
    int codeRemovalExceptionCount = codeRemovalExceptionCounter.getCount();
    int codeAllocationVariableCount = codeAllocationVariableCounter.getCount();

    // Forget about constant fields, parameters, and return values, if they
    // didn't lead to any useful optimizations. We want to avoid fruitless
    // additional optimization passes.
    if (codeSimplificationAdvancedCount == 0) {
      fieldPropagationValueCount = 0;
      methodPropagationParameterCount = 0;
      methodPropagationReturnvalueCount = 0;
    }

    if (configuration.verbose) {
      System.out.println(
          "  Number of finalized classes:                 "
              + classMarkingFinalCount
              + disabled(classMarkingFinal));
      System.out.println(
          "  Number of unboxed enum classes:              "
              + classUnboxingEnumCount
              + disabled(classUnboxingEnum));
      System.out.println(
          "  Number of vertically merged classes:         "
              + classMergingVerticalCount
              + disabled(classMergingVertical));
      System.out.println(
          "  Number of horizontally merged classes:       "
              + classMergingHorizontalCount
              + disabled(classMergingHorizontal));
      System.out.println(
          "  Number of removed write-only fields:         "
              + fieldRemovalWriteonlyCount
              + disabled(fieldRemovalWriteonly));
      System.out.println(
          "  Number of privatized fields:                 "
              + fieldMarkingPrivateCount
              + disabled(fieldMarkingPrivate));
      System.out.println(
          "  Number of inlined constant fields:           "
              + fieldPropagationValueCount
              + disabled(fieldPropagationValue));
      System.out.println(
          "  Number of privatized methods:                "
              + methodMarkingPrivateCount
              + disabled(methodMarkingPrivate));
      System.out.println(
          "  Number of staticized methods:                "
              + methodMarkingStaticCount
              + disabled(methodMarkingStatic));
      System.out.println(
          "  Number of finalized methods:                 "
              + methodMarkingFinalCount
              + disabled(methodMarkingFinal));
      System.out.println(
          "  Number of removed method parameters:         "
              + methodRemovalParameterCount
              + disabled(methodRemovalParameter));
      System.out.println(
          "  Number of inlined constant parameters:       "
              + methodPropagationParameterCount
              + disabled(methodPropagationParameter));
      System.out.println(
          "  Number of inlined constant return values:    "
              + methodPropagationReturnvalueCount
              + disabled(methodPropagationReturnvalue));
      System.out.println(
          "  Number of inlined short method calls:        "
              + methodInliningShortCount
              + disabled(methodInliningShort));
      System.out.println(
          "  Number of inlined unique method calls:       "
              + methodInliningUniqueCount
              + disabled(methodInliningUnique));
      System.out.println(
          "  Number of inlined tail recursion calls:      "
              + methodInliningTailrecursionCount
              + disabled(methodInliningTailrecursion));
      System.out.println(
          "  Number of merged code blocks:                "
              + codeMergingCount
              + disabled(codeMerging));
      System.out.println(
          "  Number of variable peephole optimizations:   "
              + codeSimplificationVariableCount
              + disabled(codeSimplificationVariable));
      System.out.println(
          "  Number of arithmetic peephole optimizations: "
              + codeSimplificationArithmeticCount
              + disabled(codeSimplificationArithmetic));
      System.out.println(
          "  Number of cast peephole optimizations:       "
              + codeSimplificationCastCount
              + disabled(codeSimplificationCast));
      System.out.println(
          "  Number of field peephole optimizations:      "
              + codeSimplificationFieldCount
              + disabled(codeSimplificationField));
      System.out.println(
          "  Number of branch peephole optimizations:     "
              + codeSimplificationBranchCount
              + disabled(codeSimplificationBranch));
      System.out.println(
          "  Number of string peephole optimizations:     "
              + codeSimplificationStringCount
              + disabled(codeSimplificationString));
      System.out.println(
          "  Number of simplified instructions:           "
              + codeSimplificationAdvancedCount
              + disabled(codeSimplificationAdvanced));
      System.out.println(
          "  Number of removed instructions:              "
              + codeRemovalCount
              + disabled(codeRemovalAdvanced));
      System.out.println(
          "  Number of removed local variables:           "
              + codeRemovalVariableCount
              + disabled(codeRemovalVariable));
      System.out.println(
          "  Number of removed exception blocks:          "
              + codeRemovalExceptionCount
              + disabled(codeRemovalException));
      System.out.println(
          "  Number of optimized local variable frames:   "
              + codeAllocationVariableCount
              + disabled(codeAllocationVariable));
    }

    return classMarkingFinalCount > 0
        || classUnboxingEnumCount > 0
        || classMergingVerticalCount > 0
        || classMergingHorizontalCount > 0
        || fieldRemovalWriteonlyCount > 0
        || fieldMarkingPrivateCount > 0
        || methodMarkingPrivateCount > 0
        || methodMarkingStaticCount > 0
        || methodMarkingFinalCount > 0
        || fieldPropagationValueCount > 0
        || methodRemovalParameterCount > 0
        || methodPropagationParameterCount > 0
        || methodPropagationReturnvalueCount > 0
        || methodInliningShortCount > 0
        || methodInliningUniqueCount > 0
        || methodInliningTailrecursionCount > 0
        || codeMergingCount > 0
        || codeSimplificationVariableCount > 0
        || codeSimplificationArithmeticCount > 0
        || codeSimplificationCastCount > 0
        || codeSimplificationFieldCount > 0
        || codeSimplificationBranchCount > 0
        || codeSimplificationStringCount > 0
        || codeSimplificationAdvancedCount > 0
        || codeRemovalCount > 0
        || codeRemovalVariableCount > 0
        || codeRemovalExceptionCount > 0
        || codeAllocationVariableCount > 0;
  }