/** 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()); }
/** 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; }