diff options
Diffstat (limited to 'src/proguard/optimize')
87 files changed, 4328 insertions, 698 deletions
diff --git a/src/proguard/optimize/BootstrapMethodArgumentShrinker.java b/src/proguard/optimize/BootstrapMethodArgumentShrinker.java index 26f1349..b4ac4cc 100644 --- a/src/proguard/optimize/BootstrapMethodArgumentShrinker.java +++ b/src/proguard/optimize/BootstrapMethodArgumentShrinker.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2014 Eric Lafortune (eric@graphics.cornell.edu) * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -21,13 +21,11 @@ package proguard.optimize; import proguard.classfile.*; -import proguard.classfile.attribute.*; -import proguard.classfile.attribute.annotation.*; -import proguard.classfile.attribute.visitor.*; +import proguard.classfile.attribute.BootstrapMethodInfo; +import proguard.classfile.attribute.visitor.BootstrapMethodInfoVisitor; import proguard.classfile.constant.*; import proguard.classfile.constant.visitor.ConstantVisitor; -import proguard.classfile.editor.ConstantPoolEditor; -import proguard.classfile.util.*; +import proguard.classfile.util.SimplifiedVisitor; import proguard.classfile.visitor.MemberVisitor; import proguard.optimize.info.*; import proguard.optimize.peephole.VariableShrinker; @@ -96,6 +94,9 @@ implements BootstrapMethodInfoVisitor, // Implementations for MemberVisitor. + public void visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryMethod) {} + + public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) { usedParameters = ParameterUsageMarker.getUsedParameters(programMethod); diff --git a/src/proguard/optimize/ChangedCodePrinter.java b/src/proguard/optimize/ChangedCodePrinter.java index 668d43d..261e164 100644 --- a/src/proguard/optimize/ChangedCodePrinter.java +++ b/src/proguard/optimize/ChangedCodePrinter.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2014 Eric Lafortune (eric@graphics.cornell.edu) * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -47,7 +47,6 @@ implements AttributeVisitor // Implementations for AttributeVisitor. - public void visitUnknownAttribute(Clazz clazz, UnknownAttribute unknownAttribute) { attributeVisitor.visitUnknownAttribute(clazz, unknownAttribute); @@ -144,6 +143,12 @@ implements AttributeVisitor } + public void visitMethodParametersAttribute(Clazz clazz, Method method, MethodParametersAttribute exceptionsAttribute) + { + attributeVisitor.visitMethodParametersAttribute(clazz, method, exceptionsAttribute); + } + + public void visitExceptionsAttribute(Clazz clazz, Method method, ExceptionsAttribute exceptionsAttribute) { attributeVisitor.visitExceptionsAttribute(clazz, method, exceptionsAttribute); @@ -228,6 +233,54 @@ implements AttributeVisitor } + public void visitRuntimeVisibleTypeAnnotationsAttribute(Clazz clazz, RuntimeVisibleTypeAnnotationsAttribute runtimeVisibleTypeAnnotationsAttribute) + { + attributeVisitor.visitRuntimeVisibleTypeAnnotationsAttribute(clazz, runtimeVisibleTypeAnnotationsAttribute); + } + + + public void visitRuntimeVisibleTypeAnnotationsAttribute(Clazz clazz, Field field, RuntimeVisibleTypeAnnotationsAttribute runtimeVisibleTypeAnnotationsAttribute) + { + attributeVisitor.visitRuntimeVisibleTypeAnnotationsAttribute(clazz, field, runtimeVisibleTypeAnnotationsAttribute); + } + + + public void visitRuntimeVisibleTypeAnnotationsAttribute(Clazz clazz, Method method, RuntimeVisibleTypeAnnotationsAttribute runtimeVisibleTypeAnnotationsAttribute) + { + attributeVisitor.visitRuntimeVisibleTypeAnnotationsAttribute(clazz, method, runtimeVisibleTypeAnnotationsAttribute); + } + + + public void visitRuntimeVisibleTypeAnnotationsAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, RuntimeVisibleTypeAnnotationsAttribute runtimeVisibleTypeAnnotationsAttribute) + { + attributeVisitor.visitRuntimeVisibleTypeAnnotationsAttribute(clazz, method, codeAttribute, runtimeVisibleTypeAnnotationsAttribute); + } + + + public void visitRuntimeInvisibleTypeAnnotationsAttribute(Clazz clazz, RuntimeInvisibleTypeAnnotationsAttribute runtimeInvisibleTypeAnnotationsAttribute) + { + attributeVisitor.visitRuntimeInvisibleTypeAnnotationsAttribute(clazz, runtimeInvisibleTypeAnnotationsAttribute); + } + + + public void visitRuntimeInvisibleTypeAnnotationsAttribute(Clazz clazz, Field field, RuntimeInvisibleTypeAnnotationsAttribute runtimeInvisibleTypeAnnotationsAttribute) + { + attributeVisitor.visitRuntimeInvisibleTypeAnnotationsAttribute(clazz, field, runtimeInvisibleTypeAnnotationsAttribute); + } + + + public void visitRuntimeInvisibleTypeAnnotationsAttribute(Clazz clazz, Method method, RuntimeInvisibleTypeAnnotationsAttribute runtimeInvisibleTypeAnnotationsAttribute) + { + attributeVisitor.visitRuntimeInvisibleTypeAnnotationsAttribute(clazz, method, runtimeInvisibleTypeAnnotationsAttribute); + } + + + public void visitRuntimeInvisibleTypeAnnotationsAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, RuntimeInvisibleTypeAnnotationsAttribute runtimeInvisibleTypeAnnotationsAttribute) + { + attributeVisitor.visitRuntimeInvisibleTypeAnnotationsAttribute(clazz, method, codeAttribute, runtimeInvisibleTypeAnnotationsAttribute); + } + + public void visitAnnotationDefaultAttribute(Clazz clazz, Method method, AnnotationDefaultAttribute annotationDefaultAttribute) { attributeVisitor.visitAnnotationDefaultAttribute(clazz, method, annotationDefaultAttribute); diff --git a/src/proguard/optimize/ConstantMemberFilter.java b/src/proguard/optimize/ConstantMemberFilter.java index 1f30a30..6bd8619 100644 --- a/src/proguard/optimize/ConstantMemberFilter.java +++ b/src/proguard/optimize/ConstantMemberFilter.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2014 Eric Lafortune (eric@graphics.cornell.edu) * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/optimize/ConstantParameterFilter.java b/src/proguard/optimize/ConstantParameterFilter.java index 1500fd0..8a5058e 100644 --- a/src/proguard/optimize/ConstantParameterFilter.java +++ b/src/proguard/optimize/ConstantParameterFilter.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2014 Eric Lafortune (eric@graphics.cornell.edu) * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -25,7 +25,6 @@ import proguard.classfile.util.*; import proguard.classfile.visitor.MemberVisitor; import proguard.evaluation.value.Value; import proguard.optimize.evaluation.StoringInvocationUnit; -import proguard.optimize.info.ParameterUsageMarker; /** * This <code>MemberVisitor</code> delegates its visits to program methods @@ -60,7 +59,7 @@ implements MemberVisitor // All parameters of non-static methods are shifted by one in the local // variable frame. int firstParameterIndex = - (programMethod.getAccessFlags() & ClassConstants.INTERNAL_ACC_STATIC) != 0 ? + (programMethod.getAccessFlags() & ClassConstants.ACC_STATIC) != 0 ? 0 : 1; int parameterCount = diff --git a/src/proguard/optimize/DuplicateInitializerFixer.java b/src/proguard/optimize/DuplicateInitializerFixer.java index 95bc2f1..f5d5787 100644 --- a/src/proguard/optimize/DuplicateInitializerFixer.java +++ b/src/proguard/optimize/DuplicateInitializerFixer.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2014 Eric Lafortune (eric@graphics.cornell.edu) * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -21,9 +21,9 @@ package proguard.optimize; import proguard.classfile.*; -import proguard.classfile.attribute.visitor.AttributeVisitor; import proguard.classfile.attribute.*; import proguard.classfile.attribute.annotation.*; +import proguard.classfile.attribute.visitor.AttributeVisitor; import proguard.classfile.editor.ConstantPoolEditor; import proguard.classfile.util.*; import proguard.classfile.visitor.MemberVisitor; @@ -41,11 +41,11 @@ implements MemberVisitor, private static final char[] TYPES = new char[] { - ClassConstants.INTERNAL_TYPE_BYTE, - ClassConstants.INTERNAL_TYPE_CHAR, - ClassConstants.INTERNAL_TYPE_SHORT, - ClassConstants.INTERNAL_TYPE_INT, - ClassConstants.INTERNAL_TYPE_BOOLEAN + ClassConstants.TYPE_BYTE, + ClassConstants.TYPE_CHAR, + ClassConstants.TYPE_SHORT, + ClassConstants.TYPE_INT, + ClassConstants.TYPE_BOOLEAN }; @@ -78,7 +78,7 @@ implements MemberVisitor, { // Is it a class instance initializer? String name = programMethod.getName(programClass); - if (name.equals(ClassConstants.INTERNAL_METHOD_NAME_INIT)) + if (name.equals(ClassConstants.METHOD_NAME_INIT)) { // Is there already another initializer with the same descriptor? String descriptor = programMethod.getDescriptor(programClass); @@ -92,7 +92,7 @@ implements MemberVisitor, programMethod = (ProgramMethod)similarMethod; } - int index = descriptor.indexOf(ClassConstants.INTERNAL_METHOD_ARGUMENTS_CLOSE); + int index = descriptor.indexOf(ClassConstants.METHOD_ARGUMENTS_CLOSE); // Try to find a new, unique descriptor. int typeCounter = 0; @@ -105,7 +105,7 @@ implements MemberVisitor, for (int arrayDimension = 0; arrayDimension < typeCounter / TYPES.length; arrayDimension++) { - newDescriptorBuffer.append(ClassConstants.INTERNAL_TYPE_ARRAY); + newDescriptorBuffer.append(ClassConstants.TYPE_ARRAY); } newDescriptorBuffer.append(TYPES[typeCounter % TYPES.length]); @@ -171,9 +171,9 @@ implements MemberVisitor, public void visitSignatureAttribute(Clazz clazz, Method method, SignatureAttribute signatureAttribute) { String descriptor = method.getDescriptor(clazz); - int descriptorIndex = descriptor.indexOf(ClassConstants.INTERNAL_METHOD_ARGUMENTS_CLOSE); - String signature = clazz.getString(signatureAttribute.u2signatureIndex); - int signatureIndex = signature.indexOf(ClassConstants.INTERNAL_METHOD_ARGUMENTS_CLOSE); + int descriptorIndex = descriptor.indexOf(ClassConstants.METHOD_ARGUMENTS_CLOSE); + String signature = signatureAttribute.getSignature(clazz); + int signatureIndex = signature.indexOf(ClassConstants.METHOD_ARGUMENTS_CLOSE); String newSignature = signature.substring(0, signatureIndex) + descriptor.charAt(descriptorIndex - 1) + @@ -188,13 +188,13 @@ implements MemberVisitor, public void visitAnyParameterAnnotationsAttribute(Clazz clazz, Method method, ParameterAnnotationsAttribute parameterAnnotationsAttribute) { // Update the number of parameters. - int oldParametersCount = parameterAnnotationsAttribute.u2parametersCount++; + int oldParametersCount = parameterAnnotationsAttribute.u1parametersCount++; if (parameterAnnotationsAttribute.u2parameterAnnotationsCount == null || - parameterAnnotationsAttribute.u2parameterAnnotationsCount.length < parameterAnnotationsAttribute.u2parametersCount) + parameterAnnotationsAttribute.u2parameterAnnotationsCount.length < parameterAnnotationsAttribute.u1parametersCount) { - int[] annotationsCounts = new int[parameterAnnotationsAttribute.u2parametersCount]; - Annotation[][] annotations = new Annotation[parameterAnnotationsAttribute.u2parametersCount][]; + int[] annotationsCounts = new int[parameterAnnotationsAttribute.u1parametersCount]; + Annotation[][] annotations = new Annotation[parameterAnnotationsAttribute.u1parametersCount][]; System.arraycopy(parameterAnnotationsAttribute.u2parameterAnnotationsCount, 0, diff --git a/src/proguard/optimize/DuplicateInitializerInvocationFixer.java b/src/proguard/optimize/DuplicateInitializerInvocationFixer.java index 5edaba0..22bfc52 100644 --- a/src/proguard/optimize/DuplicateInitializerInvocationFixer.java +++ b/src/proguard/optimize/DuplicateInitializerInvocationFixer.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2014 Eric Lafortune (eric@graphics.cornell.edu) * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -131,11 +131,15 @@ implements AttributeVisitor, // Implementations for ConstantVisitor. - public void visitMethodrefConstant(Clazz clazz, MethodrefConstant methodrefConstant) + public void visitAnyMethodrefConstant(Clazz clazz, RefConstant refConstant) { // Check the referenced constructor descriptor. - descriptor = methodrefConstant.getType(clazz); - methodrefConstant.referencedMemberAccept(this); + if (refConstant.getName(clazz).equals(ClassConstants.METHOD_NAME_INIT)) + { + descriptor = refConstant.getType(clazz); + + refConstant.referencedMemberAccept(this); + } } diff --git a/src/proguard/optimize/KeepMarker.java b/src/proguard/optimize/KeepMarker.java index b0eab7b..c98ca6e 100644 --- a/src/proguard/optimize/KeepMarker.java +++ b/src/proguard/optimize/KeepMarker.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2014 Eric Lafortune (eric@graphics.cornell.edu) * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/optimize/KeptClassFilter.java b/src/proguard/optimize/KeptClassFilter.java index 60a9d3e..c4e436f 100644 --- a/src/proguard/optimize/KeptClassFilter.java +++ b/src/proguard/optimize/KeptClassFilter.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2014 Eric Lafortune (eric@graphics.cornell.edu) * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/optimize/KeptMemberFilter.java b/src/proguard/optimize/KeptMemberFilter.java index 1bdadb4..279c019 100644 --- a/src/proguard/optimize/KeptMemberFilter.java +++ b/src/proguard/optimize/KeptMemberFilter.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2014 Eric Lafortune (eric@graphics.cornell.edu) * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/optimize/MemberDescriptorSpecializer.java b/src/proguard/optimize/MemberDescriptorSpecializer.java index 4dce62e..4656092 100644 --- a/src/proguard/optimize/MemberDescriptorSpecializer.java +++ b/src/proguard/optimize/MemberDescriptorSpecializer.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2014 Eric Lafortune (eric@graphics.cornell.edu) * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -99,7 +99,7 @@ implements MemberVisitor // All parameters of non-static methods are shifted by one in the local // variable frame. int firstParameterIndex = - (programMethod.getAccessFlags() & ClassConstants.INTERNAL_ACC_STATIC) != 0 ? + (programMethod.getAccessFlags() & ClassConstants.ACC_STATIC) != 0 ? 0 : 1; int parameterCount = diff --git a/src/proguard/optimize/MethodDescriptorShrinker.java b/src/proguard/optimize/MethodDescriptorShrinker.java index d8d4425..f5f52b0 100644 --- a/src/proguard/optimize/MethodDescriptorShrinker.java +++ b/src/proguard/optimize/MethodDescriptorShrinker.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2014 Eric Lafortune (eric@graphics.cornell.edu) * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -23,13 +23,15 @@ package proguard.optimize; import proguard.classfile.*; import proguard.classfile.attribute.*; import proguard.classfile.attribute.annotation.*; -import proguard.classfile.attribute.visitor.*; +import proguard.classfile.attribute.visitor.AttributeVisitor; import proguard.classfile.editor.ConstantPoolEditor; import proguard.classfile.util.*; import proguard.classfile.visitor.MemberVisitor; import proguard.optimize.info.*; import proguard.optimize.peephole.VariableShrinker; +import java.util.Arrays; + /** * This MemberVisitor removes unused parameters in the descriptors of the * methods that it visits. @@ -74,32 +76,26 @@ implements MemberVisitor, public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) { + if (DEBUG) + { + System.out.println("MethodDescriptorShrinker: ["+programClass.getName()+"."+programMethod.getName(programClass)+programMethod.getDescriptor(programClass)+"]"); + } + // Update the descriptor if it has any unused parameters. String descriptor = programMethod.getDescriptor(programClass); String newDescriptor = shrinkDescriptor(programMethod, descriptor); - if (!descriptor.equals(newDescriptor)) + if (!newDescriptor.equals(descriptor)) { - // Shrink the signature and parameter annotations. - programMethod.attributesAccept(programClass, this); - String name = programMethod.getName(programClass); String newName = name; // Append a code, if the method isn't a class instance initializer. - if (!name.equals(ClassConstants.INTERNAL_METHOD_NAME_INIT)) + if (!name.equals(ClassConstants.METHOD_NAME_INIT)) { newName += ClassConstants.SPECIAL_MEMBER_SEPARATOR + Long.toHexString(Math.abs((descriptor).hashCode())); } - if (DEBUG) - { - System.out.println("MethodDescriptorShrinker:"); - System.out.println(" ["+programClass.getName()+"."+ - name+descriptor+"] -> ["+ - newName+newDescriptor+"]"); - } - ConstantPoolEditor constantPoolEditor = new ConstantPoolEditor(programClass); @@ -120,6 +116,14 @@ implements MemberVisitor, programMethod.u2descriptorIndex = constantPoolEditor.addUtf8Constant(newDescriptor); + if (DEBUG) + { + System.out.println(" -> ["+newName+newDescriptor+"]"); + } + + // Shrink the signature and parameter annotations. + programMethod.attributesAccept(programClass, this); + // Visit the method, if required. if (extraMemberVisitor != null) { @@ -136,19 +140,32 @@ implements MemberVisitor, public void visitSignatureAttribute(Clazz clazz, Method method, SignatureAttribute signatureAttribute) { + if (DEBUG) + { + System.out.println(" ["+signatureAttribute.getSignature(clazz)+"]"); + } + // Compute the new signature. - String signature = clazz.getString(signatureAttribute.u2signatureIndex); + String signature = signatureAttribute.getSignature(clazz); String newSignature = shrinkDescriptor(method, signature); - // Update the signature. - signatureAttribute.u2signatureIndex = - new ConstantPoolEditor((ProgramClass)clazz).addUtf8Constant(newSignature); + if (!newSignature.equals(signature)) + { + // Update the signature. + signatureAttribute.u2signatureIndex = + new ConstantPoolEditor((ProgramClass)clazz).addUtf8Constant(newSignature); - // Update the referenced classes. - signatureAttribute.referencedClasses = - shrinkReferencedClasses(method, - signature, - signatureAttribute.referencedClasses); + // Update the referenced classes. + signatureAttribute.referencedClasses = + shrinkReferencedClasses(method, + signature, + signatureAttribute.referencedClasses); + + if (DEBUG) + { + System.out.println(" -> ["+newSignature+"]"); + } + } } @@ -160,7 +177,7 @@ implements MemberVisitor, // All parameters of non-static methods are shifted by one in the local // variable frame. int parameterIndex = - (method.getAccessFlags() & ClassConstants.INTERNAL_ACC_STATIC) != 0 ? + (method.getAccessFlags() & ClassConstants.ACC_STATIC) != 0 ? 0 : 1; int annotationIndex = 0; @@ -186,7 +203,7 @@ implements MemberVisitor, } // Update the number of parameters. - parameterAnnotationsAttribute.u2parametersCount = newAnnotationIndex; + parameterAnnotationsAttribute.u1parametersCount = newAnnotationIndex; // Clear the unused entries. while (newAnnotationIndex < annotationIndex) @@ -207,18 +224,20 @@ implements MemberVisitor, // All parameters of non-static methods are shifted by one in the local // variable frame. int parameterIndex = - (method.getAccessFlags() & ClassConstants.INTERNAL_ACC_STATIC) != 0 ? + (method.getAccessFlags() & ClassConstants.ACC_STATIC) != 0 ? 0 : 1; - // Go over the parameters. InternalTypeEnumeration internalTypeEnumeration = new InternalTypeEnumeration(descriptor); - StringBuffer newDescriptorBuffer = new StringBuffer(); + StringBuffer newDescriptorBuffer = + new StringBuffer(descriptor.length()); + // Copy the formal type parameters. newDescriptorBuffer.append(internalTypeEnumeration.formalTypeParameters()); - newDescriptorBuffer.append(ClassConstants.INTERNAL_METHOD_ARGUMENTS_OPEN); + newDescriptorBuffer.append(ClassConstants.METHOD_ARGUMENTS_OPEN); + // Go over the parameters. while (internalTypeEnumeration.hasMoreTypes()) { String type = internalTypeEnumeration.nextType(); @@ -234,7 +253,8 @@ implements MemberVisitor, parameterIndex += ClassUtil.isInternalCategory2Type(type) ? 2 : 1; } - newDescriptorBuffer.append(ClassConstants.INTERNAL_METHOD_ARGUMENTS_CLOSE); + // Copy the return type. + newDescriptorBuffer.append(ClassConstants.METHOD_ARGUMENTS_CLOSE); newDescriptorBuffer.append(internalTypeEnumeration.returnType()); return newDescriptorBuffer.toString(); @@ -253,30 +273,32 @@ implements MemberVisitor, // All parameters of non-static methods are shifted by one in the local // variable frame. int parameterIndex = - (method.getAccessFlags() & ClassConstants.INTERNAL_ACC_STATIC) != 0 ? + (method.getAccessFlags() & ClassConstants.ACC_STATIC) != 0 ? 0 : 1; - int referencedClassIndex = 0; - int newReferencedClassIndex = 0; - - // Go over the parameters. InternalTypeEnumeration internalTypeEnumeration = new InternalTypeEnumeration(descriptor); - // Also look at the formal type parameters. - String type = internalTypeEnumeration.formalTypeParameters(); - int count = new DescriptorClassEnumeration(type).classCount(); - for (int counter = 0; counter < count; counter++) + int referencedClassIndex = 0; + int newReferencedClassIndex = 0; + + // Copy the formal type parameters. { - referencedClasses[newReferencedClassIndex++] = - referencedClasses[referencedClassIndex++]; + String type = internalTypeEnumeration.formalTypeParameters(); + int count = new DescriptorClassEnumeration(type).classCount(); + for (int counter = 0; counter < count; counter++) + { + referencedClasses[newReferencedClassIndex++] = + referencedClasses[referencedClassIndex++]; + } } + // Go over the parameters. while (internalTypeEnumeration.hasMoreTypes()) { // Consider the classes referenced by this parameter type. - type = internalTypeEnumeration.nextType(); - count = new DescriptorClassEnumeration(type).classCount(); + String type = internalTypeEnumeration.nextType(); + int count = new DescriptorClassEnumeration(type).classCount(); if (ParameterUsageMarker.isParameterUsed(method, parameterIndex)) { @@ -296,19 +318,30 @@ implements MemberVisitor, parameterIndex += ClassUtil.isInternalCategory2Type(type) ? 2 : 1; } - // Also look at the return value. - type = internalTypeEnumeration.returnType(); - count = new DescriptorClassEnumeration(type).classCount(); - for (int counter = 0; counter < count; counter++) + // Copy the return type. { - referencedClasses[newReferencedClassIndex++] = - referencedClasses[referencedClassIndex++]; + String type = internalTypeEnumeration.returnType(); + int count = new DescriptorClassEnumeration(type).classCount(); + for (int counter = 0; counter < count; counter++) + { + referencedClasses[newReferencedClassIndex++] = + referencedClasses[referencedClassIndex++]; + } } - // Clear the unused entries. - while (newReferencedClassIndex < referencedClassIndex) + // Shrink the array to the proper size. + if (newReferencedClassIndex == 0) { - referencedClasses[newReferencedClassIndex++] = null; + referencedClasses = null; + } + else if (newReferencedClassIndex < referencedClassIndex) + { + Clazz[] newReferencedClasses = new Clazz[newReferencedClassIndex]; + System.arraycopy(referencedClasses, 0, + newReferencedClasses, 0, + newReferencedClassIndex); + + referencedClasses = newReferencedClasses; } } diff --git a/src/proguard/optimize/MethodStaticizer.java b/src/proguard/optimize/MethodStaticizer.java index c8bdd11..c672bfa 100644 --- a/src/proguard/optimize/MethodStaticizer.java +++ b/src/proguard/optimize/MethodStaticizer.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2014 Eric Lafortune (eric@graphics.cornell.edu) * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -74,8 +74,8 @@ implements MemberVisitor, { // Make the method static. programMethod.u2accessFlags = - (programMethod.getAccessFlags() & ~ClassConstants.INTERNAL_ACC_FINAL) | - ClassConstants.INTERNAL_ACC_STATIC; + (programMethod.getAccessFlags() & ~ClassConstants.ACC_FINAL) | + ClassConstants.ACC_STATIC; // Visit the method, if required. if (extraStaticMemberVisitor != null) diff --git a/src/proguard/optimize/OptimizationInfoMemberFilter.java b/src/proguard/optimize/OptimizationInfoMemberFilter.java index 2c5454c..bbfac20 100644 --- a/src/proguard/optimize/OptimizationInfoMemberFilter.java +++ b/src/proguard/optimize/OptimizationInfoMemberFilter.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2014 Eric Lafortune (eric@graphics.cornell.edu) * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/optimize/Optimizer.java b/src/proguard/optimize/Optimizer.java index 8042825..20f4083 100644 --- a/src/proguard/optimize/Optimizer.java +++ b/src/proguard/optimize/Optimizer.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2014 Eric Lafortune (eric@graphics.cornell.edu) * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -23,13 +23,12 @@ package proguard.optimize; import proguard.*; import proguard.classfile.*; import proguard.classfile.attribute.visitor.*; -import proguard.classfile.constant.Constant; import proguard.classfile.constant.visitor.*; import proguard.classfile.editor.*; import proguard.classfile.instruction.visitor.*; import proguard.classfile.util.MethodLinker; import proguard.classfile.visitor.*; -import proguard.evaluation.*; +import proguard.evaluation.InvocationUnit; import proguard.evaluation.value.*; import proguard.optimize.evaluation.*; import proguard.optimize.info.*; @@ -47,6 +46,7 @@ import java.util.*; public class Optimizer { private static final String CLASS_MARKING_FINAL = "class/marking/final"; + private static final String CLASS_UNBOXING_ENUM = "class/unboxing/enum"; private static final String CLASS_MERGING_VERTICAL = "class/merging/vertical"; private static final String CLASS_MERGING_HORIZONTAL = "class/merging/horizontal"; private static final String FIELD_REMOVAL_WRITEONLY = "field/removal/writeonly"; @@ -141,6 +141,7 @@ public class Optimizer new ConstantMatcher(true); boolean classMarkingFinal = filter.matches(CLASS_MARKING_FINAL); + boolean classUnboxingEnum = filter.matches(CLASS_UNBOXING_ENUM); boolean classMergingVertical = filter.matches(CLASS_MERGING_VERTICAL); boolean classMergingHorizontal = filter.matches(CLASS_MERGING_HORIZONTAL); boolean fieldRemovalWriteonly = filter.matches(FIELD_REMOVAL_WRITEONLY); @@ -171,6 +172,7 @@ public class Optimizer // 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(); @@ -251,11 +253,13 @@ public class Optimizer 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))))); + new DotClassClassVisitor(keepMarker)))))); // We also keep all classes that are accessed dynamically. programClassPool.classesAccept( @@ -271,7 +275,7 @@ public class Optimizer // We also keep all bootstrap method signatures. programClassPool.classesAccept( - new ClassVersionFilter(ClassConstants.INTERNAL_CLASS_VERSION_1_7, + new ClassVersionFilter(ClassConstants.CLASS_VERSION_1_7, new AllAttributeVisitor( new AttributeNameFilter(ClassConstants.ATTR_BootstrapMethods, new AllBootstrapMethodInfoVisitor( @@ -279,6 +283,32 @@ public class Optimizer 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()); @@ -311,8 +341,9 @@ public class Optimizer { // Make methods final, whereever possible. programClassPool.classesAccept( + new ClassAccessFilter(0, ClassConstants.ACC_INTERFACE, new AllMethodVisitor( - new MethodFinalizer(methodMarkingFinalCounter))); + new MethodFinalizer(methodMarkingFinalCounter)))); } if (fieldRemovalWriteonly) @@ -337,6 +368,55 @@ public class Optimizer 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( @@ -354,25 +434,36 @@ public class Optimizer // programClassPool.classAccept("abc/Def", new NamedMethodVisitor("abc", null, new ClassPrinter())); // Perform partial evaluation for filling out fields, method parameters, - // and method return values. - ValueFactory valueFactory = new IdentifiedValueFactory(); - + // and method return values, so they can be propagated. if (fieldPropagationValue || methodPropagationParameter || methodPropagationReturnvalue) { - // Fill out fields, method parameters, and return values, so they - // can be propagated. + // 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)))); + new PartialEvaluator(valueFactory, storingInvocationUnit, false))))); if (fieldPropagationValue) { @@ -397,8 +488,38 @@ public class Optimizer 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, @@ -446,7 +567,7 @@ public class Optimizer programClassPool.classesAccept( new AllMethodVisitor( new OptimizationInfoMemberFilter( - new MemberAccessFilter(0, ClassConstants.INTERNAL_ACC_STATIC, + new MemberAccessFilter(0, ClassConstants.ACC_STATIC, new MethodStaticizer(methodMarkingStaticCounter))))); } @@ -548,6 +669,7 @@ public class Optimizer new DotClassMarker(), new MethodInvocationMarker(), new SuperInvocationMarker(), + new DynamicInvocationMarker(), new BackwardBranchMarker(), new AccessMethodMarker(), })), @@ -561,7 +683,8 @@ public class Optimizer if (classMergingVertical) { - // Merge classes into their superclasses or interfaces. + // Merge subclasses up into their superclasses or + // merge interfaces down into their implementing classes. programClassPool.classesAccept( new VerticalClassMerger(configuration.allowAccessModification, configuration.mergeInterfacesAggressively, @@ -593,8 +716,7 @@ public class Optimizer // Fix the access flags of referenced merged classes and their // class members. programClassPool.classesAccept( - new AllConstantVisitor( - new AccessFixer())); + new AccessFixer()); } // Fix the access flags of the inner classes information. @@ -667,9 +789,9 @@ public class Optimizer { // Make all non-private fields private, whereever possible. programClassPool.classesAccept( - new ClassAccessFilter(0, ClassConstants.INTERNAL_ACC_INTERFACE, + new ClassAccessFilter(0, ClassConstants.ACC_INTERFACE, new AllFieldVisitor( - new MemberAccessFilter(0, ClassConstants.INTERNAL_ACC_PRIVATE, + new MemberAccessFilter(0, ClassConstants.ACC_PRIVATE, new MemberPrivatizer(fieldMarkingPrivateCounter))))); } @@ -677,9 +799,9 @@ public class Optimizer { // Make all non-private methods private, whereever possible. programClassPool.classesAccept( - new ClassAccessFilter(0, ClassConstants.INTERNAL_ACC_INTERFACE, + new ClassAccessFilter(0, ClassConstants.ACC_INTERFACE, new AllMethodVisitor( - new MemberAccessFilter(0, ClassConstants.INTERNAL_ACC_PRIVATE, + new MemberAccessFilter(0, ClassConstants.ACC_PRIVATE, new MemberPrivatizer(methodMarkingPrivateCounter))))); } @@ -691,8 +813,7 @@ public class Optimizer // Fix the access flags of referenced classes and class members, // for MethodInliner. programClassPool.classesAccept( - new AllConstantVisitor( - new AccessFixer())); + new AccessFixer()); } if (methodRemovalParameterCounter .getCount() > 0 || @@ -842,6 +963,7 @@ public class Optimizer new ConstantPoolShrinker()); int classMarkingFinalCount = classMarkingFinalCounter .getCount(); + int classUnboxingEnumCount = classUnboxingEnumCounter .getCount(); int classMergingVerticalCount = classMergingVerticalCounter .getCount(); int classMergingHorizontalCount = classMergingHorizontalCounter .getCount(); int fieldRemovalWriteonlyCount = fieldRemovalWriteonlyCounter .getCount(); @@ -882,6 +1004,7 @@ public class Optimizer 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)); @@ -911,6 +1034,7 @@ public class Optimizer } return classMarkingFinalCount > 0 || + classUnboxingEnumCount > 0 || classMergingVerticalCount > 0 || classMergingHorizontalCount > 0 || fieldRemovalWriteonlyCount > 0 || diff --git a/src/proguard/optimize/ParameterShrinker.java b/src/proguard/optimize/ParameterShrinker.java index 33d37d1..eb2592e 100644 --- a/src/proguard/optimize/ParameterShrinker.java +++ b/src/proguard/optimize/ParameterShrinker.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2014 Eric Lafortune (eric@graphics.cornell.edu) * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -29,7 +29,7 @@ import proguard.classfile.visitor.MemberVisitor; import proguard.optimize.info.ParameterUsageMarker; /** - * This MemberVisitor removes unused parameters from the code of the methods + * This AttributeVisitor removes unused parameters from the code of the methods * that it visits. * * @see ParameterUsageMarker @@ -41,7 +41,11 @@ public class ParameterShrinker extends SimplifiedVisitor implements AttributeVisitor { + //* private static final boolean DEBUG = false; + /*/ + private static boolean DEBUG = System.getProperty("ps") != null; + //*/ private final MemberVisitor extraVariableMemberVisitor; diff --git a/src/proguard/optimize/TailRecursionSimplifier.java b/src/proguard/optimize/TailRecursionSimplifier.java index f820566..dd38d6b 100644 --- a/src/proguard/optimize/TailRecursionSimplifier.java +++ b/src/proguard/optimize/TailRecursionSimplifier.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2014 Eric Lafortune (eric@graphics.cornell.edu) * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -23,7 +23,7 @@ package proguard.optimize; import proguard.classfile.*; import proguard.classfile.attribute.*; import proguard.classfile.attribute.visitor.*; -import proguard.classfile.constant.MethodrefConstant; +import proguard.classfile.constant.*; import proguard.classfile.constant.visitor.ConstantVisitor; import proguard.classfile.editor.CodeAttributeComposer; import proguard.classfile.instruction.*; @@ -91,15 +91,14 @@ implements AttributeVisitor, int accessFlags = method.getAccessFlags(); if (// Only check the method if it is private, static, or final. - (accessFlags & (ClassConstants.INTERNAL_ACC_PRIVATE | - ClassConstants.INTERNAL_ACC_STATIC | - ClassConstants.INTERNAL_ACC_FINAL)) != 0 && + (accessFlags & (ClassConstants.ACC_PRIVATE | + ClassConstants.ACC_STATIC | + ClassConstants.ACC_FINAL)) != 0 && // Only check the method if it is not synchronized, etc. - (accessFlags & (ClassConstants.INTERNAL_ACC_SYNCHRONIZED | - ClassConstants.INTERNAL_ACC_NATIVE | - ClassConstants.INTERNAL_ACC_INTERFACE | - ClassConstants.INTERNAL_ACC_ABSTRACT)) == 0) + (accessFlags & (ClassConstants.ACC_SYNCHRONIZED | + ClassConstants.ACC_NATIVE | + ClassConstants.ACC_ABSTRACT)) == 0) { // codeAttributeComposer.DEBUG = DEBUG = // clazz.getName().equals("abc/Def") && @@ -254,7 +253,7 @@ implements AttributeVisitor, // Implementations for ConstantVisitor. - public void visitMethodrefConstant(Clazz clazz, MethodrefConstant methodrefConstant) + public void visitAnyMethodrefConstant(Clazz clazz, RefConstant methodrefConstant) { recursive = targetMethod.equals(methodrefConstant.referencedMember); } @@ -280,7 +279,7 @@ implements AttributeVisitor, String descriptor = method.getDescriptor(clazz); boolean isStatic = - (method.getAccessFlags() & ClassConstants.INTERNAL_ACC_STATIC) != 0; + (method.getAccessFlags() & ClassConstants.ACC_STATIC) != 0; // Count the number of parameters, taking into account their categories. int parameterSize = ClassUtil.internalMethodParameterSize(descriptor); @@ -314,23 +313,23 @@ implements AttributeVisitor, byte opcode; switch (parameterType.charAt(0)) { - case ClassConstants.INTERNAL_TYPE_BOOLEAN: - case ClassConstants.INTERNAL_TYPE_BYTE: - case ClassConstants.INTERNAL_TYPE_CHAR: - case ClassConstants.INTERNAL_TYPE_SHORT: - case ClassConstants.INTERNAL_TYPE_INT: + case ClassConstants.TYPE_BOOLEAN: + case ClassConstants.TYPE_BYTE: + case ClassConstants.TYPE_CHAR: + case ClassConstants.TYPE_SHORT: + case ClassConstants.TYPE_INT: opcode = InstructionConstants.OP_ISTORE; break; - case ClassConstants.INTERNAL_TYPE_LONG: + case ClassConstants.TYPE_LONG: opcode = InstructionConstants.OP_LSTORE; break; - case ClassConstants.INTERNAL_TYPE_FLOAT: + case ClassConstants.TYPE_FLOAT: opcode = InstructionConstants.OP_FSTORE; break; - case ClassConstants.INTERNAL_TYPE_DOUBLE: + case ClassConstants.TYPE_DOUBLE: opcode = InstructionConstants.OP_DSTORE; break; diff --git a/src/proguard/optimize/WriteOnlyFieldFilter.java b/src/proguard/optimize/WriteOnlyFieldFilter.java index 762bd91..7aad651 100644 --- a/src/proguard/optimize/WriteOnlyFieldFilter.java +++ b/src/proguard/optimize/WriteOnlyFieldFilter.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2014 Eric Lafortune (eric@graphics.cornell.edu) * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/optimize/evaluation/EvaluationShrinker.java b/src/proguard/optimize/evaluation/EvaluationShrinker.java index 2e86532..daccec1 100644 --- a/src/proguard/optimize/evaluation/EvaluationShrinker.java +++ b/src/proguard/optimize/evaluation/EvaluationShrinker.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2014 Eric Lafortune (eric@graphics.cornell.edu) * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -30,7 +30,7 @@ import proguard.classfile.instruction.*; import proguard.classfile.instruction.visitor.InstructionVisitor; import proguard.classfile.util.*; import proguard.classfile.visitor.*; -import proguard.evaluation.*; +import proguard.evaluation.TracedStack; import proguard.evaluation.value.*; import proguard.optimize.info.*; @@ -50,8 +50,8 @@ implements AttributeVisitor private static final boolean DEBUG_RESULTS = false; private static final boolean DEBUG = false; /*/ - private static boolean DEBUG_RESULTS = true; - private static boolean DEBUG = true; + private static boolean DEBUG = System.getProperty("es") != null; + private static boolean DEBUG_RESULTS = DEBUG; //*/ private static final int UNSUPPORTED = -1; @@ -177,11 +177,7 @@ implements AttributeVisitor if (DEBUG_RESULTS) { System.out.println(); - System.out.println("Class "+ClassUtil.externalClassName(clazz.getName())); - System.out.println("Method "+ClassUtil.externalFullMethodDescription(clazz.getName(), - 0, - method.getName(clazz), - method.getDescriptor(clazz))); + System.out.println("EvaluationShrinker ["+clazz.getName()+"."+method.getName(clazz)+method.getDescriptor(clazz)+"]"); } // Initialize the necessary array. @@ -542,7 +538,7 @@ implements AttributeVisitor int parameterSize = ParameterUsageMarker.getParameterSize(programMethod); // Make the method invocation static, if possible. - if ((programMethod.getAccessFlags() & ClassConstants.INTERNAL_ACC_STATIC) == 0 && + if ((programMethod.getAccessFlags() & ClassConstants.ACC_STATIC) == 0 && !ParameterUsageMarker.isParameterUsed(programMethod, 0)) { replaceByStaticInvocation(programClass, @@ -705,7 +701,7 @@ implements AttributeVisitor { // Mark any variable initializations for this variable load that // are required according to the JVM. - markVariableInitializers(offset, variableInstruction.variableIndex); + markVariableInitializersBefore(offset, variableInstruction.variableIndex); } } } @@ -729,6 +725,8 @@ implements AttributeVisitor if (isInstructionNecessary(offset)) { // Check all stack entries that are popped. + // Unusual case: an exception handler with an exception that is + // no longer consumed directly by a method. // Typical case: a freshly marked variable initialization that // requires some value on the stack. int popCount = instruction.stackPopCount(clazz); @@ -739,17 +737,36 @@ implements AttributeVisitor int stackSize = tracedStack.size(); + int requiredPopCount = 0; int requiredPushCount = 0; for (int stackIndex = stackSize - popCount; stackIndex < stackSize; stackIndex++) { - if (!isStackSimplifiedBefore(offset, stackIndex)) + boolean stackSimplifiedBefore = + isStackSimplifiedBefore(offset, stackIndex); + boolean stackEntryPresentBefore = + isStackEntryPresentBefore(offset, stackIndex); + + if (stackSimplifiedBefore) { // Is this stack entry pushed by any producer - // (because it is required by other consumers)? + // (maybe an exception in an exception handler)? if (isStackEntryPresentBefore(offset, stackIndex)) { // Mark all produced stack entries. markStackEntryProducers(offset, stackIndex); + + // Remember to pop it. + requiredPopCount++; + } + } + else + { + // Is this stack entry pushed by any producer + // (because it is required by other consumers)? + if (stackEntryPresentBefore) + { + // Mark all produced stack entries. + markStackEntryProducers(offset, stackIndex); } else { @@ -759,6 +776,14 @@ implements AttributeVisitor } } + // Pop some unnecessary stack entries. + if (requiredPopCount > 0) + { + if (DEBUG) System.out.println(" Inserting before marked consumer "+instruction.toString(offset)); + + insertPopInstructions(offset, false, true, popCount); + } + // Push some necessary stack entries. if (requiredPushCount > 0) { @@ -769,7 +794,7 @@ implements AttributeVisitor throw new IllegalArgumentException("Unsupported stack size increment ["+requiredPushCount+"] at ["+offset+"]"); } - insertPushInstructions(offset, false, tracedStack.getTop(0).computationalType()); + insertPushInstructions(offset, false, true, tracedStack.getTop(0).computationalType()); } } @@ -826,7 +851,7 @@ implements AttributeVisitor { if (DEBUG) System.out.println(" Inserting after marked producer "+instruction.toString(offset)); - insertPopInstructions(offset, false, requiredPopCount); + insertPopInstructions(offset, false, false, requiredPopCount); } } } @@ -863,7 +888,7 @@ implements AttributeVisitor { if (DEBUG) System.out.println(" Replacing unmarked consumer "+instruction.toString(offset)); - insertPopInstructions(offset, true, expectedPopCount); + insertPopInstructions(offset, true, false, expectedPopCount); } } @@ -893,7 +918,7 @@ implements AttributeVisitor { if (DEBUG) System.out.println(" Replacing unmarked producer "+instruction.toString(offset)); - insertPushInstructions(offset, true, tracedStack.getTop(0).computationalType()); + insertPushInstructions(offset, true, false, tracedStack.getTop(0).computationalType()); } } } @@ -1434,35 +1459,65 @@ implements AttributeVisitor /** - * Marks the initializing instructions of the variable consumer at the given - * offset. - * @param consumerOffset the offset of the variable consumer. - * @param variableIndex the index of the variable that is loaded. + * Ensures that the given variable is initialized before the specified + * consumer of that variable, in the JVM's view. + * @param consumerOffset the instruction offset before which the variable + * needs to be initialized. + * @param variableIndex the index of the variable. */ - private void markVariableInitializers(int consumerOffset, - int variableIndex) + private void markVariableInitializersBefore(int consumerOffset, + int variableIndex) { + // Make sure the variable is initialized after all producers. + // Use the simple evaluator, to get the JVM's view of what is + // initialized. InstructionOffsetValue producerOffsets = simplePartialEvaluator.getVariablesBefore(consumerOffset).getProducerValue(variableIndex).instructionOffsetValue(); - if (producerOffsets != null) + int offsetCount = producerOffsets.instructionOffsetCount(); + for (int offsetIndex = 0; offsetIndex < offsetCount; offsetIndex++) { - int offsetCount = producerOffsets.instructionOffsetCount(); - for (int offsetIndex = 0; offsetIndex < offsetCount; offsetIndex++) + // Avoid infinite loops by only looking at producers before + // the consumer. + int producerOffset = + producerOffsets.instructionOffset(offsetIndex); + if (producerOffset < consumerOffset) { - // Make sure the variable and the instruction are marked - // at the producing offset. - int offset = producerOffsets.instructionOffset(offsetIndex); + markVariableInitializersAfter(producerOffset, variableIndex); + } + } + } - if (!isInstructionNecessary(offset) && - isVariableInitialization(offset, variableIndex)) - { - if (DEBUG) System.out.print(" Marking initialization of v"+variableIndex+" at "); - markInstruction(offset); + /** + * Ensures that the given variable is initialized after the specified + * producer of that variable, in the JVM's view. + * @param producerOffset the instruction offset after which the variable + * needs to be initialized. + * @param variableIndex the index of the variable. + */ + private void markVariableInitializersAfter(int producerOffset, + int variableIndex) + { + // No problem if the producer has already been marked. + if (!isInstructionNecessary(producerOffset)) + { + // Is the unmarked producer a variable initialization? + if (isVariableInitialization(producerOffset, variableIndex)) + { + // Mark the producer. + if (DEBUG) System.out.print(" Marking initialization of v"+variableIndex+" at "); - if (DEBUG) System.out.println(); - } + markInstruction(producerOffset); + + if (DEBUG) System.out.println(); + } + else + { + // Don't mark the producer, but recursively look at the + // preceding producers of the same variable. Their values + // will fall through, replacing this producer. + markVariableInitializersBefore(producerOffset, variableIndex); } } } @@ -1645,6 +1700,7 @@ implements AttributeVisitor */ private void insertPushInstructions(int offset, boolean replace, + boolean before, int computationalType) { // Mark this instruction. @@ -1657,21 +1713,7 @@ implements AttributeVisitor if (DEBUG) System.out.println(": "+replacementInstruction.toString(offset)); // Replace or insert the push instruction. - if (replace) - { - // Replace the push instruction. - codeAttributeEditor.replaceInstruction(offset, replacementInstruction); - } - else - { - // Insert the push instruction. - codeAttributeEditor.insertBeforeInstruction(offset, replacementInstruction); - - if (extraAddedInstructionVisitor != null) - { - replacementInstruction.accept(null, null, null, offset, extraAddedInstructionVisitor); - } - } + insertInstruction(offset, replace, before, replacementInstruction); } @@ -1700,7 +1742,10 @@ implements AttributeVisitor * Pops the given number of stack entries at or after the given offset. * The instructions are marked as necessary. */ - private void insertPopInstructions(int offset, boolean replace, int popCount) + private void insertPopInstructions(int offset, + boolean replace, + boolean before, + int popCount) { // Mark this instruction. markInstruction(offset); @@ -1713,19 +1758,7 @@ implements AttributeVisitor Instruction popInstruction = new SimpleInstruction(InstructionConstants.OP_POP); - if (replace) - { - codeAttributeEditor.replaceInstruction(offset, popInstruction); - } - else - { - codeAttributeEditor.insertAfterInstruction(offset, popInstruction); - - if (extraAddedInstructionVisitor != null) - { - popInstruction.accept(null, null, null, offset, extraAddedInstructionVisitor); - } - } + insertInstruction(offset, replace, before, popInstruction); break; } case 2: @@ -1734,19 +1767,7 @@ implements AttributeVisitor Instruction popInstruction = new SimpleInstruction(InstructionConstants.OP_POP2); - if (replace) - { - codeAttributeEditor.replaceInstruction(offset, popInstruction); - } - else - { - codeAttributeEditor.insertAfterInstruction(offset, popInstruction); - - if (extraAddedInstructionVisitor != null) - { - popInstruction.accept(null, null, null, offset, extraAddedInstructionVisitor); - } - } + insertInstruction(offset, replace, before, popInstruction); break; } default: @@ -1771,31 +1792,86 @@ implements AttributeVisitor popInstructions[popCount / 2] = popInstruction; } - if (replace) - { - codeAttributeEditor.replaceInstruction(offset, popInstructions); + insertInstructions(offset, + replace, + before, + popInstruction, + popInstructions); + break; + } + } + } - for (int index = 1; index < popInstructions.length; index++) - { - if (extraAddedInstructionVisitor != null) - { - popInstructions[index].accept(null, null, null, offset, extraAddedInstructionVisitor); - } - } - } - else + + /** + * Inserts or replaces the given instruction at the given offset. + */ + private void insertInstruction(int offset, + boolean replace, + boolean before, + Instruction instruction) + { + if (replace) + { + codeAttributeEditor.replaceInstruction(offset, instruction); + } + else + { + if (before) + { + codeAttributeEditor.insertBeforeInstruction(offset, instruction); + } + else + { + codeAttributeEditor.insertAfterInstruction(offset, instruction); + } + + if (extraAddedInstructionVisitor != null) + { + instruction.accept(null, null, null, offset, extraAddedInstructionVisitor); + } + } + } + + + /** + * Inserts or replaces the given instruction at the given offset. + */ + private void insertInstructions(int offset, + boolean replace, + boolean before, + Instruction instruction, + Instruction[] instructions) + { + if (replace) + { + codeAttributeEditor.replaceInstruction(offset, instructions); + + if (extraAddedInstructionVisitor != null) + { + for (int index = 1; index < instructions.length; index++) { - codeAttributeEditor.insertAfterInstruction(offset, popInstructions); + instructions[index].accept(null, null, null, offset, extraAddedInstructionVisitor); + } + } + } + else + { + if (before) + { + codeAttributeEditor.insertBeforeInstruction(offset, instructions); + } + else + { + codeAttributeEditor.insertAfterInstruction(offset, instructions); + } - for (int index = 0; index < popInstructions.length; index++) - { - if (extraAddedInstructionVisitor != null) - { - popInstructions[index].accept(null, null, null, offset, extraAddedInstructionVisitor); - } - } + for (int index = 0; index < instructions.length; index++) + { + if (extraAddedInstructionVisitor != null) + { + instructions[index].accept(null, null, null, offset, extraAddedInstructionVisitor); } - break; } } } @@ -1998,14 +2074,14 @@ implements AttributeVisitor int variableIndex) { // Wasn't the variable set yet? - Value valueBefore = partialEvaluator.getVariablesBefore(instructionOffset).getValue(variableIndex); + Value valueBefore = simplePartialEvaluator.getVariablesBefore(instructionOffset).getValue(variableIndex); if (valueBefore == null) { return true; } // Is the computational type different now? - Value valueAfter = partialEvaluator.getVariablesAfter(instructionOffset).getValue(variableIndex); + Value valueAfter = simplePartialEvaluator.getVariablesAfter(instructionOffset).getValue(variableIndex); if (valueAfter.computationalType() != valueBefore.computationalType()) { return true; @@ -2020,7 +2096,7 @@ implements AttributeVisitor } // Was the producer an argument (which may be removed)? - Value producersBefore = partialEvaluator.getVariablesBefore(instructionOffset).getProducerValue(variableIndex); + Value producersBefore = simplePartialEvaluator.getVariablesBefore(instructionOffset).getProducerValue(variableIndex); return producersBefore.instructionOffsetValue().instructionOffsetCount() == 1 && producersBefore.instructionOffsetValue().instructionOffset(0) == PartialEvaluator.AT_METHOD_ENTRY; } diff --git a/src/proguard/optimize/evaluation/EvaluationSimplifier.java b/src/proguard/optimize/evaluation/EvaluationSimplifier.java index e6e73d9..8187342 100644 --- a/src/proguard/optimize/evaluation/EvaluationSimplifier.java +++ b/src/proguard/optimize/evaluation/EvaluationSimplifier.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2014 Eric Lafortune (eric@graphics.cornell.edu) * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -27,10 +27,12 @@ import proguard.classfile.editor.*; import proguard.classfile.instruction.*; import proguard.classfile.instruction.visitor.InstructionVisitor; import proguard.classfile.util.*; -import proguard.classfile.visitor.*; -import proguard.evaluation.*; +import proguard.classfile.visitor.ClassPrinter; +import proguard.evaluation.TracedVariables; import proguard.evaluation.value.*; -import proguard.optimize.info.*; +import proguard.optimize.info.SideEffectInstructionChecker; + +import java.util.Arrays; /** * This AttributeVisitor simplifies the code attributes that it visits, based @@ -49,7 +51,7 @@ implements AttributeVisitor, //* private static final boolean DEBUG = false; /*/ - private static boolean DEBUG = true; + private static boolean DEBUG = System.getProperty("es") != null; //*/ private final InstructionVisitor extraInstructionVisitor; @@ -125,11 +127,7 @@ implements AttributeVisitor, if (DEBUG) { System.out.println(); - System.out.println("Class "+ClassUtil.externalClassName(clazz.getName())); - System.out.println("Method "+ClassUtil.externalFullMethodDescription(clazz.getName(), - 0, - method.getName(clazz), - method.getDescriptor(clazz))); + System.out.println("EvaluationSimplifier ["+clazz.getName()+"."+method.getName(clazz)+method.getDescriptor(clazz)+"]"); } // Evaluate the method. @@ -185,6 +183,7 @@ implements AttributeVisitor, case InstructionConstants.OP_I2B: case InstructionConstants.OP_I2C: case InstructionConstants.OP_I2S: + case InstructionConstants.OP_ARRAYLENGTH: replaceIntegerPushInstruction(clazz, offset, simpleInstruction); break; @@ -354,15 +353,50 @@ implements AttributeVisitor, } - public void visitAnySwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SwitchInstruction switchInstruction) + public void visitTableSwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, TableSwitchInstruction tableSwitchInstruction) { // First try to simplify it to a simple branch. - replaceBranchInstruction(clazz, offset, switchInstruction); + replaceBranchInstruction(clazz, offset, tableSwitchInstruction); - // Otherwise make sure all branch targets are valid. + // Otherwise try to simplify simple enum switches. if (!codeAttributeEditor.isModified(offset)) { - replaceSwitchInstruction(clazz, offset, switchInstruction); + replaceSimpleEnumSwitchInstruction(clazz, + codeAttribute, + offset, + tableSwitchInstruction); + + // Otherwise make sure all branch targets are valid. + if (!codeAttributeEditor.isModified(offset)) + { + cleanUpSwitchInstruction(clazz, offset, tableSwitchInstruction); + + trimSwitchInstruction(clazz, offset, tableSwitchInstruction); + } + } + } + + + public void visitLookUpSwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, LookUpSwitchInstruction lookUpSwitchInstruction) + { + // First try to simplify it to a simple branch. + replaceBranchInstruction(clazz, offset, lookUpSwitchInstruction); + + // Otherwise try to simplify simple enum switches. + if (!codeAttributeEditor.isModified(offset)) + { + replaceSimpleEnumSwitchInstruction(clazz, + codeAttribute, + offset, + lookUpSwitchInstruction); + + // Otherwise make sure all branch targets are valid. + if (!codeAttributeEditor.isModified(offset)) + { + cleanUpSwitchInstruction(clazz, offset, lookUpSwitchInstruction); + + trimSwitchInstruction(clazz, offset, lookUpSwitchInstruction); + } } } @@ -824,9 +858,185 @@ implements AttributeVisitor, /** + * Replaces the given table switch instruction, if it is based on the value + * of a fixed array. This is typical for switches on simple enums. + */ + private void replaceSimpleEnumSwitchInstruction(Clazz clazz, + CodeAttribute codeAttribute, + int offset, + TableSwitchInstruction tableSwitchInstruction) + { + // Check if the switch instruction is consuming a single value loaded + // from a fully specified array. + InstructionOffsetValue producerOffsets = + partialEvaluator.getStackBefore(offset).getTopProducerValue(0).instructionOffsetValue(); + + if (producerOffsets.instructionOffsetCount() == 1) + { + int producerOffset = producerOffsets.instructionOffset(0); + + if (codeAttribute.code[producerOffset] == InstructionConstants.OP_IALOAD && + !codeAttributeEditor.isModified(producerOffset)) + { + ReferenceValue referenceValue = + partialEvaluator.getStackBefore(producerOffset).getTop(1).referenceValue(); + + if (referenceValue.isParticular()) + { + // Simplify the entire construct. + replaceSimpleEnumSwitchInstruction(clazz, + codeAttribute, + producerOffset, + offset, + tableSwitchInstruction, + referenceValue); + } + } + } + } + + + /** + * Replaces the given table switch instruction that is based on a value of + * the given fixed array. + */ + private void replaceSimpleEnumSwitchInstruction(Clazz clazz, + CodeAttribute codeAttribute, + int loadOffset, + int switchOffset, + TableSwitchInstruction tableSwitchInstruction, + ReferenceValue mappingValue) + { + ValueFactory valueFactory = new ParticularValueFactory(); + + // Transform the jump offsets. + int[] jumpOffsets = tableSwitchInstruction.jumpOffsets; + int[] newJumpOffsets = new int[mappingValue.arrayLength(valueFactory).value()]; + + for (int index = 0; index < newJumpOffsets.length; index++) + { + int switchCase = + mappingValue.integerArrayLoad(valueFactory.createIntegerValue( + index), + valueFactory).value(); + + newJumpOffsets[index] = + switchCase >= tableSwitchInstruction.lowCase && + switchCase <= tableSwitchInstruction.highCase ? + jumpOffsets[switchCase - tableSwitchInstruction.lowCase] : + tableSwitchInstruction.defaultOffset; + } + + // Update the instruction. + tableSwitchInstruction.lowCase = 0; + tableSwitchInstruction.highCase = newJumpOffsets.length - 1; + tableSwitchInstruction.jumpOffsets = newJumpOffsets; + + // Replace the original one with the new version. + replaceSimpleEnumSwitchInstruction(clazz, + loadOffset, + switchOffset, + tableSwitchInstruction); + + cleanUpSwitchInstruction(clazz, switchOffset, tableSwitchInstruction); + + trimSwitchInstruction(clazz, switchOffset, tableSwitchInstruction); + } + + + /** + * Replaces the given look up switch instruction, if it is based on the + * value of a fixed array. This is typical for switches on simple enums. + */ + private void replaceSimpleEnumSwitchInstruction(Clazz clazz, + CodeAttribute codeAttribute, + int offset, + LookUpSwitchInstruction lookupSwitchInstruction) + { + // Check if the switch instruction is consuming a single value loaded + // from a fully specified array. + InstructionOffsetValue producerOffsets = + partialEvaluator.getStackBefore(offset).getTopProducerValue(0).instructionOffsetValue(); + + if (producerOffsets.instructionOffsetCount() == 1) + { + int producerOffset = producerOffsets.instructionOffset(0); + + if (codeAttribute.code[producerOffset] == InstructionConstants.OP_IALOAD && + !codeAttributeEditor.isModified(producerOffset)) + { + ReferenceValue referenceValue = + partialEvaluator.getStackBefore(producerOffset).getTop(1).referenceValue(); + + if (referenceValue.isParticular()) + { + // Simplify the entire construct. + replaceSimpleEnumSwitchInstruction(clazz, + codeAttribute, + producerOffset, + offset, + lookupSwitchInstruction, + referenceValue); + } + } + } + } + + + /** + * Replaces the given look up switch instruction that is based on a value of + * the given fixed array. This is typical for switches on simple enums. + */ + private void replaceSimpleEnumSwitchInstruction(Clazz clazz, + CodeAttribute codeAttribute, + int loadOffset, + int switchOffset, + LookUpSwitchInstruction lookupSwitchInstruction, + ReferenceValue mappingValue) + { + ValueFactory valueFactory = new ParticularValueFactory(); + + // Transform the jump offsets. + int[] cases = lookupSwitchInstruction.cases; + int[] jumpOffsets = lookupSwitchInstruction.jumpOffsets; + int[] newJumpOffsets = new int[mappingValue.arrayLength(valueFactory).value()]; + + for (int index = 0; index < newJumpOffsets.length; index++) + { + int switchCase = + mappingValue.integerArrayLoad(valueFactory.createIntegerValue(index), + valueFactory).value(); + + int caseIndex = Arrays.binarySearch(cases, switchCase); + + newJumpOffsets[index] = caseIndex >= 0 ? + jumpOffsets[caseIndex] : + lookupSwitchInstruction.defaultOffset; + } + + // Replace the original lookup switch with a table switch. + TableSwitchInstruction replacementSwitchInstruction = + new TableSwitchInstruction(InstructionConstants.OP_TABLESWITCH, + lookupSwitchInstruction.defaultOffset, + 0, + newJumpOffsets.length - 1, + newJumpOffsets); + + replaceSimpleEnumSwitchInstruction(clazz, + loadOffset, + switchOffset, + replacementSwitchInstruction); + + cleanUpSwitchInstruction(clazz, switchOffset, replacementSwitchInstruction); + + trimSwitchInstruction(clazz, switchOffset, replacementSwitchInstruction); + } + + + /** * Makes sure all branch targets of the given switch instruction are valid. */ - private void replaceSwitchInstruction(Clazz clazz, + private void cleanUpSwitchInstruction(Clazz clazz, int offset, SwitchInstruction switchInstruction) { @@ -872,6 +1082,129 @@ implements AttributeVisitor, /** + * Trims redundant offsets from the given switch instruction. + */ + private void trimSwitchInstruction(Clazz clazz, + int offset, + TableSwitchInstruction tableSwitchInstruction) + { + // Get an offset that can serve as a valid default offset. + int defaultOffset = tableSwitchInstruction.defaultOffset; + int[] jumpOffsets = tableSwitchInstruction.jumpOffsets; + int length = jumpOffsets.length; + + // Find the lowest index with a non-default jump offset. + int lowIndex = 0; + while (lowIndex < length && + jumpOffsets[lowIndex] == defaultOffset) + { + lowIndex++; + } + + // Find the highest index with a non-default jump offset. + int highIndex = length - 1; + while (highIndex >= 0 && + jumpOffsets[highIndex] == defaultOffset) + { + highIndex--; + } + + // Can we use a shorter array? + int newLength = highIndex - lowIndex + 1; + if (newLength < length) + { + if (newLength <= 0) + { + // Replace the switch instruction by a simple branch instruction. + Instruction replacementInstruction = + new BranchInstruction(InstructionConstants.OP_GOTO, + defaultOffset); + + replaceInstruction(clazz, offset, tableSwitchInstruction, + replacementInstruction); + } + else + { + // Trim the array. + int[] newJumpOffsets = new int[newLength]; + + System.arraycopy(jumpOffsets, lowIndex, newJumpOffsets, 0, newLength); + + tableSwitchInstruction.jumpOffsets = newJumpOffsets; + tableSwitchInstruction.lowCase += lowIndex; + tableSwitchInstruction.highCase -= length - newLength - lowIndex; + + replaceInstruction(clazz, offset, tableSwitchInstruction, + tableSwitchInstruction); + } + } + } + + + /** + * Trims redundant offsets from the given switch instruction. + */ + private void trimSwitchInstruction(Clazz clazz, + int offset, + LookUpSwitchInstruction lookUpSwitchInstruction) + { + // Get an offset that can serve as a valid default offset. + int defaultOffset = lookUpSwitchInstruction.defaultOffset; + int[] jumpOffsets = lookUpSwitchInstruction.jumpOffsets; + int length = jumpOffsets.length; + int newLength = length; + + // Count the default jump offsets. + for (int index = 0; index < length; index++) + { + if (jumpOffsets[index] == defaultOffset) + { + newLength--; + } + } + + // Can we use shorter arrays? + if (newLength < length) + { + if (newLength <= 0) + { + // Replace the switch instruction by a simple branch instruction. + Instruction replacementInstruction = + new BranchInstruction(InstructionConstants.OP_GOTO, + defaultOffset); + + replaceInstruction(clazz, offset, lookUpSwitchInstruction, + replacementInstruction); + } + else + { + // Remove redundant entries from the arrays. + int[] cases = lookUpSwitchInstruction.cases; + int[] newJumpOffsets = new int[newLength]; + int[] newCases = new int[newLength]; + + int newIndex = 0; + + for (int index = 0; index < length; index++) + { + if (jumpOffsets[index] != defaultOffset) + { + newJumpOffsets[newIndex] = jumpOffsets[index]; + newCases[newIndex++] = cases[index]; + } + } + + lookUpSwitchInstruction.jumpOffsets = newJumpOffsets; + lookUpSwitchInstruction.cases = newCases; + + replaceInstruction(clazz, offset, lookUpSwitchInstruction, + lookUpSwitchInstruction); + } + } + } + + + /** * Replaces the given instruction by an infinite loop. */ private void replaceByInfiniteLoop(Clazz clazz, @@ -891,7 +1224,11 @@ implements AttributeVisitor, { // Note: we're not passing the right arguments for now, knowing that // they aren't used anyway. - instruction.accept(clazz, null, null, offset, extraInstructionVisitor); + instruction.accept(clazz, + null, + null, + offset, + extraInstructionVisitor); } } @@ -985,4 +1322,39 @@ implements AttributeVisitor, } } } + + + /** + * Replaces the simple enum switch instructions at a given offsets by a + * given replacement instruction. + */ + private void replaceSimpleEnumSwitchInstruction(Clazz clazz, + int loadOffset, + int switchOffset, + SwitchInstruction replacementSwitchInstruction) + { + if (DEBUG) System.out.println(" Replacing switch instruction at ["+switchOffset+"] -> ["+loadOffset+"] swap + pop, "+replacementSwitchInstruction.toString(switchOffset)+")"); + + // Remove the array load instruction. + codeAttributeEditor.replaceInstruction(loadOffset, new Instruction[] + { + new SimpleInstruction(InstructionConstants.OP_SWAP), + new SimpleInstruction(InstructionConstants.OP_POP), + }); + + // Replace the switch instruction. + codeAttributeEditor.replaceInstruction(switchOffset, replacementSwitchInstruction); + + // Visit the instruction, if required. + if (extraInstructionVisitor != null) + { + // Note: we're not passing the right arguments for now, knowing that + // they aren't used anyway. + replacementSwitchInstruction.accept(clazz, + null, + null, + switchOffset, + extraInstructionVisitor); + } + } } diff --git a/src/proguard/optimize/evaluation/LivenessAnalyzer.java b/src/proguard/optimize/evaluation/LivenessAnalyzer.java index 5ce8ccb..87bd4e5 100644 --- a/src/proguard/optimize/evaluation/LivenessAnalyzer.java +++ b/src/proguard/optimize/evaluation/LivenessAnalyzer.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2014 Eric Lafortune (eric@graphics.cornell.edu) * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/optimize/evaluation/LoadingInvocationUnit.java b/src/proguard/optimize/evaluation/LoadingInvocationUnit.java index d6baa67..80b4b84 100644 --- a/src/proguard/optimize/evaluation/LoadingInvocationUnit.java +++ b/src/proguard/optimize/evaluation/LoadingInvocationUnit.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2014 Eric Lafortune (eric@graphics.cornell.edu) * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -26,7 +26,7 @@ import proguard.evaluation.BasicInvocationUnit; import proguard.evaluation.value.*; /** - * This InvocationUbit loads parameter values and return values that were + * This InvocationUnit loads parameter values and return values that were * previously stored with the methods that are invoked. * * @see StoringInvocationUnit @@ -45,7 +45,7 @@ extends BasicInvocationUnit */ public LoadingInvocationUnit(ValueFactory valueFactory) { - this(valueFactory, false, false, false); + this(valueFactory, true, true, true); } @@ -80,8 +80,7 @@ extends BasicInvocationUnit { // Retrieve the stored field class value. ReferenceValue value = StoringInvocationUnit.getFieldClassValue((Field)referencedMember); - if (value != null && - value.isParticular()) + if (value != null) { return value; } @@ -104,8 +103,7 @@ extends BasicInvocationUnit { // Retrieve the stored field value. Value value = StoringInvocationUnit.getFieldValue((Field)referencedMember); - if (value != null && - value.isParticular()) + if (value != null) { return value; } @@ -126,8 +124,7 @@ extends BasicInvocationUnit { // Retrieve the stored method parameter value. Value value = StoringInvocationUnit.getMethodParameterValue(method, parameterIndex); - if (value != null && - value.isParticular()) + if (value != null) { return value; } @@ -153,8 +150,7 @@ extends BasicInvocationUnit { // Retrieve the stored method return value. Value value = StoringInvocationUnit.getMethodReturnValue((Method)referencedMember); - if (value != null && - value.isParticular()) + if (value != null) { return value; } @@ -165,31 +161,4 @@ extends BasicInvocationUnit refConstant, type); } - - -// // Small utility methods. -// -// private Value refresh(Value value) -// { -// if (value.isParticular()) -// { -// return value; -// } -// -// switch (value.computationalType()) -// { -// case Value.TYPE_INTEGER: return valueFactory.createIntegerValue(); -// case Value.TYPE_LONG: return valueFactory.createLongValue(); -// case Value.TYPE_FLOAT: return valueFactory.createFloatValue(); -// case Value.TYPE_DOUBLE: return valueFactory.createDoubleValue(); -// default: -// { -// ReferenceValue referenceValue = value.referenceValue(); -// -// return valueFactory.createReferenceValue(referenceValue.getType(), -// referenceValue.getReferencedClass(), -// referenceValue.isNull() != Value.NEVER); -// } -// } -// } } diff --git a/src/proguard/optimize/evaluation/PartialEvaluator.java b/src/proguard/optimize/evaluation/PartialEvaluator.java index 6a5bedf..0301f12 100644 --- a/src/proguard/optimize/evaluation/PartialEvaluator.java +++ b/src/proguard/optimize/evaluation/PartialEvaluator.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2014 Eric Lafortune (eric@graphics.cornell.edu) * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -96,9 +96,10 @@ implements AttributeVisitor, * during evaluation. * @param invocationUnit the invocation unit that will handle all * communication with other fields and methods. - * @param evaluateAllCode a flag that specifies whether all branch targets - * and exception handlers should be evaluated, - * even if they are unreachable. + * @param evaluateAllCode a flag that specifies whether all casts, branch + * targets, and exception handlers should be + * evaluated, even if they are unnecessary or + * unreachable. */ public PartialEvaluator(ValueFactory valueFactory, InvocationUnit invocationUnit, @@ -132,17 +133,22 @@ implements AttributeVisitor, /** * Creates a new PartialEvaluator. - * @param valueFactory the value factory that will create all - * values during evaluation. - * @param invocationUnit the invocation unit that will handle all - * communication with other fields and methods. - * @param evaluateAllCode a flag that specifies whether all branch - * targets and exception handlers should be - * evaluated, even if they are unreachable. - * @param branchUnit the branch unit that will handle all - * branches. - * @param branchTargetFinder the utility class that will find all - * branches. + * @param valueFactory the value factory that will create + * all values during evaluation. + * @param invocationUnit the invocation unit that will handle + * all communication with other fields + * and methods. + * @param evaluateAllCode a flag that specifies whether all + * casts, branch targets, and exception + * handlers should be evaluated, even + * if they are unnecessary or + * unreachable. + * @param branchUnit the branch unit that will handle all + * branches. + * @param branchTargetFinder the utility class that will find all + * branches. + * @param callingInstructionBlockStack the stack of instruction blocks to + * be evaluated */ private PartialEvaluator(ValueFactory valueFactory, InvocationUnit invocationUnit, @@ -293,7 +299,7 @@ implements AttributeVisitor, if (isTraced(offset)) { int initializationOffset = branchTargetFinder.initializationOffset(offset); - if (initializationOffset != NONE) + if (initializationOffset >= 0) { System.out.println(" is to be initialized at ["+initializationOffset+"]"); } @@ -639,7 +645,8 @@ implements AttributeVisitor, stack, valueFactory, branchUnit, - invocationUnit); + invocationUnit, + evaluateAllCode); int instructionOffset = startOffset; @@ -1055,7 +1062,7 @@ implements AttributeVisitor, //stack.push(valueFactory.createReference((ClassConstant)((ProgramClass)clazz).getConstant(exceptionInfo.u2catchType), false)); String catchClassName = catchType != 0 ? clazz.getClassName(catchType) : - ClassConstants.INTERNAL_NAME_JAVA_LANG_THROWABLE; + ClassConstants.NAME_JAVA_LANG_THROWABLE; Clazz catchClass = catchType != 0 ? ((ClassConstant)((ProgramClass)clazz).getConstant(catchType)).referencedClass : diff --git a/src/proguard/optimize/evaluation/SimpleEnumArrayPropagator.java b/src/proguard/optimize/evaluation/SimpleEnumArrayPropagator.java new file mode 100644 index 0000000..d6aaf7d --- /dev/null +++ b/src/proguard/optimize/evaluation/SimpleEnumArrayPropagator.java @@ -0,0 +1,94 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2014 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package proguard.optimize.evaluation; + +import proguard.classfile.*; +import proguard.classfile.util.SimplifiedVisitor; +import proguard.classfile.visitor.*; +import proguard.evaluation.value.*; +import proguard.optimize.info.*; + +/** + * This ClassVisitor propagates the value of the $VALUES field to the values() + * method in the simple enum classes that it visits. + * + * @see SimpleEnumMarker + * @author Eric Lafortune + */ +public class SimpleEnumArrayPropagator +extends SimplifiedVisitor +implements ClassVisitor, + MemberVisitor +{ + private final ValueFactory valueFactory = new ParticularValueFactory(); + + private Value array; + + + // Implementations for ClassVisitor. + + public void visitProgramClass(ProgramClass programClass) + { + // Update the return value of the "int[] values()" method. + programClass.methodsAccept(new MemberDescriptorFilter("()[I", this)); + } + + + // Implementations for MemberVisitor. + + public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) + { + // Find the array length of the "int[] $VALUES" field. + programClass.fieldsAccept(new MemberDescriptorFilter("[I", this)); + + if (array != null) + { + // Set the array value with the found array length. We can't use + // the original array, because its elements might get overwritten. + Value propagatedArray = + valueFactory.createArrayReferenceValue("I", + null, + array.referenceValue().arrayLength( + valueFactory)); + + setMethodReturnValue(programMethod, propagatedArray); + + array = null; + } + } + + public void visitProgramField(ProgramClass programClass, ProgramField programField) + { + array = StoringInvocationUnit.getFieldValue(programField); + } + + + // Small utility methods. + + private static void setMethodReturnValue(Method method, Value value) + { + MethodOptimizationInfo info = MethodOptimizationInfo.getMethodOptimizationInfo(method); + if (info != null) + { + info.setReturnValue(value); + } + } +} diff --git a/src/proguard/optimize/evaluation/SimpleEnumClassChecker.java b/src/proguard/optimize/evaluation/SimpleEnumClassChecker.java new file mode 100644 index 0000000..1bd5008 --- /dev/null +++ b/src/proguard/optimize/evaluation/SimpleEnumClassChecker.java @@ -0,0 +1,74 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2014 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package proguard.optimize.evaluation; + +import proguard.classfile.*; +import proguard.classfile.visitor.*; +import proguard.optimize.info.SimpleEnumMarker; + +/** + * This ClassVisitor marks all program classes that it visits as simple enums, + * if their methods qualify. + * + * @author Eric Lafortune + */ +public class SimpleEnumClassChecker +implements ClassVisitor +{ + //* + private static final boolean DEBUG = false; + /*/ + private static boolean DEBUG = System.getProperty("enum") != null; + //*/ + + + private final SimpleEnumMarker simpleEnumMarker = new SimpleEnumMarker(true); + private final MemberVisitor virtualMethodChecker = new MemberAccessFilter(0, + ClassConstants.ACC_PRIVATE | + ClassConstants.ACC_STATIC, + new MemberToClassVisitor( + new SimpleEnumMarker(false))); + + + // Implementations for ClassVisitor. + + public void visitLibraryClass(LibraryClass libraryClass) {} + + public void visitProgramClass(ProgramClass programClass) + { + // Does the class have the simple enum constructor? + if (programClass.findMethod(ClassConstants.METHOD_NAME_INIT, + ClassConstants.METHOD_TYPE_INIT_ENUM) != null) + { + if (DEBUG) + { + System.out.println("SimpleEnumClassChecker: ["+programClass.getName()+"] is a candidate simple enum, without extra fields"); + } + + // Mark it. + simpleEnumMarker.visitProgramClass(programClass); + + // However, unmark it again if it has any non-private, non-static + // methods. + programClass.methodsAccept(virtualMethodChecker); + } + } +}
\ No newline at end of file diff --git a/src/proguard/optimize/evaluation/SimpleEnumClassSimplifier.java b/src/proguard/optimize/evaluation/SimpleEnumClassSimplifier.java new file mode 100644 index 0000000..33f775f --- /dev/null +++ b/src/proguard/optimize/evaluation/SimpleEnumClassSimplifier.java @@ -0,0 +1,164 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2014 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package proguard.optimize.evaluation; + +import proguard.classfile.*; +import proguard.classfile.attribute.*; +import proguard.classfile.attribute.visitor.*; +import proguard.classfile.constant.*; +import proguard.classfile.editor.*; +import proguard.classfile.instruction.*; +import proguard.classfile.instruction.visitor.InstructionVisitor; +import proguard.classfile.util.SimplifiedVisitor; +import proguard.classfile.visitor.*; +import proguard.optimize.info.SimpleEnumMarker; +import proguard.optimize.peephole.*; + +/** + * This ClassVisitor simplifies the classes that it visits to simple enums. + * + * @see SimpleEnumMarker + * @see MemberReferenceFixer + * @author Eric Lafortune + */ +public class SimpleEnumClassSimplifier +extends SimplifiedVisitor +implements ClassVisitor, + AttributeVisitor, + InstructionVisitor +{ + //* + private static final boolean DEBUG = false; + /*/ + private static boolean DEBUG = System.getProperty("enum") != null; + //*/ + + + private static final int ENUM_CLASS_NAME = InstructionSequenceReplacer.A; + private static final int ENUM_TYPE_NAME = InstructionSequenceReplacer.B; + private static final int ENUM_CONSTANT_NAME = InstructionSequenceReplacer.X; + private static final int ENUM_CONSTANT_ORDINAL = InstructionSequenceReplacer.Y; + private static final int ENUM_CONSTANT_FIELD_NAME = InstructionSequenceReplacer.Z; + + private static final int STRING_ENUM_CONSTANT_NAME = 0; + + private static final int METHOD_ENUM_INIT = 1; + private static final int FIELD_ENUM_CONSTANT = 2; + + private static final int CLASS_ENUM = 3; + + private static final int NAME_AND_TYPE_ENUM_INIT = 4; + private static final int NAME_AND_TYPE_ENUM_CONSTANT = 5; + + private static final int UTF8_INIT = 6; + private static final int UTF8_STRING_I = 7; + + + private static final Constant[] CONSTANTS = new Constant[] + { + new StringConstant(ENUM_CONSTANT_NAME, null, null), + + new MethodrefConstant(CLASS_ENUM, NAME_AND_TYPE_ENUM_INIT, null, null), + new FieldrefConstant( CLASS_ENUM, NAME_AND_TYPE_ENUM_CONSTANT, null, null), + + new ClassConstant(ENUM_CLASS_NAME, null), + + new NameAndTypeConstant(UTF8_INIT, UTF8_STRING_I), + new NameAndTypeConstant(ENUM_CONSTANT_FIELD_NAME, ENUM_TYPE_NAME), + + new Utf8Constant(ClassConstants.METHOD_NAME_INIT), + new Utf8Constant(ClassConstants.METHOD_TYPE_INIT_ENUM), + }; + + private static final Instruction[] INSTRUCTIONS = new Instruction[] + { + new ConstantInstruction(InstructionConstants.OP_NEW, CLASS_ENUM), + new SimpleInstruction(InstructionConstants.OP_DUP), + new ConstantInstruction(InstructionConstants.OP_LDC, STRING_ENUM_CONSTANT_NAME), + new SimpleInstruction(InstructionConstants.OP_ICONST_0, ENUM_CONSTANT_ORDINAL), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_ENUM_INIT), + }; + + private static final Instruction[] REPLACEMENT_INSTRUCTIONS = new Instruction[] + { + new SimpleInstruction(InstructionConstants.OP_SIPUSH, ENUM_CONSTANT_ORDINAL), + new SimpleInstruction(InstructionConstants.OP_ICONST_1), + new SimpleInstruction(InstructionConstants.OP_IADD), + }; + + + private final CodeAttributeEditor codeAttributeEditor = + new CodeAttributeEditor(true, true); + + private final InstructionSequenceReplacer instructionSequenceReplacer = + new InstructionSequenceReplacer(CONSTANTS, + INSTRUCTIONS, + REPLACEMENT_INSTRUCTIONS, + null, + codeAttributeEditor); + + private final MemberVisitor initializerSimplifier = new AllAttributeVisitor(this); + + + // Implementations for ClassVisitor. + + public void visitProgramClass(ProgramClass programClass) + { + if (DEBUG) + { + System.out.println("SimpleEnumClassSimplifier: ["+programClass.getName()+"]"); + } + + // Unmark the class as an enum. + programClass.u2accessFlags &= ~ClassConstants.ACC_ENUM; + + // Remove the valueOf method, if present. + Method valueOfMethod = + programClass.findMethod(ClassConstants.METHOD_NAME_VALUEOF, null); + if (valueOfMethod != null) + { + new ClassEditor(programClass).removeMethod(valueOfMethod); + } + + // Simplify the static initializer. + programClass.methodAccept(ClassConstants.METHOD_NAME_CLINIT, + ClassConstants.METHOD_TYPE_CLINIT, + initializerSimplifier); + } + + + // Implementations for AttributeVisitor. + + public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} + + + public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) + { + // Set up the code attribute editor. + codeAttributeEditor.reset(codeAttribute.u4codeLength); + + // Find the peephole changes. + codeAttribute.instructionsAccept(clazz, method, instructionSequenceReplacer); + + // Apply the peephole changes. + codeAttributeEditor.visitCodeAttribute(clazz, method, codeAttribute); + } +} diff --git a/src/proguard/optimize/evaluation/SimpleEnumDescriptorSimplifier.java b/src/proguard/optimize/evaluation/SimpleEnumDescriptorSimplifier.java new file mode 100644 index 0000000..f1323ea --- /dev/null +++ b/src/proguard/optimize/evaluation/SimpleEnumDescriptorSimplifier.java @@ -0,0 +1,583 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2014 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package proguard.optimize.evaluation; + +import proguard.classfile.*; +import proguard.classfile.attribute.*; +import proguard.classfile.attribute.visitor.*; +import proguard.classfile.constant.*; +import proguard.classfile.constant.visitor.ConstantVisitor; +import proguard.classfile.editor.*; +import proguard.classfile.instruction.visitor.InstructionVisitor; +import proguard.classfile.util.*; +import proguard.classfile.visitor.*; +import proguard.optimize.info.*; + +/** + * This ClassVisitor simplifies the descriptors that contain simple enums in + * the program classes that it visits. + * + * @see SimpleEnumMarker + * @see MemberReferenceFixer + * @author Eric Lafortune + */ +public class SimpleEnumDescriptorSimplifier +extends SimplifiedVisitor +implements ClassVisitor, + ConstantVisitor, + MemberVisitor, + AttributeVisitor, + LocalVariableInfoVisitor, + LocalVariableTypeInfoVisitor +{ + //* + private static final boolean DEBUG = false; + /*/ + private static boolean DEBUG = System.getProperty("enum") != null; + //*/ + + + // Implementations for ClassVisitor. + + public void visitProgramClass(ProgramClass programClass) + { + if (DEBUG) + { + System.out.println("SimpleEnumDescriptorSimplifier: "+programClass.getName()); + } + + // Simplify the class members. + programClass.fieldsAccept(this); + programClass.methodsAccept(this); + + // Simplify the attributes. + //programClass.attributesAccept(this); + + // Simplify the simple enum array constants. + programClass.constantPoolEntriesAccept(this); + } + + + // Implementations for ConstantVisitor. + + public void visitAnyConstant(Clazz clazz, Constant constant) {} + + + public void visitStringConstant(Clazz clazz, StringConstant stringConstant) + { + // Does the constant refer to a simple enum type? + Clazz referencedClass = stringConstant.referencedClass; + if (isSimpleEnum(referencedClass)) + { + // Is it an array type? + String name = stringConstant.getString(clazz); + if (ClassUtil.isInternalArrayType(name)) + { + // Update the type. + ConstantPoolEditor constantPoolEditor = + new ConstantPoolEditor((ProgramClass)clazz); + + String newName = simplifyDescriptor(name, referencedClass); + + stringConstant.u2stringIndex = + constantPoolEditor.addUtf8Constant(newName); + + // Clear the referenced class. + stringConstant.referencedClass = null; + } + } + } + + + public void visitInvokeDynamicConstant(Clazz clazz, InvokeDynamicConstant invokeDynamicConstant) + { + // Update the descriptor if it has any simple enum classes. + String descriptor = invokeDynamicConstant.getType(clazz); + String newDescriptor = simplifyDescriptor(descriptor, invokeDynamicConstant.referencedClasses); + + if (!descriptor.equals(newDescriptor)) + { + // Update the descriptor. + ConstantPoolEditor constantPoolEditor = + new ConstantPoolEditor((ProgramClass)clazz); + + invokeDynamicConstant.u2nameAndTypeIndex = + constantPoolEditor.addNameAndTypeConstant(invokeDynamicConstant.getName(clazz), + newDescriptor); + + // Update the referenced classes. + invokeDynamicConstant.referencedClasses = + simplifyReferencedClasses(descriptor, invokeDynamicConstant.referencedClasses); + } + } + + + public void visitClassConstant(Clazz clazz, ClassConstant classConstant) + { + // Does the constant refer to a simple enum type? + Clazz referencedClass = classConstant.referencedClass; + if (isSimpleEnum(referencedClass)) + { + // Is it an array type? + String name = classConstant.getName(clazz); + if (ClassUtil.isInternalArrayType(name)) + { + // Update the type. + ConstantPoolEditor constantPoolEditor = + new ConstantPoolEditor((ProgramClass)clazz); + + String newName = simplifyDescriptor(name, referencedClass); + + classConstant.u2nameIndex = + constantPoolEditor.addUtf8Constant(newName); + + // Clear the referenced class. + classConstant.referencedClass = null; + } + } + } + + + // Implementations for MemberVisitor. + + public void visitProgramField(ProgramClass programClass, ProgramField programField) + { + // Update the descriptor if it has a simple enum class. + String descriptor = programField.getDescriptor(programClass); + String newDescriptor = simplifyDescriptor(descriptor, programField.referencedClass); + + if (!descriptor.equals(newDescriptor)) + { + String name = programField.getName(programClass); + String newName = name + ClassConstants.SPECIAL_MEMBER_SEPARATOR + Long.toHexString(Math.abs((descriptor).hashCode())); + + if (DEBUG) + { + System.out.println("SimpleEnumDescriptorSimplifier: ["+programClass.getName()+"."+name+" "+descriptor + "] -> ["+newName+" "+newDescriptor+"]"); + } + + ConstantPoolEditor constantPoolEditor = + new ConstantPoolEditor(programClass); + + // Update the name. + programField.u2nameIndex = + constantPoolEditor.addUtf8Constant(newName); + + // Update the descriptor itself. + programField.u2descriptorIndex = + constantPoolEditor.addUtf8Constant(newDescriptor); + + // Clear the referenced class. + programField.referencedClass = null; + + // Clear the field value. + FieldOptimizationInfo fieldOptimizationInfo = + FieldOptimizationInfo.getFieldOptimizationInfo(programField); + if (fieldOptimizationInfo != null) + { + fieldOptimizationInfo.resetValue(programClass, programField); + } + + // Simplify the signature. + programField.attributesAccept(programClass, this); + } + } + + + public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) + { +// // Skip the valueOf method. +// if (programMethod.getName(programClass).equals(ClassConstants.METHOD_NAME_VALUEOF)) +// { +// return; +// } + + // Simplify the code, the signature, and the parameter annotations, + // before simplifying the descriptor. + programMethod.attributesAccept(programClass, this); + + // Update the descriptor if it has any simple enum classes. + String descriptor = programMethod.getDescriptor(programClass); + String newDescriptor = simplifyDescriptor(descriptor, programMethod.referencedClasses); + + if (!descriptor.equals(newDescriptor)) + { + String name = programMethod.getName(programClass); + String newName = name; + + // Append a code, if the method isn't a class instance initializer. + if (!name.equals(ClassConstants.METHOD_NAME_INIT)) + { + newName += ClassConstants.SPECIAL_MEMBER_SEPARATOR + Long.toHexString(Math.abs((descriptor).hashCode())); + } + + if (DEBUG) + { + System.out.println("SimpleEnumDescriptorSimplifier: ["+programClass.getName()+"."+name+descriptor+"] -> ["+newName+newDescriptor+"]"); + } + + ConstantPoolEditor constantPoolEditor = + new ConstantPoolEditor(programClass); + + // Update the name, if necessary. + if (!newName.equals(name)) + { + programMethod.u2nameIndex = + constantPoolEditor.addUtf8Constant(newName); + } + + // Update the descriptor itself. + programMethod.u2descriptorIndex = + constantPoolEditor.addUtf8Constant(newDescriptor); + + // Update the referenced classes. + programMethod.referencedClasses = + simplifyReferencedClasses(descriptor, programMethod.referencedClasses); + } + } + + + // Implementations for AttributeVisitor. + + public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} + + + public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) + { + // Simplify the local variable descriptors. + codeAttribute.attributesAccept(clazz, method, this); + } + + + public void visitLocalVariableTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTableAttribute localVariableTableAttribute) + { + // Change the references of the local variables. + localVariableTableAttribute.localVariablesAccept(clazz, method, codeAttribute, this); + } + + + public void visitLocalVariableTypeTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeTableAttribute localVariableTypeTableAttribute) + { + // Change the references of the local variables. + localVariableTypeTableAttribute.localVariablesAccept(clazz, method, codeAttribute, this); + } + + + public void visitSignatureAttribute(Clazz clazz, Field field, SignatureAttribute signatureAttribute) + { + // We're only looking at the base type for now. + if (signatureAttribute.referencedClasses != null && + signatureAttribute.referencedClasses.length > 0) + { + // Update the signature if it has any simple enum classes. + String signature = signatureAttribute.getSignature(clazz); + String newSignature = simplifyDescriptor(signature, + signatureAttribute.referencedClasses[0]); + + if (!signature.equals(newSignature)) + { + // Update the signature. + signatureAttribute.u2signatureIndex = + new ConstantPoolEditor((ProgramClass)clazz).addUtf8Constant(newSignature); + + // Clear the referenced class. + signatureAttribute.referencedClasses[0] = null; + } + } + } + + + public void visitSignatureAttribute(Clazz clazz, Method method, SignatureAttribute signatureAttribute) + { + // Compute the new signature. + String signature = signatureAttribute.getSignature(clazz); + String newSignature = simplifyDescriptor(signature, + signatureAttribute.referencedClasses); + + if (!signature.equals(newSignature)) + { + // Update the signature. + signatureAttribute.u2signatureIndex = + new ConstantPoolEditor((ProgramClass)clazz).addUtf8Constant(newSignature); + } + } + + + // Implementations for LocalVariableInfoVisitor. + + public void visitLocalVariableInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableInfo localVariableInfo) + { + // Update the descriptor if it has a simple enum class. + String descriptor = localVariableInfo.getDescriptor(clazz); + String newDescriptor = simplifyDescriptor(descriptor, localVariableInfo.referencedClass); + + if (!descriptor.equals(newDescriptor)) + { + // Update the descriptor. + localVariableInfo.u2descriptorIndex = + new ConstantPoolEditor((ProgramClass)clazz).addUtf8Constant(newDescriptor); + + // Clear the referenced class. + localVariableInfo.referencedClass = null; + } + } + + + // Implementations for LocalVariableTypeInfoVisitor. + + public void visitLocalVariableTypeInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeInfo localVariableTypeInfo) + { + // We're only looking at the base type for now. + if (localVariableTypeInfo.referencedClasses != null && + localVariableTypeInfo.referencedClasses.length > 0) + { + // Update the signature if it has any simple enum classes. + String signature = localVariableTypeInfo.getSignature(clazz); + String newSignature = simplifyDescriptor(signature, + localVariableTypeInfo.referencedClasses[0]); + + if (!signature.equals(newSignature)) + { + // Update the signature. + localVariableTypeInfo.u2signatureIndex = + new ConstantPoolEditor((ProgramClass)clazz).addUtf8Constant(newSignature); + + // Clear the referenced class. + localVariableTypeInfo.referencedClasses[0] = null; + } + } + } + + + // Small utility methods. + + /** + * Returns the descriptor with simplified enum type. + */ + private String simplifyDescriptor(String descriptor, + Clazz referencedClass) + { + return isSimpleEnum(referencedClass) ? + descriptor.substring(0, ClassUtil.internalArrayTypeDimensionCount(descriptor)) + ClassConstants.TYPE_INT : + descriptor; + } + + + /** + * Returns the descriptor with simplified enum types. + */ + private String simplifyDescriptor(String descriptor, + Clazz[] referencedClasses) + { + if (referencedClasses != null) + { + InternalTypeEnumeration internalTypeEnumeration = + new InternalTypeEnumeration(descriptor); + + int referencedClassIndex = 0; + + StringBuffer newDescriptorBuffer = + new StringBuffer(descriptor.length()); + + // Go over the formal type parameters. + { + // Consider the classes referenced by this formal type + // parameter. + String type = internalTypeEnumeration.formalTypeParameters(); + int count = new DescriptorClassEnumeration(type).classCount(); + + // Replace simple enum types. + for (int counter = 0; counter < count; counter++) + { + Clazz referencedClass = + referencedClasses[referencedClassIndex++]; + + if (isSimpleEnum(referencedClass)) + { + // Let's replace the simple enum type by + // java.lang.Integer. + type = + type.substring(0, ClassUtil.internalArrayTypeDimensionCount(type)) + + ClassConstants.NAME_JAVA_LANG_INTEGER; + } + } + + newDescriptorBuffer.append(type); + } + + newDescriptorBuffer.append(ClassConstants.METHOD_ARGUMENTS_OPEN); + + // Go over the parameters. + while (internalTypeEnumeration.hasMoreTypes()) + { + // Consider the classes referenced by this parameter type. + String type = internalTypeEnumeration.nextType(); + int count = new DescriptorClassEnumeration(type).classCount(); + + // Replace simple enum types. + for (int counter = 0; counter < count; counter++) + { + Clazz referencedClass = + referencedClasses[referencedClassIndex++]; + + if (isSimpleEnum(referencedClass)) + { + type = + type.substring(0, ClassUtil.internalArrayTypeDimensionCount(type)) + + ClassConstants.TYPE_INT; + } + } + + newDescriptorBuffer.append(type); + } + + newDescriptorBuffer.append(ClassConstants.METHOD_ARGUMENTS_CLOSE); + + // Go over the return value. + { + String type = internalTypeEnumeration.returnType(); + int count = new DescriptorClassEnumeration(type).classCount(); + + // Replace simple enum types. + for (int counter = 0; counter < count; counter++) + { + Clazz referencedClass = + referencedClasses[referencedClassIndex++]; + + if (isSimpleEnum(referencedClass)) + { + type = + type.substring(0, ClassUtil.internalArrayTypeDimensionCount(type)) + + ClassConstants.TYPE_INT; + } + } + + newDescriptorBuffer.append(type); + } + + descriptor = newDescriptorBuffer.toString(); + } + + return descriptor; + } + + + /** + * Returns the simplified and shrunk array of referenced classes for the + * given descriptor. + */ + private Clazz[] simplifyReferencedClasses(String descriptor, + Clazz[] referencedClasses) + { + if (referencedClasses != null) + { + InternalTypeEnumeration internalTypeEnumeration = + new InternalTypeEnumeration(descriptor); + + int referencedClassIndex = 0; + int newReferencedClassIndex = 0; + + // Go over the formal type parameters. + { + String type = internalTypeEnumeration.formalTypeParameters(); + int count = new DescriptorClassEnumeration(type).classCount(); + + // Clear all non-simple enum classes + // (now java.lang.Integer). + for (int counter = 0; counter < count; counter++) + { + Clazz referencedClass = + referencedClasses[referencedClassIndex++]; + + referencedClasses[newReferencedClassIndex++] = + isSimpleEnum(referencedClass) ? null : referencedClass; + } + } + + // Go over the parameters. + while (internalTypeEnumeration.hasMoreTypes()) + { + // Consider the classes referenced by this parameter type. + String type = internalTypeEnumeration.nextType(); + int count = new DescriptorClassEnumeration(type).classCount(); + + // Copy all non-simple enum classes. + for (int counter = 0; counter < count; counter++) + { + Clazz referencedClass = + referencedClasses[referencedClassIndex++]; + + if (!isSimpleEnum(referencedClass)) + { + referencedClasses[newReferencedClassIndex++] = + referencedClass; + } + } + } + + // Go over the return type. + { + String type = internalTypeEnumeration.returnType(); + int count = new DescriptorClassEnumeration(type).classCount(); + + // Copy all non-simple enum classes. + for (int counter = 0; counter < count; counter++) + { + Clazz referencedClass = + referencedClasses[referencedClassIndex++]; + + if (!isSimpleEnum(referencedClass)) + { + referencedClasses[newReferencedClassIndex++] = + referencedClass; + } + } + } + + // Shrink the array to the proper size. + if (newReferencedClassIndex == 0) + { + referencedClasses = null; + } + else if (newReferencedClassIndex < referencedClassIndex) + { + Clazz[] newReferencedClasses = new Clazz[newReferencedClassIndex]; + System.arraycopy(referencedClasses, 0, + newReferencedClasses, 0, + newReferencedClassIndex); + + referencedClasses = newReferencedClasses; + } + } + + return referencedClasses; + } + + + /** + * Returns whether the given class is not null and a simple enum class. + */ + private boolean isSimpleEnum(Clazz clazz) + { + return clazz != null && + SimpleEnumMarker.isSimpleEnum(clazz); + } +} diff --git a/src/proguard/optimize/evaluation/SimpleEnumUseChecker.java b/src/proguard/optimize/evaluation/SimpleEnumUseChecker.java new file mode 100644 index 0000000..b748c68 --- /dev/null +++ b/src/proguard/optimize/evaluation/SimpleEnumUseChecker.java @@ -0,0 +1,760 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2014 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package proguard.optimize.evaluation; + +import proguard.classfile.*; +import proguard.classfile.attribute.*; +import proguard.classfile.attribute.visitor.*; +import proguard.classfile.constant.ClassConstant; +import proguard.classfile.constant.visitor.ConstantVisitor; +import proguard.classfile.instruction.*; +import proguard.classfile.instruction.visitor.InstructionVisitor; +import proguard.classfile.util.*; +import proguard.classfile.visitor.*; +import proguard.evaluation.*; +import proguard.evaluation.value.*; +import proguard.optimize.info.SimpleEnumMarker; + +/** + * This ClassVisitor marks enums that can't be simplified due to the way they + * are used in the classes that it visits. + * + * @see SimpleEnumMarker + * @author Eric Lafortune + */ +public class SimpleEnumUseChecker +extends SimplifiedVisitor +implements ClassVisitor, + MemberVisitor, + AttributeVisitor, + InstructionVisitor, + ConstantVisitor, + ParameterVisitor +{ + //* + private static final boolean DEBUG = false; + /*/ + private static boolean DEBUG = System.getProperty("enum") != null; + //*/ + + private final PartialEvaluator partialEvaluator; + private final MemberVisitor methodCodeChecker = new AllAttributeVisitor(this); + private final ConstantVisitor invokedMethodChecker = new ReferencedMemberVisitor(this); + private final ConstantVisitor parameterChecker = new ReferencedMemberVisitor(new AllParameterVisitor(this)); + private final ClassVisitor complexEnumMarker = new SimpleEnumMarker(false); + private final ReferencedClassVisitor referencedComplexEnumMarker = new ReferencedClassVisitor(complexEnumMarker); + + + // Fields acting as parameters and return values for the visitor methods. + private int invocationOffset; + + + /** + * Creates a new SimpleEnumUseSimplifier. + */ + public SimpleEnumUseChecker() + { + this(new PartialEvaluator()); + } + + + /** + * Creates a new SimpleEnumUseChecker. + * @param partialEvaluator the partial evaluator that will execute the code + * and provide information about the results. + */ + public SimpleEnumUseChecker(PartialEvaluator partialEvaluator) + { + this.partialEvaluator = partialEvaluator; + } + + + // Implementations for ClassVisitor. + + public void visitProgramClass(ProgramClass programClass) + { + if ((programClass.getAccessFlags() & ClassConstants.ACC_ANNOTATTION) != 0) + { + // Unmark the simple enum classes in annotations. + programClass.methodsAccept(referencedComplexEnumMarker); + } + else + { + // Unmark the simple enum classes that are used in a complex way. + programClass.methodsAccept(methodCodeChecker); + } + } + + + // Implementations for AttributeVisitor. + + public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} + + + public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) + { + // Evaluate the method. + partialEvaluator.visitCodeAttribute(clazz, method, codeAttribute); + + int codeLength = codeAttribute.u4codeLength; + + // Check all traced instructions. + for (int offset = 0; offset < codeLength; offset++) + { + if (partialEvaluator.isTraced(offset)) + { + Instruction instruction = InstructionFactory.create(codeAttribute.code, + offset); + + instruction.accept(clazz, method, codeAttribute, offset, this); + + // Check generalized stacks and variables at branch targets. + if (partialEvaluator.isBranchOrExceptionTarget(offset)) + { + checkMixedStackEntriesBefore(offset); + + checkMixedVariablesBefore(offset); + } + } + } + } + + + // Implementations for InstructionVisitor. + + public void visitSimpleInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SimpleInstruction simpleInstruction) + { + switch (simpleInstruction.opcode) + { + case InstructionConstants.OP_AASTORE: + { + // Check if the instruction is storing a simple enum in a + // more general array. + if (!isPoppingSimpleEnumType(offset, 2)) + { + if (DEBUG) + { + if (isPoppingSimpleEnumType(offset)) + { + System.out.println("SimpleEnumUseChecker: ["+clazz.getName()+"."+method.getName(clazz)+method.getDescriptor(clazz)+"] stores enum ["+ + partialEvaluator.getStackBefore(offset).getTop(0).referenceValue().getType()+"] in more general array ["+ + partialEvaluator.getStackBefore(offset).getTop(2).referenceValue().getType()+"]"); + } + } + + markPoppedComplexEnumType(offset); + } + break; + } + case InstructionConstants.OP_ARETURN: + { + // Check if the instruction is returning a simple enum as a + // more general type. + if (!isReturningSimpleEnumType(clazz, method)) + { + if (DEBUG) + { + if (isPoppingSimpleEnumType(offset)) + { + System.out.println("SimpleEnumUseChecker: ["+clazz.getName()+"."+method.getName(clazz)+method.getDescriptor(clazz)+"] returns enum [" + + partialEvaluator.getStackBefore(offset).getTop(0).referenceValue().getType()+"] as more general type"); + } + } + + markPoppedComplexEnumType(offset); + } + break; + } + case InstructionConstants.OP_MONITORENTER: + case InstructionConstants.OP_MONITOREXIT: + { + // Make sure the popped type is not a simple enum type. + if (DEBUG) + { + if (isPoppingSimpleEnumType(offset)) + { + System.out.println("SimpleEnumUseChecker: ["+clazz.getName()+"."+method.getName(clazz)+method.getDescriptor(clazz)+"] uses enum ["+ + partialEvaluator.getStackBefore(offset).getTop(0).referenceValue().getType()+"] as monitor"); + } + } + + markPoppedComplexEnumType(offset); + + break; + } + } + } + + + public void visitVariableInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VariableInstruction variableInstruction) + { + } + + + public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction) + { + switch (constantInstruction.opcode) + { + case InstructionConstants.OP_PUTSTATIC: + case InstructionConstants.OP_PUTFIELD: + { + // Check if the instruction is generalizing a simple enum to a + // different type. + invocationOffset = offset; + clazz.constantPoolEntryAccept(constantInstruction.constantIndex, + parameterChecker); + break; + } + case InstructionConstants.OP_INVOKEVIRTUAL: + { + // Check if the instruction is calling a simple enum. + String invokedMethodName = + clazz.getRefName(constantInstruction.constantIndex); + String invokedMethodType = + clazz.getRefType(constantInstruction.constantIndex); + int stackEntryIndex = + ClassUtil.internalMethodParameterSize(invokedMethodType); + if (isPoppingSimpleEnumType(offset, stackEntryIndex) && + !isSupportedMethod(invokedMethodName, + invokedMethodType)) + { + if (DEBUG) + { + System.out.println("SimpleEnumUseChecker: ["+clazz.getName()+"."+method.getName(clazz)+method.getDescriptor(clazz)+"] calls ["+partialEvaluator.getStackBefore(offset).getTop(stackEntryIndex).referenceValue().getType()+"."+invokedMethodName+"]"); + } + + markPoppedComplexEnumType(offset, stackEntryIndex); + } + + // Check if any of the parameters is generalizing a simple + // enum to a different type. + invocationOffset = offset; + clazz.constantPoolEntryAccept(constantInstruction.constantIndex, + parameterChecker); + break; + } + case InstructionConstants.OP_INVOKESPECIAL: + case InstructionConstants.OP_INVOKESTATIC: + case InstructionConstants.OP_INVOKEINTERFACE: + { + // Check if it is calling a method that we can't simplify. + clazz.constantPoolEntryAccept(constantInstruction.constantIndex, + invokedMethodChecker); + + // Check if any of the parameters is generalizing a simple + // enum to a different type. + invocationOffset = offset; + clazz.constantPoolEntryAccept(constantInstruction.constantIndex, + parameterChecker); + break; + } + case InstructionConstants.OP_CHECKCAST: + case InstructionConstants.OP_INSTANCEOF: + { + // Check if the instruction is popping a different type. + if (!isPoppingExpectedType(offset, + clazz, + constantInstruction.constantIndex)) + { + if (DEBUG) + { + if (isPoppingSimpleEnumType(offset)) + { + System.out.println("SimpleEnumUseChecker: ["+clazz.getName()+"."+method.getName(clazz)+method.getDescriptor(clazz)+"] is casting or checking ["+ + partialEvaluator.getStackBefore(offset).getTop(0).referenceValue().getType()+"] as ["+ + clazz.getClassName(constantInstruction.constantIndex)+"]"); + } + } + + // Make sure the popped type is not a simple enum type. + markPoppedComplexEnumType(offset); + + // Make sure the checked type is not a simple enum type. + // We're somewhat arbitrarily skipping casts in static + // methods of simple enum classes, because they do occur + // in values() and valueOf(String), without obstructing + // simplification. + if (!isSimpleEnum(clazz) || + (method.getAccessFlags() & ClassConstants.ACC_STATIC) == 0 || + constantInstruction.opcode != InstructionConstants.OP_CHECKCAST) + { + if (DEBUG) + { + if (isSimpleEnum(((ClassConstant)((ProgramClass)clazz).getConstant(constantInstruction.constantIndex)).referencedClass)) + { + System.out.println("SimpleEnumUseChecker: ["+clazz.getName()+"."+method.getName(clazz)+method.getDescriptor(clazz)+"] is casting or checking ["+ + partialEvaluator.getStackBefore(offset).getTop(0).referenceValue().getType()+"] as ["+ + clazz.getClassName(constantInstruction.constantIndex)+"]"); + } + } + + markConstantComplexEnumType(clazz, constantInstruction.constantIndex); + } + } + break; + } + } + } + + + public void visitBranchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, BranchInstruction branchInstruction) + { + switch (branchInstruction.opcode) + { + case InstructionConstants.OP_IFACMPEQ: + case InstructionConstants.OP_IFACMPNE: + { + // Check if the instruction is comparing different types. + if (!isPoppingIdenticalTypes(offset, 0, 1)) + { + if (DEBUG) + { + if (isPoppingSimpleEnumType(offset, 0)) + { + System.out.println("SimpleEnumUseChecker: ["+clazz.getName()+"."+method.getName(clazz)+method.getDescriptor(clazz)+"] compares ["+partialEvaluator.getStackBefore(offset).getTop(0).referenceValue().getType()+"] to plain type"); + } + + if (isPoppingSimpleEnumType(offset, 1)) + { + System.out.println("SimpleEnumUseChecker: ["+clazz.getName()+"."+method.getName(clazz)+method.getDescriptor(clazz)+"] compares ["+partialEvaluator.getStackBefore(offset).getTop(1).referenceValue().getType()+"] to plain type"); + } + } + + // Make sure the first popped type is not a simple enum type. + markPoppedComplexEnumType(offset, 0); + + // Make sure the second popped type is not a simple enum type. + markPoppedComplexEnumType(offset, 1); + } + break; + } + } + } + + + public void visitAnySwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SwitchInstruction switchInstruction) + { + } + + + // Implementations for MemberVisitor. + + public void visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryMethod) {} + + + public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) + { + if (isSimpleEnum(programClass) && + isUnsupportedMethod(programMethod.getName(programClass), + programMethod.getDescriptor(programClass))) + { + if (DEBUG) + { + System.out.println("SimpleEnumUseChecker: invocation of ["+programClass.getName()+"."+programMethod.getName(programClass)+programMethod.getDescriptor(programClass)+"]"); + } + + complexEnumMarker.visitProgramClass(programClass); + } + } + + + // Implementations for ParameterVisitor. + + public void visitParameter(Clazz clazz, Member member, int parameterIndex, int parameterCount, int parameterOffset, int parameterSize, String parameterType, Clazz referencedClass) + { + // Check if the parameter is passing a simple enum as a more general + // type. + int stackEntryIndex = parameterSize - parameterOffset - 1; + if (ClassUtil.isInternalClassType(parameterType) && + !isPoppingExpectedType(invocationOffset, stackEntryIndex, + ClassUtil.isInternalArrayType(parameterType) ? + parameterType : + ClassUtil.internalClassNameFromClassType(parameterType))) + { + if (DEBUG) + { + ReferenceValue poppedValue = + partialEvaluator.getStackBefore(invocationOffset).getTop(stackEntryIndex).referenceValue(); + if (isSimpleEnumType(poppedValue)) + { + System.out.println("SimpleEnumUseChecker: ["+poppedValue.getType()+"] "+ + (member instanceof Field ? + ("is stored as more general type ["+parameterType+"] in field ["+clazz.getName()+"."+member.getName(clazz)+"]") : + ("is passed as more general argument #"+parameterIndex+" ["+parameterType+"] to ["+clazz.getName()+"."+member.getName(clazz)+"]"))); + } + } + + // Make sure the popped type is not a simple enum type. + markPoppedComplexEnumType(invocationOffset, stackEntryIndex); + } + } + + + // Small utility methods. + + /** + * Returns whether the specified enum method is supported for simple enums. + */ + private boolean isSupportedMethod(String name, String type) + { + return + name.equals(ClassConstants.METHOD_NAME_ORDINAL) && + type.equals(ClassConstants.METHOD_TYPE_ORDINAL) || + + name.equals(ClassConstants.METHOD_NAME_CLONE) && + type.equals(ClassConstants.METHOD_TYPE_CLONE); + } + + + /** + * Returns whether the specified enum method is unsupported for simple enums. + */ + private boolean isUnsupportedMethod(String name, String type) + { + return + name.equals(ClassConstants.METHOD_NAME_VALUEOF); + } + + + /** + * Unmarks simple enum classes that are mixed with incompatible reference + * types in the stack before the given instruction offset. + */ + private void checkMixedStackEntriesBefore(int offset) + { + TracedStack stackBefore = partialEvaluator.getStackBefore(offset); + + // Check all stack entries. + int stackSize = stackBefore.size(); + + for (int stackEntryIndex = 0; stackEntryIndex < stackSize; stackEntryIndex++) + { + // Check reference entries. + Value stackEntry = stackBefore.getBottom(stackEntryIndex); + if (stackEntry.computationalType() == Value.TYPE_REFERENCE) + { + // Check reference entries with multiple producers. + InstructionOffsetValue producerOffsets = + stackBefore.getBottomActualProducerValue(stackEntryIndex).instructionOffsetValue(); + + int producerCount = producerOffsets.instructionOffsetCount(); + if (producerCount > 1) + { + // Is the consumed stack entry not a simple enum? + ReferenceValue consumedStackEntry = + stackEntry.referenceValue(); + + if (!isSimpleEnumType(consumedStackEntry)) + { + // Check all producers. + for (int producerIndex = 0; producerIndex < producerCount; producerIndex++) + { + int producerOffset = + producerOffsets.instructionOffset(producerIndex); + + if (producerOffset >= 0) + { + if (DEBUG) + { + ReferenceValue producedValue = + partialEvaluator.getStackAfter(producerOffset).getTop(0).referenceValue(); + if (isSimpleEnumType(producedValue)) + { + System.out.println("SimpleEnumUseChecker: ["+producedValue.getType()+"] mixed with general type on stack"); + } + } + + // Make sure the produced stack entry isn't a + // simple enum either. + markPushedComplexEnumType(producerOffset); + } + } + } + } + } + } + } + + + /** + * Unmarks simple enum classes that are mixed with incompatible reference + * types in the variables before the given instruction offset. + */ + private void checkMixedVariablesBefore(int offset) + { + TracedVariables variablesBefore = + partialEvaluator.getVariablesBefore(offset); + + // Check all variables. + int variablesSize = variablesBefore.size(); + + for (int variableIndex = 0; variableIndex < variablesSize; variableIndex++) + { + // Check reference variables. + Value variable = variablesBefore.getValue(variableIndex); + if (variable != null && + variable.computationalType() == Value.TYPE_REFERENCE) + { + // Check reference variables with multiple producers. + InstructionOffsetValue producerOffsets = + variablesBefore.getProducerValue(variableIndex).instructionOffsetValue(); + + int producerCount = producerOffsets.instructionOffsetCount(); + if (producerCount > 1) + { + // Is the consumed variable not a simple enum? + ReferenceValue consumedVariable = + variable.referenceValue(); + + if (!isSimpleEnumType(consumedVariable)) + { + // Check all producers. + for (int producerIndex = 0; producerIndex < producerCount; producerIndex++) + { + int producerOffset = + producerOffsets.instructionOffset(producerIndex); + + if (producerOffset >= 0) + { + if (DEBUG) + { + ReferenceValue producedValue = + partialEvaluator.getVariablesAfter(producerOffset).getValue(variableIndex).referenceValue(); + if (isSimpleEnumType(producedValue)) + { + System.out.println("SimpleEnumUseChecker: ["+producedValue.getType()+"] mixed with general type in variables"); + } + } + + // Make sure the stored variable entry isn't a + // simple enum either. + markStoredComplexEnumType(producerOffset, variableIndex); + } + } + } + } + } + } + } + + + /** + * Returns whether the instruction at the given offset is popping two + * identical reference types. + */ + private boolean isPoppingIdenticalTypes(int offset, + int stackEntryIndex1, + int stackEntryIndex2) + { + TracedStack stackBefore = partialEvaluator.getStackBefore(offset); + + String type1 = + stackBefore.getTop(stackEntryIndex1).referenceValue().getType(); + String type2 = + stackBefore.getTop(stackEntryIndex2).referenceValue().getType(); + + return type1 == null ? type2 == null : type1.equals(type2); + } + + + /** + * Returns whether the instruction at the given offset is popping exactly + * the reference type of the specified class constant. + */ + private boolean isPoppingExpectedType(int offset, + Clazz clazz, + int constantIndex) + { + return isPoppingExpectedType(offset, 0, clazz, constantIndex); + } + + + /** + * Returns whether the instruction at the given offset is popping exactly + * the reference type of the specified class constant. + */ + private boolean isPoppingExpectedType(int offset, + int stackEntryIndex, + Clazz clazz, + int constantIndex) + { + return isPoppingExpectedType(offset, + stackEntryIndex, + clazz.getClassName(constantIndex)); + } + + + /** + * Returns whether the instruction at the given offset is popping exactly + * the given reference type. + */ + private boolean isPoppingExpectedType(int offset, + int stackEntryIndex, + String expectedType) + { + TracedStack stackBefore = partialEvaluator.getStackBefore(offset); + + String poppedType = + stackBefore.getTop(stackEntryIndex).referenceValue().getType(); + + return expectedType.equals(poppedType); + } + + + /** + * Returns whether the given method is returning a simple enum type. + * This includes simple enum arrays. + */ + private boolean isReturningSimpleEnumType(Clazz clazz, Method method) + { + String descriptor = method.getDescriptor(clazz); + String returnType = ClassUtil.internalMethodReturnType(descriptor); + + if (ClassUtil.isInternalClassType(returnType)) + { + Clazz[] referencedClasses = + ((ProgramMethod)method).referencedClasses; + + if (referencedClasses != null) + { + int returnedClassIndex = + new DescriptorClassEnumeration(descriptor).classCount() - 1; + + Clazz returnedClass = referencedClasses[returnedClassIndex]; + + return isSimpleEnum(returnedClass); + } + } + + return false; + } + + + /** + * Returns whether the instruction at the given offset is popping a type + * with a simple enum class. This includes simple enum arrays. + */ + private boolean isPoppingSimpleEnumType(int offset) + { + return isPoppingSimpleEnumType(offset, 0); + } + + + /** + * Returns whether the instruction at the given offset is popping a type + * with a simple enum class. This includes simple enum arrays. + */ + private boolean isPoppingSimpleEnumType(int offset, int stackEntryIndex) + { + ReferenceValue referenceValue = + partialEvaluator.getStackBefore(offset).getTop(stackEntryIndex).referenceValue(); + + return isSimpleEnumType(referenceValue); + } + + + /** + * Returns whether the given value is a simple enum type. This includes + * simple enum arrays. + */ + private boolean isSimpleEnumType(ReferenceValue referenceValue) + { + return isSimpleEnum(referenceValue.getReferencedClass()); + } + + + /** + * Returns whether the given class is not null and a simple enum class. + */ + private boolean isSimpleEnum(Clazz clazz) + { + return clazz != null && + SimpleEnumMarker.isSimpleEnum(clazz); + } + + + /** + * Marks the enum class of the popped type as complex. + */ + private void markConstantComplexEnumType(Clazz clazz, int constantIndex) + { + clazz.constantPoolEntryAccept(constantIndex, + referencedComplexEnumMarker); + } + + + /** + * Marks the enum class of the popped type as complex. + */ + private void markPoppedComplexEnumType(int offset) + { + markPoppedComplexEnumType(offset, 0); + } + + + /** + * Marks the enum class of the specified popped type as complex. + */ + private void markPoppedComplexEnumType(int offset, int stackEntryIndex) + { + ReferenceValue referenceValue = + partialEvaluator.getStackBefore(offset).getTop(stackEntryIndex).referenceValue(); + + markComplexEnumType(referenceValue); + } + + + /** + * Marks the enum class of the specified pushed type as complex. + */ + private void markPushedComplexEnumType(int offset) + { + ReferenceValue referenceValue = + partialEvaluator.getStackAfter(offset).getTop(0).referenceValue(); + + markComplexEnumType(referenceValue); + } + + + /** + * Marks the enum class of the specified stored type as complex. + */ + private void markStoredComplexEnumType(int offset, int variableIndex) + { + ReferenceValue referenceValue = + partialEvaluator.getVariablesAfter(offset).getValue(variableIndex).referenceValue(); + + markComplexEnumType(referenceValue); + } + + + /** + * Marks the enum class of the specified value as complex. + */ + private void markComplexEnumType(ReferenceValue referenceValue) + { + Clazz clazz = referenceValue.getReferencedClass(); + if (clazz != null) + { + clazz.accept(complexEnumMarker); + } + } +} diff --git a/src/proguard/optimize/evaluation/SimpleEnumUseSimplifier.java b/src/proguard/optimize/evaluation/SimpleEnumUseSimplifier.java new file mode 100644 index 0000000..b5a2396 --- /dev/null +++ b/src/proguard/optimize/evaluation/SimpleEnumUseSimplifier.java @@ -0,0 +1,820 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2014 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package proguard.optimize.evaluation; + +import proguard.classfile.*; +import proguard.classfile.attribute.*; +import proguard.classfile.attribute.visitor.AttributeVisitor; +import proguard.classfile.constant.*; +import proguard.classfile.constant.visitor.ConstantVisitor; +import proguard.classfile.editor.*; +import proguard.classfile.instruction.*; +import proguard.classfile.instruction.visitor.InstructionVisitor; +import proguard.classfile.util.*; +import proguard.classfile.visitor.*; +import proguard.evaluation.value.*; +import proguard.optimize.info.SimpleEnumMarker; + +/** + * This AttributeVisitor simplifies the use of enums in the code attributes that + * it visits. + * + * @see SimpleEnumMarker + * @see MemberReferenceFixer + * @author Eric Lafortune + */ +public class SimpleEnumUseSimplifier +extends SimplifiedVisitor +implements AttributeVisitor, + InstructionVisitor, + ConstantVisitor, + ParameterVisitor +{ + //* + private static final boolean DEBUG = false; + /*/ + private static boolean DEBUG = System.getProperty("enum") != null; + //*/ + + private final InstructionVisitor extraInstructionVisitor; + + private final PartialEvaluator partialEvaluator; + private final CodeAttributeEditor codeAttributeEditor = new CodeAttributeEditor(true, true); + private final ConstantVisitor nullParameterFixer = new ReferencedMemberVisitor(new AllParameterVisitor(this)); + + // Fields acting as parameters and return values for the visitor methods. + private Clazz invocationClazz; + private Method invocationMethod; + private CodeAttribute invocationCodeAttribute; + private int invocationOffset; + private boolean isSimpleEnum; + + + /** + * Creates a new SimpleEnumUseSimplifier. + */ + public SimpleEnumUseSimplifier() + { + this(new PartialEvaluator(), null); + } + + + /** + * Creates a new SimpleEnumDescriptorSimplifier. + * @param partialEvaluator the partial evaluator that will + * execute the code and provide + * information about the results. + * @param extraInstructionVisitor an optional extra visitor for all + * simplified instructions. + */ + public SimpleEnumUseSimplifier(PartialEvaluator partialEvaluator, + InstructionVisitor extraInstructionVisitor) + { + this.partialEvaluator = partialEvaluator; + this.extraInstructionVisitor = extraInstructionVisitor; + } + + + // Implementations for AttributeVisitor. + + public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} + + + public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) + { + if (DEBUG) + { + System.out.println("SimpleEnumUseSimplifier: "+clazz.getName()+"."+method.getName(clazz)+method.getDescriptor(clazz)); + } + + // Skip the non-static methods of simple enum classes. + if (SimpleEnumMarker.isSimpleEnum(clazz) && + (method.getAccessFlags() & ClassConstants.ACC_STATIC) == 0) + { + return; + } + + // Evaluate the method. + partialEvaluator.visitCodeAttribute(clazz, method, codeAttribute); + + int codeLength = codeAttribute.u4codeLength; + + // Reset the code changes. + codeAttributeEditor.reset(codeLength); + + // Replace any instructions that can be simplified. + for (int offset = 0; offset < codeLength; offset++) + { + if (partialEvaluator.isTraced(offset)) + { + Instruction instruction = InstructionFactory.create(codeAttribute.code, + offset); + + instruction.accept(clazz, method, codeAttribute, offset, this); + } + } + + // Apply all accumulated changes to the code. + codeAttributeEditor.visitCodeAttribute(clazz, method, codeAttribute); + } + + + // Implementations for InstructionVisitor. + + public void visitSimpleInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SimpleInstruction simpleInstruction) + { + switch (simpleInstruction.opcode) + { + case InstructionConstants.OP_AALOAD: + { + if (isPushingSimpleEnum(offset)) + { + // Load a simple enum integer from an integer array. + replaceInstruction(clazz, + offset, + simpleInstruction, + new SimpleInstruction( + InstructionConstants.OP_IALOAD)); + } + break; + } + case InstructionConstants.OP_AASTORE: + { + if (isPoppingSimpleEnumArray(offset, 2)) + { + // Store a simple enum integer in an integer array. + replaceInstruction(clazz, + offset, + simpleInstruction, + new SimpleInstruction(InstructionConstants.OP_IASTORE)); + + // Replace any producers of null constants. + replaceNullStackEntryProducers(clazz, method, codeAttribute, offset); + } + break; + } + case InstructionConstants.OP_ARETURN: + { + if (isReturningSimpleEnum(clazz, method)) + { + // Return a simple enum integer instead of an enum. + replaceInstruction(clazz, + offset, + simpleInstruction, + new SimpleInstruction(InstructionConstants.OP_IRETURN)); + + // Replace any producers of null constants. + replaceNullStackEntryProducers(clazz, method, codeAttribute, offset); + } + break; + } + } + } + + + public void visitVariableInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VariableInstruction variableInstruction) + { + int variableIndex = variableInstruction.variableIndex; + + switch (variableInstruction.opcode) + { + case InstructionConstants.OP_ALOAD: + case InstructionConstants.OP_ALOAD_0: + case InstructionConstants.OP_ALOAD_1: + case InstructionConstants.OP_ALOAD_2: + case InstructionConstants.OP_ALOAD_3: + { + if (isPushingSimpleEnum(offset)) + { + // Load a simple enum integer instead of an enum. + replaceInstruction(clazz, + offset, + variableInstruction, + new VariableInstruction(InstructionConstants.OP_ILOAD, + variableIndex)); + + // Replace any producers of null constants. + replaceNullVariableProducers(clazz, + method, + codeAttribute, + offset, + variableIndex); + } + break; + } + case InstructionConstants.OP_ASTORE: + case InstructionConstants.OP_ASTORE_0: + case InstructionConstants.OP_ASTORE_1: + case InstructionConstants.OP_ASTORE_2: + case InstructionConstants.OP_ASTORE_3: + { + if (!partialEvaluator.isSubroutineStart(offset) && + isPoppingSimpleEnum(offset)) + { + // Store a simple enum integer instead of an enum. + replaceInstruction(clazz, + offset, + variableInstruction, + new VariableInstruction(InstructionConstants.OP_ISTORE, + variableIndex)); + + // Replace any producers of null constants. + replaceNullStackEntryProducers(clazz, method, codeAttribute, offset); + } + break; + } + } + } + + + public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction) + { + switch (constantInstruction.opcode) + { + case InstructionConstants.OP_PUTSTATIC: + case InstructionConstants.OP_PUTFIELD: + { + // Replace any producers of null constants. + invocationClazz = clazz; + invocationMethod = method; + invocationCodeAttribute = codeAttribute; + invocationOffset = offset; + clazz.constantPoolEntryAccept(constantInstruction.constantIndex, + nullParameterFixer); + break; + } + case InstructionConstants.OP_INVOKEVIRTUAL: + { + // Check if the instruction is calling a simple enum. + String invokedMethodName = + clazz.getRefName(constantInstruction.constantIndex); + String invokedMethodType = + clazz.getRefType(constantInstruction.constantIndex); + int stackEntryIndex = + ClassUtil.internalMethodParameterSize(invokedMethodType); + if (isPoppingSimpleEnum(offset, stackEntryIndex)) + { + replaceSupportedMethod(clazz, + offset, + constantInstruction, + invokedMethodName, + invokedMethodType); + } + + // Fall through to check the parameters. + } + case InstructionConstants.OP_INVOKESPECIAL: + case InstructionConstants.OP_INVOKESTATIC: + case InstructionConstants.OP_INVOKEINTERFACE: + { + // Replace any producers of null constants. + invocationClazz = clazz; + invocationMethod = method; + invocationCodeAttribute = codeAttribute; + invocationOffset = offset; + clazz.constantPoolEntryAccept(constantInstruction.constantIndex, + nullParameterFixer); + break; + } + case InstructionConstants.OP_ANEWARRAY: + { + int constantIndex = constantInstruction.constantIndex; + + if (isReferencingSimpleEnum(clazz, constantIndex) && + !ClassUtil.isInternalArrayType(clazz.getClassName(constantIndex))) + { + // Create an integer array instead of an enum array. + replaceInstruction(clazz, + offset, + constantInstruction, + new SimpleInstruction(InstructionConstants.OP_NEWARRAY, + InstructionConstants.ARRAY_T_INT)); + } + break; + } + case InstructionConstants.OP_CHECKCAST: + { + if (isPoppingSimpleEnum(offset)) + { + // Enum classes can only be simple if the checkcast + // succeeds, so we can delete it. + deleteInstruction(clazz, + offset, + constantInstruction); + + // Replace any producers of null constants. + replaceNullStackEntryProducers(clazz, method, codeAttribute, offset); + } + break; + } + case InstructionConstants.OP_INSTANCEOF: + { + if (isPoppingSimpleEnum(offset)) + { + // Enum classes can only be simple if the instanceof + // succeeds, so we can push a constant result. + replaceInstruction(clazz, + offset, + constantInstruction, + new SimpleInstruction(InstructionConstants.OP_ICONST_1)); + + // Replace any producers of null constants. + replaceNullStackEntryProducers(clazz, method, codeAttribute, offset); + } + break; + } + } + } + + + public void visitBranchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, BranchInstruction branchInstruction) + { + switch (branchInstruction.opcode) + { + case InstructionConstants.OP_IFACMPEQ: + { + if (isPoppingSimpleEnum(offset)) + { + // Compare simple enum integers instead of enums. + replaceInstruction(clazz, + offset, + branchInstruction, + new BranchInstruction(InstructionConstants.OP_IFICMPEQ, + branchInstruction.branchOffset)); + } + break; + } + case InstructionConstants.OP_IFACMPNE: + { + if (isPoppingSimpleEnum(offset)) + { + // Compare simple enum integers instead of enums. + replaceInstruction(clazz, + offset, + branchInstruction, + new BranchInstruction(InstructionConstants.OP_IFICMPNE, + branchInstruction.branchOffset)); + } + break; + } + case InstructionConstants.OP_IFNULL: + { + if (isPoppingSimpleEnum(offset)) + { + // Compare with 0 instead of null. + replaceInstruction(clazz, + offset, + branchInstruction, + new BranchInstruction( + InstructionConstants.OP_IFEQ, + branchInstruction.branchOffset)); + } + break; + } + case InstructionConstants.OP_IFNONNULL: + { + if (isPoppingSimpleEnum(offset)) + { + // Compare with 0 instead of null. + replaceInstruction(clazz, + offset, + branchInstruction, + new BranchInstruction(InstructionConstants.OP_IFNE, + branchInstruction.branchOffset)); + } + break; + } + } + } + + + public void visitAnySwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SwitchInstruction switchInstruction) + { + } + + + // Implementations for ConstantVisitor. + + public void visitAnyConstant(Clazz clazz, Constant constant) {} + + + public void visitStringConstant(Clazz clazz, StringConstant stringConstant) + { + // Does the constant refer to a simple enum type? + isSimpleEnum = isSimpleEnum(stringConstant.referencedClass); + } + + + public void visitClassConstant(Clazz clazz, ClassConstant classConstant) + { + // Does the constant refer to a simple enum type? + isSimpleEnum = isSimpleEnum(classConstant.referencedClass); + } + + + // Implementations for ParameterVisitor. + + public void visitParameter(Clazz clazz, Member member, int parameterIndex, int parameterCount, int parameterOffset, int parameterSize, String parameterType, Clazz referencedClass) + { + // Check if the parameter is passing a simple enum as a more general + // type. + if (!ClassUtil.isInternalPrimitiveType(parameterType.charAt(0)) && + isSimpleEnum(referencedClass)) + { + // Replace any producers of null constants for this parameter. + int stackEntryIndex = parameterSize - parameterOffset - 1; + + replaceNullStackEntryProducers(invocationClazz, + invocationMethod, + invocationCodeAttribute, + invocationOffset, + stackEntryIndex); + } + } + + + // Small utility methods. + + /** + * Returns whether the constant at the given offset is referencing a + * simple enum class. + */ + private boolean isReferencingSimpleEnum(Clazz clazz, int constantIndex) + { + isSimpleEnum = false; + + clazz.constantPoolEntryAccept(constantIndex, this); + + return isSimpleEnum; + } + + + /** + * Returns whether the given method is returning a simple enum class. + */ + private boolean isReturningSimpleEnum(Clazz clazz, Method method) + { + String descriptor = method.getDescriptor(clazz); + String returnType = ClassUtil.internalMethodReturnType(descriptor); + + if (ClassUtil.isInternalClassType(returnType) && + !ClassUtil.isInternalArrayType(returnType)) + { + Clazz[] referencedClasses = + ((ProgramMethod)method).referencedClasses; + + if (referencedClasses != null) + { + int returnedClassIndex = + new DescriptorClassEnumeration(descriptor).classCount() - 1; + + Clazz returnedClass = referencedClasses[returnedClassIndex]; + + return isSimpleEnum(returnedClass); + } + } + + return false; + } + + + /** + * Returns whether the instruction at the given offset is pushing a simple + * enum class. + */ + private boolean isPushingSimpleEnum(int offset) + { + ReferenceValue referenceValue = + partialEvaluator.getStackAfter(offset).getTop(0).referenceValue(); + + Clazz referencedClass = referenceValue.getReferencedClass(); + + return isSimpleEnum(referencedClass) && + !ClassUtil.isInternalArrayType(referenceValue.getType()); + } + + + /** + * Returns whether the instruction at the given offset is popping a simple + * enum class. + */ + private boolean isPoppingSimpleEnum(int offset) + { + return isPoppingSimpleEnum(offset, 0); + } + + + /** + * Returns whether the instruction at the given offset is popping a simple + * enum class. + */ + private boolean isPoppingSimpleEnum(int offset, int stackEntryIndex) + { + ReferenceValue referenceValue = + partialEvaluator.getStackBefore(offset).getTop(stackEntryIndex).referenceValue(); + + return isSimpleEnum(referenceValue.getReferencedClass()) && + !ClassUtil.isInternalArrayType(referenceValue.getType()); + } + + + /** + * Returns whether the instruction at the given offset is popping a simple + * enum type. This includes simple enum arrays. + */ + private boolean isPoppingSimpleEnumType(int offset, int stackEntryIndex) + { + ReferenceValue referenceValue = + partialEvaluator.getStackBefore(offset).getTop(stackEntryIndex).referenceValue(); + + return isSimpleEnum(referenceValue.getReferencedClass()); + } + + + /** + * Returns whether the instruction at the given offset is popping a + * one-dimensional simple enum array. + */ + private boolean isPoppingSimpleEnumArray(int offset, int stackEntryIndex) + { + ReferenceValue referenceValue = + partialEvaluator.getStackBefore(offset).getTop(stackEntryIndex).referenceValue(); + + return isSimpleEnum(referenceValue.getReferencedClass()) && + ClassUtil.internalArrayTypeDimensionCount(referenceValue.getType()) == 1; + } + + + /** + * Returns whether the given class is not null and a simple enum class. + */ + private boolean isSimpleEnum(Clazz clazz) + { + return clazz != null && + SimpleEnumMarker.isSimpleEnum(clazz); + } + + + /** + * Returns whether the specified enum method is supported for simple enums. + */ + private void replaceSupportedMethod(Clazz clazz, + int offset, + Instruction instruction, + String name, + String type) + { + if (name.equals(ClassConstants.METHOD_NAME_ORDINAL) && + type.equals(ClassConstants.METHOD_TYPE_ORDINAL)) + { + Instruction[] replacementInstructions = new Instruction[] + { + new SimpleInstruction(InstructionConstants.OP_ICONST_1), + new SimpleInstruction(InstructionConstants.OP_ISUB), + }; + + replaceInstructions(clazz, + offset, + instruction, + replacementInstructions); + } + } + + + /** + * Replaces the instruction at the given offset by the given instructions. + */ + private void replaceInstructions(Clazz clazz, + int offset, + Instruction instruction, + Instruction[] replacementInstructions) + { + if (DEBUG) System.out.println(" Replacing instruction "+instruction.toString(offset)+" -> "+replacementInstructions.length+" instructions"); + + codeAttributeEditor.replaceInstruction(offset, replacementInstructions); + + // Visit the instruction, if required. + if (extraInstructionVisitor != null) + { + // Note: we're not passing the right arguments for now, knowing that + // they aren't used anyway. + instruction.accept(clazz, null, null, offset, extraInstructionVisitor); + } + } + + + /** + * Replaces the instruction at the given offset by the given instruction, + * popping any now unused stack entries. + */ + private void replaceInstruction(Clazz clazz, + int offset, + Instruction instruction, + Instruction replacementInstruction) + { + // Pop unneeded stack entries if necessary. + int popCount = + instruction.stackPopCount(clazz) - + replacementInstruction.stackPopCount(clazz); + + insertPopInstructions(offset, popCount); + + if (DEBUG) System.out.println(" Replacing instruction "+instruction.toString(offset)+" -> "+replacementInstruction.toString()+(popCount == 0 ? "" : " ("+popCount+" pops)")); + + codeAttributeEditor.replaceInstruction(offset, replacementInstruction); + + // Visit the instruction, if required. + if (extraInstructionVisitor != null) + { + // Note: we're not passing the right arguments for now, knowing that + // they aren't used anyway. + instruction.accept(clazz, null, null, offset, extraInstructionVisitor); + } + } + + + /** + * Deletes the instruction at the given offset, popping any now unused + * stack entries. + */ + private void deleteInstruction(Clazz clazz, + int offset, + Instruction instruction) + { + // Pop unneeded stack entries if necessary. + //int popCount = instruction.stackPopCount(clazz); + // + //insertPopInstructions(offset, popCount); + // + //if (DEBUG) System.out.println(" Deleting instruction "+instruction.toString(offset)+(popCount == 0 ? "" : " ("+popCount+" pops)")); + + if (DEBUG) System.out.println(" Deleting instruction "+instruction.toString(offset)); + + codeAttributeEditor.deleteInstruction(offset); + + // Visit the instruction, if required. + if (extraInstructionVisitor != null) + { + // Note: we're not passing the right arguments for now, knowing that + // they aren't used anyway. + instruction.accept(clazz, null, null, offset, extraInstructionVisitor); + } + } + + + /** + * Pops the given number of stack entries before the instruction at the + * given offset. + */ + private void insertPopInstructions(int offset, int popCount) + { + switch (popCount) + { + case 0: + { + break; + } + case 1: + { + // Insert a single pop instruction. + Instruction popInstruction = + new SimpleInstruction(InstructionConstants.OP_POP); + + codeAttributeEditor.insertBeforeInstruction(offset, + popInstruction); + break; + } + case 2: + { + // Insert a single pop2 instruction. + Instruction popInstruction = + new SimpleInstruction(InstructionConstants.OP_POP2); + + codeAttributeEditor.insertBeforeInstruction(offset, + popInstruction); + break; + } + default: + { + // Insert the specified number of pop instructions. + Instruction[] popInstructions = + new Instruction[popCount / 2 + popCount % 2]; + + Instruction popInstruction = + new SimpleInstruction(InstructionConstants.OP_POP2); + + for (int index = 0; index < popCount / 2; index++) + { + popInstructions[index] = popInstruction; + } + + if (popCount % 2 == 1) + { + popInstruction = + new SimpleInstruction(InstructionConstants.OP_POP); + + popInstructions[popCount / 2] = popInstruction; + } + + codeAttributeEditor.insertBeforeInstruction(offset, + popInstructions); + break; + } + } + } + + + /** + * Replaces aconst_null producers of the consumer of the top stack entry + * at the given offset by iconst_0. + */ + private void replaceNullStackEntryProducers(Clazz clazz, + Method method, + CodeAttribute codeAttribute, + int consumerOffset) + { + replaceNullStackEntryProducers(clazz, method, codeAttribute, consumerOffset, 0); + } + + + /** + * Replaces aconst_null producers of the specified stack entry by + * iconst_0. + */ + private void replaceNullStackEntryProducers(Clazz clazz, + Method method, + CodeAttribute codeAttribute, + int consumerOffset, + int stackEntryIndex) + { + InstructionOffsetValue producerOffsets = + partialEvaluator.getStackBefore(consumerOffset).getTopActualProducerValue(stackEntryIndex).instructionOffsetValue(); + + for (int index = 0; index < producerOffsets.instructionOffsetCount(); index++) + { + int producerOffset = producerOffsets.instructionOffset(index); + + // TODO: A method might be pushing the null constant. + if (producerOffset >= 0 && + codeAttribute.code[producerOffset] == InstructionConstants.OP_ACONST_NULL) + { + // Replace pushing null by pushing 0. + replaceInstruction(clazz, + producerOffset, + new SimpleInstruction(InstructionConstants.OP_ACONST_NULL), + new SimpleInstruction(InstructionConstants.OP_ICONST_0)); + } + } + } + + + /** + * Replaces aconst_null/astore producers of the specified reference variable by + * iconst_0/istore. + */ + private void replaceNullVariableProducers(Clazz clazz, + Method method, + CodeAttribute codeAttribute, + int consumerOffset, + int variableIndex) + { + InstructionOffsetValue producerOffsets = + partialEvaluator.getVariablesBefore(consumerOffset).getProducerValue(variableIndex).instructionOffsetValue(); + + for (int index = 0; index < producerOffsets.instructionOffsetCount(); index++) + { + int producerOffset = producerOffsets.instructionOffset(index); + + if (producerOffset >= 0 && + partialEvaluator.getVariablesAfter(producerOffset).getValue(variableIndex).referenceValue().isNull() == Value.ALWAYS) + { + // Replace loading null by loading 0. + replaceInstruction(clazz, + producerOffset, + new VariableInstruction(InstructionConstants.OP_ASTORE, variableIndex), + new VariableInstruction(InstructionConstants.OP_ISTORE, variableIndex)); + + // Replace pushing null by pushing 0. + replaceNullStackEntryProducers(clazz, method, codeAttribute, producerOffset); + } + } + } +} diff --git a/src/proguard/optimize/evaluation/StoringInvocationUnit.java b/src/proguard/optimize/evaluation/StoringInvocationUnit.java index 846f685..271b654 100644 --- a/src/proguard/optimize/evaluation/StoringInvocationUnit.java +++ b/src/proguard/optimize/evaluation/StoringInvocationUnit.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2014 Eric Lafortune (eric@graphics.cornell.edu) * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -27,7 +27,7 @@ import proguard.evaluation.value.*; import proguard.optimize.info.*; /** - * This InvocationUbit stores parameter values and return values with the + * This InvocationUnit stores parameter values and return values with the * methods that are invoked. * * @see LoadingInvocationUnit @@ -126,7 +126,7 @@ extends BasicInvocationUnit generalizeMethodReturnValue(method, value); } } - + // Small utility methods. diff --git a/src/proguard/optimize/evaluation/TracedBranchUnit.java b/src/proguard/optimize/evaluation/TracedBranchUnit.java index e6acf6f..9e55275 100644 --- a/src/proguard/optimize/evaluation/TracedBranchUnit.java +++ b/src/proguard/optimize/evaluation/TracedBranchUnit.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2014 Eric Lafortune (eric@graphics.cornell.edu) * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/optimize/evaluation/VariableOptimizer.java b/src/proguard/optimize/evaluation/VariableOptimizer.java index 73efddc..bef1445 100644 --- a/src/proguard/optimize/evaluation/VariableOptimizer.java +++ b/src/proguard/optimize/evaluation/VariableOptimizer.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2014 Eric Lafortune (eric@graphics.cornell.edu) * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -21,11 +21,11 @@ package proguard.optimize.evaluation; import proguard.classfile.*; +import proguard.classfile.attribute.*; import proguard.classfile.attribute.visitor.*; import proguard.classfile.editor.*; -import proguard.classfile.visitor.MemberVisitor; -import proguard.classfile.attribute.*; import proguard.classfile.util.*; +import proguard.classfile.visitor.MemberVisitor; /** * This AttributeVisitor optimizes variable allocation based on their the liveness, @@ -110,7 +110,7 @@ implements AttributeVisitor, codeAttribute.attributesAccept(clazz, method, this); int startIndex = - (method.getAccessFlags() & ClassConstants.INTERNAL_ACC_STATIC) != 0 || + (method.getAccessFlags() & ClassConstants.ACC_STATIC) != 0 || reuseThis ? 0 : 1; int parameterSize = diff --git a/src/proguard/optimize/info/AccessMethodMarker.java b/src/proguard/optimize/info/AccessMethodMarker.java index e4c8d7c..4c3c17b 100644 --- a/src/proguard/optimize/info/AccessMethodMarker.java +++ b/src/proguard/optimize/info/AccessMethodMarker.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2014 Eric Lafortune (eric@graphics.cornell.edu) * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -21,8 +21,7 @@ package proguard.optimize.info; import proguard.classfile.*; -import proguard.classfile.attribute.*; -import proguard.classfile.attribute.visitor.*; +import proguard.classfile.attribute.CodeAttribute; import proguard.classfile.constant.*; import proguard.classfile.constant.visitor.ConstantVisitor; import proguard.classfile.instruction.*; @@ -110,7 +109,7 @@ implements InstructionVisitor, { int accessFlags = clazz.getAccessFlags(); - if ((accessFlags & ClassConstants.INTERNAL_ACC_PUBLIC) == 0) + if ((accessFlags & ClassConstants.ACC_PUBLIC) == 0) { setAccessesPackageCode(invokingMethod); } @@ -123,15 +122,15 @@ implements InstructionVisitor, { int accessFlags = member.getAccessFlags(); - if ((accessFlags & ClassConstants.INTERNAL_ACC_PRIVATE) != 0) + if ((accessFlags & ClassConstants.ACC_PRIVATE) != 0) { setAccessesPrivateCode(invokingMethod); } - else if ((accessFlags & ClassConstants.INTERNAL_ACC_PROTECTED) != 0) + else if ((accessFlags & ClassConstants.ACC_PROTECTED) != 0) { setAccessesProtectedCode(invokingMethod); } - else if ((accessFlags & ClassConstants.INTERNAL_ACC_PUBLIC) == 0) + else if ((accessFlags & ClassConstants.ACC_PUBLIC) == 0) { setAccessesPackageCode(invokingMethod); } diff --git a/src/proguard/optimize/info/BackwardBranchMarker.java b/src/proguard/optimize/info/BackwardBranchMarker.java index 07bfefb..089b66e 100644 --- a/src/proguard/optimize/info/BackwardBranchMarker.java +++ b/src/proguard/optimize/info/BackwardBranchMarker.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2014 Eric Lafortune (eric@graphics.cornell.edu) * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/optimize/info/CatchExceptionMarker.java b/src/proguard/optimize/info/CatchExceptionMarker.java index 8f87a08..2894d2a 100644 --- a/src/proguard/optimize/info/CatchExceptionMarker.java +++ b/src/proguard/optimize/info/CatchExceptionMarker.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2014 Eric Lafortune (eric@graphics.cornell.edu) * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/optimize/info/CaughtClassFilter.java b/src/proguard/optimize/info/CaughtClassFilter.java index 762e7de..0c32151 100644 --- a/src/proguard/optimize/info/CaughtClassFilter.java +++ b/src/proguard/optimize/info/CaughtClassFilter.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2014 Eric Lafortune (eric@graphics.cornell.edu) * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/optimize/info/CaughtClassMarker.java b/src/proguard/optimize/info/CaughtClassMarker.java index 1752f0c..31ee1a8 100644 --- a/src/proguard/optimize/info/CaughtClassMarker.java +++ b/src/proguard/optimize/info/CaughtClassMarker.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2014 Eric Lafortune (eric@graphics.cornell.edu) * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -21,7 +21,6 @@ package proguard.optimize.info; import proguard.classfile.*; -import proguard.classfile.util.SimplifiedVisitor; import proguard.classfile.visitor.ClassVisitor; /** diff --git a/src/proguard/optimize/info/ClassOptimizationInfo.java b/src/proguard/optimize/info/ClassOptimizationInfo.java index dbe041e..5ac56f9 100644 --- a/src/proguard/optimize/info/ClassOptimizationInfo.java +++ b/src/proguard/optimize/info/ClassOptimizationInfo.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2014 Eric Lafortune (eric@graphics.cornell.edu) * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -34,6 +34,7 @@ public class ClassOptimizationInfo private boolean isInstanceofed = false; private boolean isDotClassed = false; private boolean isCaught = false; + private boolean isSimpleEnum = false; private boolean containsStaticInitializer = false; private boolean containsPackageVisibleMembers = false; private boolean invokesPackageVisibleMembers = false; @@ -88,6 +89,18 @@ public class ClassOptimizationInfo } + public void setSimpleEnum(boolean simple) + { + isSimpleEnum = simple; + } + + + public boolean isSimpleEnum() + { + return isSimpleEnum; + } + + public void setContainsStaticInitializer() { containsStaticInitializer = true; @@ -157,7 +170,6 @@ public class ClassOptimizationInfo public static ClassOptimizationInfo getClassOptimizationInfo(Clazz clazz) { Object visitorInfo = clazz.getVisitorInfo(); - return visitorInfo instanceof ClassOptimizationInfo ? (ClassOptimizationInfo)visitorInfo : null; diff --git a/src/proguard/optimize/info/ClassOptimizationInfoSetter.java b/src/proguard/optimize/info/ClassOptimizationInfoSetter.java index f3d78e2..b9211e7 100644 --- a/src/proguard/optimize/info/ClassOptimizationInfoSetter.java +++ b/src/proguard/optimize/info/ClassOptimizationInfoSetter.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2014 Eric Lafortune (eric@graphics.cornell.edu) * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -20,7 +20,7 @@ */ package proguard.optimize.info; -import proguard.classfile.*; +import proguard.classfile.ProgramClass; import proguard.classfile.util.SimplifiedVisitor; import proguard.classfile.visitor.ClassVisitor; import proguard.optimize.KeepMarker; diff --git a/src/proguard/optimize/info/DotClassFilter.java b/src/proguard/optimize/info/DotClassFilter.java index c3fd878..8ab4f98 100644 --- a/src/proguard/optimize/info/DotClassFilter.java +++ b/src/proguard/optimize/info/DotClassFilter.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2014 Eric Lafortune (eric@graphics.cornell.edu) * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/optimize/info/DotClassMarker.java b/src/proguard/optimize/info/DotClassMarker.java index ef5cfd1..d9519ac 100644 --- a/src/proguard/optimize/info/DotClassMarker.java +++ b/src/proguard/optimize/info/DotClassMarker.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2014 Eric Lafortune (eric@graphics.cornell.edu) * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/optimize/info/DynamicInvocationMarker.java b/src/proguard/optimize/info/DynamicInvocationMarker.java new file mode 100644 index 0000000..daee6b4 --- /dev/null +++ b/src/proguard/optimize/info/DynamicInvocationMarker.java @@ -0,0 +1,79 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2014 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package proguard.optimize.info; + +import proguard.classfile.*; +import proguard.classfile.attribute.CodeAttribute; +import proguard.classfile.constant.*; +import proguard.classfile.constant.visitor.ConstantVisitor; +import proguard.classfile.instruction.*; +import proguard.classfile.instruction.visitor.InstructionVisitor; +import proguard.classfile.util.SimplifiedVisitor; +import proguard.classfile.visitor.*; + +/** + * This InstructionVisitor marks whether the methods whose instructions it + * visits contain the invokedynamic instruction. + * + * @author Eric Lafortune + */ +public class DynamicInvocationMarker +extends SimplifiedVisitor +implements InstructionVisitor, + ConstantVisitor, + ClassVisitor, + MemberVisitor +{ + // Implementations for InstructionVisitor. + + public void visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction) {} + + + public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction) + { + if (constantInstruction.opcode == InstructionConstants.OP_INVOKEDYNAMIC) + { + setInvokesDynamically(method); + } + } + + + // Small utility methods. + + private static void setInvokesDynamically(Method method) + { + MethodOptimizationInfo info = MethodOptimizationInfo.getMethodOptimizationInfo(method); + if (info != null) + { + info.setInvokesDynamically(); + } + } + + + /** + * Returns whether the given method calls the invokedynamic instruction. + */ + public static boolean invokesDynamically(Method method) + { + MethodOptimizationInfo info = MethodOptimizationInfo.getMethodOptimizationInfo(method); + return info == null || info.invokesDynamically(); + } +} diff --git a/src/proguard/optimize/info/ExceptionInstructionChecker.java b/src/proguard/optimize/info/ExceptionInstructionChecker.java index 4bfa96f..a32a1d3 100644 --- a/src/proguard/optimize/info/ExceptionInstructionChecker.java +++ b/src/proguard/optimize/info/ExceptionInstructionChecker.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2014 Eric Lafortune (eric@graphics.cornell.edu) * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -22,12 +22,9 @@ package proguard.optimize.info; import proguard.classfile.*; import proguard.classfile.attribute.CodeAttribute; -import proguard.classfile.constant.RefConstant; -import proguard.classfile.constant.visitor.ConstantVisitor; import proguard.classfile.instruction.*; import proguard.classfile.instruction.visitor.InstructionVisitor; import proguard.classfile.util.SimplifiedVisitor; -import proguard.classfile.visitor.MemberVisitor; /** * This class can tell whether an instruction might throw exceptions. @@ -45,15 +42,90 @@ implements InstructionVisitor /** - * Returns whether the given instruction may throw exceptions. + * Returns whether the specified method may throw exceptions. */ - public boolean mayThrowExceptions(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction) + public boolean mayThrowExceptions(Clazz clazz, + Method method, + CodeAttribute codeAttribute) { - mayThrowExceptions = false; + return mayThrowExceptions(clazz, + method, + codeAttribute, + 0, + codeAttribute.u4codeLength); + } + + + /** + * Returns whether the specified block of code may throw exceptions. + */ + public boolean mayThrowExceptions(Clazz clazz, + Method method, + CodeAttribute codeAttribute, + int startOffset, + int endOffset) + { + byte[] code = codeAttribute.code; + + // Go over all instructions. + int offset = startOffset; + while (offset < endOffset) + { + // Get the current instruction. + Instruction instruction = InstructionFactory.create(code, offset); - instruction.accept(clazz, method, codeAttribute, offset, this); + // Check if it may be throwing exceptions. + if (mayThrowExceptions(clazz, + method, + codeAttribute, + offset, + instruction)) + { + return true; + } - return mayThrowExceptions; + // Go to the next instruction. + offset += instruction.length(offset); + } + + return false; + } + + + /** + * Returns whether the specified instruction may throw exceptions. + */ + public boolean mayThrowExceptions(Clazz clazz, + Method method, + CodeAttribute codeAttribute, + int offset) + { + Instruction instruction = InstructionFactory.create(codeAttribute.code, offset); + + return mayThrowExceptions(clazz, + method, + codeAttribute, + offset, + instruction); + } + + + /** + * Returns whether the given instruction may throw exceptions. + */ + public boolean mayThrowExceptions(Clazz clazz, + Method method, + CodeAttribute codeAttribute, + int offset, + Instruction instruction) + { + return instruction.mayThrowExceptions(); + +// mayThrowExceptions = false; +// +// instruction.accept(clazz, method, codeAttribute, offset, this); +// +// return mayThrowExceptions; } @@ -64,15 +136,13 @@ implements InstructionVisitor public void visitSimpleInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SimpleInstruction simpleInstruction) { - byte opcode = simpleInstruction.opcode; - // Check for instructions that may throw exceptions. // Note that monitorexit can not sensibly throw exceptions, except the // broken and deprecated asynchronous ThreadDeath. Removing the // artificial infinite looping exception blocks that recent compilers // add does not strictly follow the JVM specs, but it does have the // additional benefit of avoiding a bug in the JVM in JDK 1.1. - switch (opcode) + switch (simpleInstruction.opcode) { case InstructionConstants.OP_IDIV: case InstructionConstants.OP_LDIV: @@ -101,16 +171,13 @@ implements InstructionVisitor // These instructions may throw exceptions. mayThrowExceptions = true; } - } public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction) { - byte opcode = constantInstruction.opcode; - // Check for instructions that may throw exceptions. - switch (opcode) + switch (constantInstruction.opcode) { case InstructionConstants.OP_GETSTATIC: case InstructionConstants.OP_PUTSTATIC: @@ -128,7 +195,6 @@ implements InstructionVisitor case InstructionConstants.OP_MULTIANEWARRAY: // These instructions may throw exceptions. mayThrowExceptions = true; - } // case InstructionConstants.OP_INVOKEVIRTUAL: // case InstructionConstants.OP_INVOKESPECIAL: @@ -136,6 +202,7 @@ implements InstructionVisitor // case InstructionConstants.OP_INVOKEINTERFACE: // // Check if the invoking the method may throw an exception. // clazz.constantPoolEntryAccept(constantInstruction.constantIndex, this); + } } diff --git a/src/proguard/optimize/info/FieldOptimizationInfo.java b/src/proguard/optimize/info/FieldOptimizationInfo.java index 0fa9167..730eead 100644 --- a/src/proguard/optimize/info/FieldOptimizationInfo.java +++ b/src/proguard/optimize/info/FieldOptimizationInfo.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2014 Eric Lafortune (eric@graphics.cornell.edu) * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -21,11 +21,11 @@ package proguard.optimize.info; import proguard.classfile.*; -import proguard.classfile.attribute.visitor.*; import proguard.classfile.attribute.*; -import proguard.classfile.util.*; -import proguard.evaluation.value.*; +import proguard.classfile.attribute.visitor.*; +import proguard.classfile.util.SimplifiedVisitor; import proguard.evaluation.ConstantValueFactory; +import proguard.evaluation.value.*; /** * This class stores some optimization information that can be attached to @@ -37,8 +37,9 @@ public class FieldOptimizationInfo extends SimplifiedVisitor implements AttributeVisitor { - private static final SpecificValueFactory VALUE_FACTORY = new SpecificValueFactory(); - private static final ConstantValueFactory CONSTANT_VALUE_FACTORY = new ConstantValueFactory(VALUE_FACTORY); + private static final ParticularValueFactory VALUE_FACTORY = new ParticularValueFactory(); + private static final ConstantValueFactory CONSTANT_VALUE_FACTORY = new ConstantValueFactory(VALUE_FACTORY); + private static final InitialValueFactory INITIAL_VALUE_FACTORY = new InitialValueFactory(VALUE_FACTORY); private boolean isWritten; private boolean isRead; @@ -52,20 +53,9 @@ implements AttributeVisitor int accessFlags = field.getAccessFlags(); isWritten = - isRead = (accessFlags & ClassConstants.INTERNAL_ACC_VOLATILE) != 0; + isRead = (accessFlags & ClassConstants.ACC_VOLATILE) != 0; - if ((accessFlags & ClassConstants.INTERNAL_ACC_STATIC) != 0) - { - // See if we can initialize the static field with a constant value. - field.accept(clazz, new AllAttributeVisitor(this)); - } - - if ((accessFlags & ClassConstants.INTERNAL_ACC_FINAL) == 0 && - value == null) - { - // Otherwise initialize the non-final field with the default value. - value = initialValue(field.getDescriptor(clazz)); - } + resetValue(clazz, field); } @@ -129,6 +119,30 @@ implements AttributeVisitor } + public void resetValue(Clazz clazz, Field field) + { + int accessFlags = field.getAccessFlags(); + + value = null; + + // See if we can initialize a static field with a constant value. + if ((accessFlags & ClassConstants.ACC_STATIC) != 0) + { + field.accept(clazz, new AllAttributeVisitor(this)); + } + + // Otherwise initialize a non-final field with the default value. + // Conservatively, even a final field needs to be initialized with the + // default value, because it may be accessed before it is set. + if (value == null && + (SideEffectInstructionChecker.OPTIMIZE_CONSERVATIVELY || + (accessFlags & ClassConstants.ACC_FINAL) == 0)) + { + value = INITIAL_VALUE_FACTORY.createValue(field.getDescriptor(clazz)); + } + } + + public void generalizeValue(Value value) { this.value = this.value != null ? @@ -157,36 +171,6 @@ implements AttributeVisitor // Small utility methods. - private Value initialValue(String type) - { - switch (type.charAt(0)) - { - case ClassConstants.INTERNAL_TYPE_BOOLEAN: - case ClassConstants.INTERNAL_TYPE_BYTE: - case ClassConstants.INTERNAL_TYPE_CHAR: - case ClassConstants.INTERNAL_TYPE_SHORT: - case ClassConstants.INTERNAL_TYPE_INT: - return VALUE_FACTORY.createIntegerValue(0); - - case ClassConstants.INTERNAL_TYPE_LONG: - return VALUE_FACTORY.createLongValue(0L); - - case ClassConstants.INTERNAL_TYPE_FLOAT: - return VALUE_FACTORY.createFloatValue(0.0f); - - case ClassConstants.INTERNAL_TYPE_DOUBLE: - return VALUE_FACTORY.createDoubleValue(0.0); - - case ClassConstants.INTERNAL_TYPE_CLASS_START: - case ClassConstants.INTERNAL_TYPE_ARRAY: - return VALUE_FACTORY.createReferenceValueNull(); - - default: - throw new IllegalArgumentException("Invalid type ["+type+"]"); - } - } - - public static void setFieldOptimizationInfo(Clazz clazz, Field field) { field.setVisitorInfo(new FieldOptimizationInfo(clazz, field)); diff --git a/src/proguard/optimize/info/InstanceofClassFilter.java b/src/proguard/optimize/info/InstanceofClassFilter.java index 7cd85bc..ab345fb 100644 --- a/src/proguard/optimize/info/InstanceofClassFilter.java +++ b/src/proguard/optimize/info/InstanceofClassFilter.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2014 Eric Lafortune (eric@graphics.cornell.edu) * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/optimize/info/InstanceofClassMarker.java b/src/proguard/optimize/info/InstanceofClassMarker.java index 96d5baf..3556dd8 100644 --- a/src/proguard/optimize/info/InstanceofClassMarker.java +++ b/src/proguard/optimize/info/InstanceofClassMarker.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2014 Eric Lafortune (eric@graphics.cornell.edu) * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/optimize/info/InstantiationClassFilter.java b/src/proguard/optimize/info/InstantiationClassFilter.java index a659f06..7822205 100644 --- a/src/proguard/optimize/info/InstantiationClassFilter.java +++ b/src/proguard/optimize/info/InstantiationClassFilter.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2014 Eric Lafortune (eric@graphics.cornell.edu) * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/optimize/info/InstantiationClassMarker.java b/src/proguard/optimize/info/InstantiationClassMarker.java index b4afffd..3f90991 100644 --- a/src/proguard/optimize/info/InstantiationClassMarker.java +++ b/src/proguard/optimize/info/InstantiationClassMarker.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2014 Eric Lafortune (eric@graphics.cornell.edu) * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/optimize/info/MemberOptimizationInfoSetter.java b/src/proguard/optimize/info/MemberOptimizationInfoSetter.java index 3c27c93..ad64cf9 100644 --- a/src/proguard/optimize/info/MemberOptimizationInfoSetter.java +++ b/src/proguard/optimize/info/MemberOptimizationInfoSetter.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2014 Eric Lafortune (eric@graphics.cornell.edu) * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/optimize/info/MethodInvocationMarker.java b/src/proguard/optimize/info/MethodInvocationMarker.java index afb2336..c9a9131 100644 --- a/src/proguard/optimize/info/MethodInvocationMarker.java +++ b/src/proguard/optimize/info/MethodInvocationMarker.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2014 Eric Lafortune (eric@graphics.cornell.edu) * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/optimize/info/MethodOptimizationInfo.java b/src/proguard/optimize/info/MethodOptimizationInfo.java index fe754e5..9bfdb50 100644 --- a/src/proguard/optimize/info/MethodOptimizationInfo.java +++ b/src/proguard/optimize/info/MethodOptimizationInfo.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2014 Eric Lafortune (eric@graphics.cornell.edu) * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -38,6 +38,7 @@ public class MethodOptimizationInfo private boolean catchesExceptions = false; private boolean branchesBackward = false; private boolean invokesSuperMethods = false; + private boolean invokesDynamically = false; private boolean accessesPrivateCode = false; private boolean accessesPackageCode = false; private boolean accessesProtectedCode = false; @@ -58,7 +59,7 @@ public class MethodOptimizationInfo int parameterCount = ClassUtil.internalMethodParameterCount(method.getDescriptor(clazz)); - if ((method.getAccessFlags() & ClassConstants.INTERNAL_ACC_STATIC) == 0) + if ((method.getAccessFlags() & ClassConstants.ACC_STATIC) == 0) { parameterCount++; } @@ -142,6 +143,18 @@ public class MethodOptimizationInfo } + public void setInvokesDynamically() + { + invokesDynamically = true; + } + + + public boolean invokesDynamically() + { + return invokesDynamically; + } + + public void setAccessesPrivateCode() { accessesPrivateCode = true; @@ -256,6 +269,13 @@ public class MethodOptimizationInfo } + // For setting enum return values. + public void setReturnValue(Value returnValue) + { + this.returnValue = returnValue; + } + + public void merge(MethodOptimizationInfo other) { if (other != null) @@ -266,6 +286,7 @@ public class MethodOptimizationInfo this.catchesExceptions |= other.catchesExceptions; this.branchesBackward |= other.branchesBackward; this.invokesSuperMethods |= other.invokesSuperMethods; + this.invokesDynamically |= other.invokesDynamically; this.accessesPrivateCode |= other.accessesPrivateCode; this.accessesPackageCode |= other.accessesPackageCode; this.accessesProtectedCode |= other.accessesProtectedCode; diff --git a/src/proguard/optimize/info/NoSideEffectMethodMarker.java b/src/proguard/optimize/info/NoSideEffectMethodMarker.java index bf5ce45..34549b3 100644 --- a/src/proguard/optimize/info/NoSideEffectMethodMarker.java +++ b/src/proguard/optimize/info/NoSideEffectMethodMarker.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2014 Eric Lafortune (eric@graphics.cornell.edu) * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/optimize/info/NonPrivateMemberMarker.java b/src/proguard/optimize/info/NonPrivateMemberMarker.java index 06f8500..152e114 100644 --- a/src/proguard/optimize/info/NonPrivateMemberMarker.java +++ b/src/proguard/optimize/info/NonPrivateMemberMarker.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2014 Eric Lafortune (eric@graphics.cornell.edu) * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -49,13 +49,13 @@ implements ClassVisitor, programClass.constantPoolEntriesAccept(this); // Explicitly mark the <clinit> method. - programClass.methodAccept(ClassConstants.INTERNAL_METHOD_NAME_CLINIT, - ClassConstants.INTERNAL_METHOD_TYPE_CLINIT, + programClass.methodAccept(ClassConstants.METHOD_NAME_CLINIT, + ClassConstants.METHOD_TYPE_CLINIT, this); // Explicitly mark the parameterless <init> method. - programClass.methodAccept(ClassConstants.INTERNAL_METHOD_NAME_INIT, - ClassConstants.INTERNAL_METHOD_TYPE_INIT, + programClass.methodAccept(ClassConstants.METHOD_NAME_INIT, + ClassConstants.METHOD_TYPE_INIT, this); // Mark all methods that may have implementations. diff --git a/src/proguard/optimize/info/PackageVisibleMemberContainingClassMarker.java b/src/proguard/optimize/info/PackageVisibleMemberContainingClassMarker.java index 02e1a18..7f1dfdb 100644 --- a/src/proguard/optimize/info/PackageVisibleMemberContainingClassMarker.java +++ b/src/proguard/optimize/info/PackageVisibleMemberContainingClassMarker.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2014 Eric Lafortune (eric@graphics.cornell.edu) * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -39,7 +39,7 @@ implements ClassVisitor, public void visitAnyClass(Clazz clazz) { // Check the class itself. - if ((clazz.getAccessFlags() & ClassConstants.INTERNAL_ACC_PUBLIC) == 0) + if ((clazz.getAccessFlags() & ClassConstants.ACC_PUBLIC) == 0) { setPackageVisibleMembers(clazz); } @@ -57,8 +57,8 @@ implements ClassVisitor, public void visitAnyMember(Clazz clazz, Member member) { if ((member.getAccessFlags() & - (ClassConstants.INTERNAL_ACC_PRIVATE | - ClassConstants.INTERNAL_ACC_PUBLIC)) == 0) + (ClassConstants.ACC_PRIVATE | + ClassConstants.ACC_PUBLIC)) == 0) { setPackageVisibleMembers(clazz); } diff --git a/src/proguard/optimize/info/PackageVisibleMemberInvokingClassMarker.java b/src/proguard/optimize/info/PackageVisibleMemberInvokingClassMarker.java index 3148e3d..6a8e329 100644 --- a/src/proguard/optimize/info/PackageVisibleMemberInvokingClassMarker.java +++ b/src/proguard/optimize/info/PackageVisibleMemberInvokingClassMarker.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2014 Eric Lafortune (eric@graphics.cornell.edu) * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -21,8 +21,8 @@ package proguard.optimize.info; import proguard.classfile.*; -import proguard.classfile.constant.visitor.ConstantVisitor; import proguard.classfile.constant.*; +import proguard.classfile.constant.visitor.ConstantVisitor; import proguard.classfile.util.SimplifiedVisitor; import proguard.classfile.visitor.*; @@ -89,7 +89,7 @@ implements ConstantVisitor, public void visitAnyClass(Clazz clazz) { if ((clazz.getAccessFlags() & - ClassConstants.INTERNAL_ACC_PUBLIC) == 0) + ClassConstants.ACC_PUBLIC) == 0) { setInvokesPackageVisibleMembers(referencingClass); } @@ -101,8 +101,8 @@ implements ConstantVisitor, public void visitAnyMember(Clazz clazz, Member member) { if ((member.getAccessFlags() & - (ClassConstants.INTERNAL_ACC_PUBLIC | - ClassConstants.INTERNAL_ACC_PRIVATE)) == 0) + (ClassConstants.ACC_PUBLIC | + ClassConstants.ACC_PRIVATE)) == 0) { setInvokesPackageVisibleMembers(referencingClass); } diff --git a/src/proguard/optimize/info/ParameterUsageMarker.java b/src/proguard/optimize/info/ParameterUsageMarker.java index a2a264d..0061148 100644 --- a/src/proguard/optimize/info/ParameterUsageMarker.java +++ b/src/proguard/optimize/info/ParameterUsageMarker.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2014 Eric Lafortune (eric@graphics.cornell.edu) * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -89,7 +89,7 @@ implements MemberVisitor, // Must we mark the 'this' parameter? if (markThisParameter && - (accessFlags & ClassConstants.INTERNAL_ACC_STATIC) == 0) + (accessFlags & ClassConstants.ACC_STATIC) == 0) { // Mark the 'this' parameter. markParameterUsed(programMethod, 0); @@ -100,19 +100,19 @@ implements MemberVisitor, { // Mark all parameters, without the 'this' parameter. markUsedParameters(programMethod, - (accessFlags & ClassConstants.INTERNAL_ACC_STATIC) != 0 ? + (accessFlags & ClassConstants.ACC_STATIC) != 0 ? -1L : -2L); } // Is it a native method? - if ((accessFlags & ClassConstants.INTERNAL_ACC_NATIVE) != 0) + if ((accessFlags & ClassConstants.ACC_NATIVE) != 0) { // Mark all parameters. markUsedParameters(programMethod, -1L); } // Is it an abstract method? - else if ((accessFlags & ClassConstants.INTERNAL_ACC_ABSTRACT) != 0) + else if ((accessFlags & ClassConstants.ACC_ABSTRACT) != 0) { // Mark the 'this' parameter. markParameterUsed(programMethod, 0); @@ -123,10 +123,10 @@ implements MemberVisitor, { // Is the method not static, but synchronized, or can it have // other implementations, or is it a class instance initializer? - if ((accessFlags & ClassConstants.INTERNAL_ACC_STATIC) == 0 && - ((accessFlags & ClassConstants.INTERNAL_ACC_SYNCHRONIZED) != 0 || + if ((accessFlags & ClassConstants.ACC_STATIC) == 0 && + ((accessFlags & ClassConstants.ACC_SYNCHRONIZED) != 0 || programClass.mayHaveImplementations(programMethod) || - programMethod.getName(programClass).equals(ClassConstants.INTERNAL_METHOD_NAME_INIT))) + programMethod.getName(programClass).equals(ClassConstants.METHOD_NAME_INIT))) { // Mark the 'this' parameter. markParameterUsed(programMethod, 0); diff --git a/src/proguard/optimize/info/ReadWriteFieldMarker.java b/src/proguard/optimize/info/ReadWriteFieldMarker.java index 6bd4b2f..ea98d3e 100644 --- a/src/proguard/optimize/info/ReadWriteFieldMarker.java +++ b/src/proguard/optimize/info/ReadWriteFieldMarker.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2014 Eric Lafortune (eric@graphics.cornell.edu) * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/optimize/info/SideEffectInstructionChecker.java b/src/proguard/optimize/info/SideEffectInstructionChecker.java index 91f1f02..49a1281 100644 --- a/src/proguard/optimize/info/SideEffectInstructionChecker.java +++ b/src/proguard/optimize/info/SideEffectInstructionChecker.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2014 Eric Lafortune (eric@graphics.cornell.edu) * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -33,7 +33,8 @@ import java.util.*; /** * This class can tell whether an instruction has any side effects outside of - * its method. Return instructions can be included or not. + * its method. Return instructions and local field accesses can be included or + * not. * * @see ReadWriteFieldMarker * @see StaticInitializerContainingClassMarker @@ -47,17 +48,25 @@ implements InstructionVisitor, ConstantVisitor, MemberVisitor { - private static final boolean OPTIMIZE_CONSERVATIVELY = System.getProperty("optimize.conservatively") != null; + static final boolean OPTIMIZE_CONSERVATIVELY = System.getProperty("optimize.conservatively") != null; private final boolean includeReturnInstructions; private final boolean includeLocalFieldAccess; // A return value for the visitor methods. + private boolean writingField; private Clazz referencingClass; private boolean hasSideEffects; + /** + * Creates a new SideEffectInstructionChecker + * @param includeReturnInstructions specifies whether return instructions + * count as side effects. + * @param includeLocalFieldAccess specifies whether reading or writing + * local fields counts as side effects. + */ public SideEffectInstructionChecker(boolean includeReturnInstructions, boolean includeLocalFieldAccess) { @@ -96,6 +105,10 @@ implements InstructionVisitor, // Check for instructions that might cause side effects. switch (opcode) { + case InstructionConstants.OP_IDIV: + case InstructionConstants.OP_LDIV: + case InstructionConstants.OP_IREM: + case InstructionConstants.OP_LREM: case InstructionConstants.OP_IALOAD: case InstructionConstants.OP_LALOAD: case InstructionConstants.OP_FALOAD: @@ -104,8 +117,13 @@ implements InstructionVisitor, case InstructionConstants.OP_BALOAD: case InstructionConstants.OP_CALOAD: case InstructionConstants.OP_SALOAD: + case InstructionConstants.OP_NEWARRAY: + case InstructionConstants.OP_ARRAYLENGTH: + case InstructionConstants.OP_ANEWARRAY: + case InstructionConstants.OP_MULTIANEWARRAY: // These instructions strictly taken may cause a side effect - // (NullPointerException, ArrayIndexOutOfBoundsException). + // (ArithmeticException, NullPointerException, + // ArrayIndexOutOfBoundsException, NegativeArraySizeException). hasSideEffects = OPTIMIZE_CONSERVATIVELY; break; @@ -160,16 +178,53 @@ implements InstructionVisitor, switch (opcode) { case InstructionConstants.OP_GETSTATIC: + // Check if accessing the field might cause any side effects. + writingField = false; + clazz.constantPoolEntryAccept(constantInstruction.constantIndex, this); + break; + case InstructionConstants.OP_PUTSTATIC: - case InstructionConstants.OP_INVOKESPECIAL: - case InstructionConstants.OP_INVOKESTATIC: - // Check if the field is write-only or volatile, or if the - // invoked method is causing any side effects. + // Check if accessing the field might cause any side effects. + writingField = true; clazz.constantPoolEntryAccept(constantInstruction.constantIndex, this); break; case InstructionConstants.OP_GETFIELD: + if (OPTIMIZE_CONSERVATIVELY) + { + // These instructions strictly taken may cause a side effect + // (NullPointerException). + hasSideEffects = true; + } + else + { + // Check if the field is write-only or volatile. + writingField = false; + clazz.constantPoolEntryAccept(constantInstruction.constantIndex, this); + } + break; + case InstructionConstants.OP_PUTFIELD: + if (OPTIMIZE_CONSERVATIVELY) + { + // These instructions strictly taken may cause a side effect + // (NullPointerException). + hasSideEffects = true; + } + else + { + // Check if the field is write-only or volatile. + writingField = true; + clazz.constantPoolEntryAccept(constantInstruction.constantIndex, this); + } + break; + + case InstructionConstants.OP_INVOKESPECIAL: + case InstructionConstants.OP_INVOKESTATIC: + // Check if the invoked method is causing any side effects. + clazz.constantPoolEntryAccept(constantInstruction.constantIndex, this); + break; + case InstructionConstants.OP_INVOKEVIRTUAL: case InstructionConstants.OP_INVOKEINTERFACE: case InstructionConstants.OP_INVOKEDYNAMIC: @@ -181,15 +236,16 @@ implements InstructionVisitor, } else { - // Check if the field is write-only or volatile, or if the - // invoked method is causing any side effects. + // Check if the invoked method is causing any side effects. clazz.constantPoolEntryAccept(constantInstruction.constantIndex, this); } break; + case InstructionConstants.OP_ANEWARRAY: case InstructionConstants.OP_CHECKCAST: + case InstructionConstants.OP_MULTIANEWARRAY: // This instructions strictly taken may cause a side effect - // (ClassCastException). + // (ClassCastException, NegativeArraySizeException). hasSideEffects = OPTIMIZE_CONSERVATIVELY; break; } @@ -252,11 +308,9 @@ implements InstructionVisitor, { hasSideEffects = (includeLocalFieldAccess || !programClass.equals(referencingClass)) && - ((ReadWriteFieldMarker.isRead(programField) && - ReadWriteFieldMarker.isWritten(programField)) || - ((programField.getAccessFlags() & ClassConstants.INTERNAL_ACC_VOLATILE) != 0) || - (!programClass.equals(referencingClass) && - !initializedSuperClasses(referencingClass).containsAll(initializedSuperClasses(programClass)))); + ((writingField && ReadWriteFieldMarker.isRead(programField)) || + (programField.getAccessFlags() & ClassConstants.ACC_VOLATILE) != 0 || + mayHaveSideEffects(referencingClass, programClass)); } @@ -267,8 +321,7 @@ implements InstructionVisitor, hasSideEffects = !NoSideEffectMethodMarker.hasNoSideEffects(programMethod) && (SideEffectMethodMarker.hasSideEffects(programMethod) || - (!programClass.equals(referencingClass) && - !initializedSuperClasses(referencingClass).containsAll(initializedSuperClasses(programClass)))); + mayHaveSideEffects(referencingClass, programClass)); } @@ -285,6 +338,21 @@ implements InstructionVisitor, } + // Small utility methods. + + /** + * Returns whether a field reference or method invocation from the + * referencing class to the referenced class might have any side + * effects. + */ + private boolean mayHaveSideEffects(Clazz referencingClass, Clazz referencedClass) + { + return + !referencedClass.equals(referencingClass) && + !initializedSuperClasses(referencingClass).containsAll(initializedSuperClasses(referencedClass)); + } + + /** * Returns the set of superclasses and interfaces that are initialized. */ @@ -296,8 +364,8 @@ implements InstructionVisitor, // static initializers. clazz.hierarchyAccept(true, true, true, false, new StaticInitializerContainingClassFilter( - new NamedMethodVisitor(ClassConstants.INTERNAL_METHOD_NAME_CLINIT, - ClassConstants.INTERNAL_METHOD_TYPE_CLINIT, + new NamedMethodVisitor(ClassConstants.METHOD_NAME_CLINIT, + ClassConstants.METHOD_TYPE_CLINIT, new SideEffectMethodFilter( new MemberToClassVisitor( new ClassCollector(set)))))); diff --git a/src/proguard/optimize/info/SideEffectMethodFilter.java b/src/proguard/optimize/info/SideEffectMethodFilter.java index 52e072a..3821870 100644 --- a/src/proguard/optimize/info/SideEffectMethodFilter.java +++ b/src/proguard/optimize/info/SideEffectMethodFilter.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2014 Eric Lafortune (eric@graphics.cornell.edu) * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/optimize/info/SideEffectMethodMarker.java b/src/proguard/optimize/info/SideEffectMethodMarker.java index f7953c0..79584e1 100644 --- a/src/proguard/optimize/info/SideEffectMethodMarker.java +++ b/src/proguard/optimize/info/SideEffectMethodMarker.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2014 Eric Lafortune (eric@graphics.cornell.edu) * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -86,8 +86,8 @@ implements ClassPoolVisitor, // Initialize the return value. hasSideEffects = (programMethod.getAccessFlags() & - (ClassConstants.INTERNAL_ACC_NATIVE | - ClassConstants.INTERNAL_ACC_SYNCHRONIZED)) != 0; + (ClassConstants.ACC_NATIVE | + ClassConstants.ACC_SYNCHRONIZED)) != 0; // Look further if the method hasn't been marked yet. if (!hasSideEffects) @@ -132,7 +132,7 @@ implements ClassPoolVisitor, int length = codeAttribute.u4codeLength; SideEffectInstructionChecker checker = - method.getName(clazz).equals(ClassConstants.INTERNAL_METHOD_NAME_CLINIT) ? + method.getName(clazz).equals(ClassConstants.METHOD_NAME_CLINIT) ? initializerSideEffectInstructionChecker : sideEffectInstructionChecker; diff --git a/src/proguard/optimize/info/SimpleEnumFilter.java b/src/proguard/optimize/info/SimpleEnumFilter.java new file mode 100644 index 0000000..8db58f4 --- /dev/null +++ b/src/proguard/optimize/info/SimpleEnumFilter.java @@ -0,0 +1,63 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2014 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package proguard.optimize.info; + +import proguard.classfile.*; +import proguard.classfile.visitor.ClassVisitor; + +/** + * This ClassVisitor delegates all its method calls to another ClassVisitor, + * but only for Clazz objects that are simple enums. + * + * @see SimpleEnumMarker + * @author Eric Lafortune + */ +public class SimpleEnumFilter +implements ClassVisitor +{ + private final ClassVisitor classVisitor; + + + public SimpleEnumFilter(ClassVisitor classVisitor) + { + this.classVisitor = classVisitor; + } + + + // Implementations for ClassVisitor. + + public void visitProgramClass(ProgramClass programClass) + { + if (SimpleEnumMarker.isSimpleEnum(programClass)) + { + classVisitor.visitProgramClass(programClass); + } + } + + + public void visitLibraryClass(LibraryClass libraryClass) + { + if (SimpleEnumMarker.isSimpleEnum(libraryClass)) + { + classVisitor.visitLibraryClass(libraryClass); + } + } +}
\ No newline at end of file diff --git a/src/proguard/optimize/info/SimpleEnumMarker.java b/src/proguard/optimize/info/SimpleEnumMarker.java new file mode 100644 index 0000000..dfb1ccb --- /dev/null +++ b/src/proguard/optimize/info/SimpleEnumMarker.java @@ -0,0 +1,75 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2014 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package proguard.optimize.info; + +import proguard.classfile.*; +import proguard.classfile.visitor.ClassVisitor; + +/** + * This ClassVisitor marks all program classes that it visits with a given + * flag for simple enums. + * + * @author Eric Lafortune + */ +public class SimpleEnumMarker +implements ClassVisitor +{ + private final boolean simple; + + + /** + * Creates a new SimpleEnumMarker that marks visited classes with the + * given flag. + */ + public SimpleEnumMarker(boolean simple) + { + this.simple = simple; + } + + + // Implementations for ClassVisitor. + + public void visitLibraryClass(LibraryClass libraryClass) {} + + public void visitProgramClass(ProgramClass programClass) + { + setSimpleEnum(programClass); + } + + + // Small utility methods. + + private void setSimpleEnum(Clazz clazz) + { + ClassOptimizationInfo info = ClassOptimizationInfo.getClassOptimizationInfo(clazz); + if (info != null) + { + info.setSimpleEnum(simple); + } + } + + + public static boolean isSimpleEnum(Clazz clazz) + { + ClassOptimizationInfo info = ClassOptimizationInfo.getClassOptimizationInfo(clazz); + return info != null && info.isSimpleEnum(); + } +}
\ No newline at end of file diff --git a/src/proguard/optimize/info/StaticInitializerContainingClassFilter.java b/src/proguard/optimize/info/StaticInitializerContainingClassFilter.java index 36aa392..ccb3d31 100644 --- a/src/proguard/optimize/info/StaticInitializerContainingClassFilter.java +++ b/src/proguard/optimize/info/StaticInitializerContainingClassFilter.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2014 Eric Lafortune (eric@graphics.cornell.edu) * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/optimize/info/StaticInitializerContainingClassMarker.java b/src/proguard/optimize/info/StaticInitializerContainingClassMarker.java index 3a7e642..12e0079 100644 --- a/src/proguard/optimize/info/StaticInitializerContainingClassMarker.java +++ b/src/proguard/optimize/info/StaticInitializerContainingClassMarker.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2014 Eric Lafortune (eric@graphics.cornell.edu) * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -22,7 +22,7 @@ package proguard.optimize.info; import proguard.classfile.*; import proguard.classfile.util.SimplifiedVisitor; -import proguard.classfile.visitor.*; +import proguard.classfile.visitor.ClassVisitor; /** * This ClassVisitor marks all classes that contain static initializers. @@ -37,8 +37,8 @@ implements ClassVisitor public void visitAnyClass(Clazz clazz) { - if (clazz.findMethod(ClassConstants.INTERNAL_METHOD_NAME_CLINIT, - ClassConstants.INTERNAL_METHOD_TYPE_CLINIT) != null) + if (clazz.findMethod(ClassConstants.METHOD_NAME_CLINIT, + ClassConstants.METHOD_TYPE_CLINIT) != null) { setStaticInitializer(clazz); } diff --git a/src/proguard/optimize/info/SuperInvocationMarker.java b/src/proguard/optimize/info/SuperInvocationMarker.java index 37b118a..990a9ea 100644 --- a/src/proguard/optimize/info/SuperInvocationMarker.java +++ b/src/proguard/optimize/info/SuperInvocationMarker.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2014 Eric Lafortune (eric@graphics.cornell.edu) * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -69,7 +69,7 @@ implements InstructionVisitor, { invokesSuperMethods = !clazz.equals(refConstant.referencedClass) && - !refConstant.getName(clazz).equals(ClassConstants.INTERNAL_METHOD_NAME_INIT); + !refConstant.getName(clazz).equals(ClassConstants.METHOD_NAME_INIT); } diff --git a/src/proguard/optimize/info/VariableUsageMarker.java b/src/proguard/optimize/info/VariableUsageMarker.java index b189ca9..1d3c04a 100644 --- a/src/proguard/optimize/info/VariableUsageMarker.java +++ b/src/proguard/optimize/info/VariableUsageMarker.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2014 Eric Lafortune (eric@graphics.cornell.edu) * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/optimize/peephole/BranchTargetFinder.java b/src/proguard/optimize/peephole/BranchTargetFinder.java index 79499f1..507c1f8 100644 --- a/src/proguard/optimize/peephole/BranchTargetFinder.java +++ b/src/proguard/optimize/peephole/BranchTargetFinder.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2014 Eric Lafortune (eric@graphics.cornell.edu) * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -50,9 +50,14 @@ implements AttributeVisitor, private static boolean DEBUG = System.getProperty("btf") != null; //*/ - public static final int NONE = -2; - public static final int AT_METHOD_ENTRY = -1; + public static final int NONE = -1; + // We'll explicitly mark instructions that are not part of a subroutine, + // with NO_SUBROUTINE. Subroutines may just branch back into normal code + // (e.g. due to a break instruction in Java code), and we want to avoid + // marking such normal code as subroutine. The first mark wins, so we're + // assuming that such code is marked as normal code before it is marked + // as subroutine. public static final int UNKNOWN = -1; public static final int NO_SUBROUTINE = -2; @@ -355,24 +360,17 @@ implements AttributeVisitor, currentSubroutineStart = NO_SUBROUTINE; recentCreationOffsetIndex = 0; - // Initialize the stack of 'new' instruction offsets if this method - // is an instance initializer. - if (method.getName(clazz).equals(ClassConstants.INTERNAL_METHOD_NAME_INIT)) - { - recentCreationOffsets[recentCreationOffsetIndex++] = AT_METHOD_ENTRY; - } - // Mark branch targets by going over all instructions. codeAttribute.instructionsAccept(clazz, method, this); + + // Mark branch targets in the exception table. + codeAttribute.exceptionsAccept(clazz, method, this); } while (repeat); // The end of the code is a branch target sentinel. instructionMarks[codeLength] = BRANCH_TARGET; - // Mark branch targets in the exception table. - codeAttribute.exceptionsAccept(clazz, method, this); - if (containsSubroutines) { // Set the subroutine returning flag and the subroutine end at each @@ -487,33 +485,39 @@ implements AttributeVisitor, // Check if this is an instruction of a subroutine. checkSubroutine(offset); - // Check if the instruction is a 'new' instruction. - if (constantInstruction.opcode == InstructionConstants.OP_NEW) + byte opcode = constantInstruction.opcode; + if (opcode == InstructionConstants.OP_NEW) { // Push the 'new' instruction offset on the stack. recentCreationOffsets[recentCreationOffsetIndex++] = offset; } - else + else if (opcode == InstructionConstants.OP_INVOKESPECIAL) { - // Check if the instruction is an initializer invocation. + // Is it calling an instance initializer? isInitializer = false; clazz.constantPoolEntryAccept(constantInstruction.constantIndex, this); if (isInitializer) { - // Pop the 'new' instruction offset from the stack. - int recentCreationOffset = recentCreationOffsets[--recentCreationOffsetIndex]; + // Do we have any 'new' instruction offsets on the stack? + if (recentCreationOffsetIndex > 0) + { + // Pop the 'new' instruction offset from the stack. + int recentCreationOffset = recentCreationOffsets[--recentCreationOffsetIndex]; - // Fill it out in the creation offsets. - creationOffsets[offset] = recentCreationOffset; + // Link the creation offset and the initialization offset. + // TODO: There could be multiple initialization offsets. + creationOffsets[offset] = recentCreationOffset; - // Fill out the initialization offsets. - if (recentCreationOffset == AT_METHOD_ENTRY) - { - superInitializationOffset = offset; + initializationOffsets[recentCreationOffset] = offset; } else { - initializationOffsets[recentCreationOffset] = offset; + // Remember the super initialization offset. + // TODO: There could be multiple initialization offsets. + // For instance, in the constructor of the generated class + // groovy.inspect.swingui.GeneratedBytecodeAwareGroovyClassLoader + // in groovy-all-2.2.1.jar. + superInitializationOffset = offset; } } } @@ -613,7 +617,8 @@ implements AttributeVisitor, public void visitMethodrefConstant(Clazz clazz, MethodrefConstant methodrefConstant) { - isInitializer = methodrefConstant.getName(clazz).equals(ClassConstants.INTERNAL_METHOD_NAME_INIT); + // Remember whether the method is an initializer. + isInitializer = methodrefConstant.getName(clazz).equals(ClassConstants.METHOD_NAME_INIT); } @@ -621,10 +626,24 @@ implements AttributeVisitor, public void visitExceptionInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, ExceptionInfo exceptionInfo) { + int startPC = exceptionInfo.u2startPC; + int endPC = exceptionInfo.u2endPC; + int handlerPC = exceptionInfo.u2handlerPC; + // Mark the exception offsets. - instructionMarks[exceptionInfo.u2startPC] |= EXCEPTION_START; - instructionMarks[exceptionInfo.u2endPC] |= EXCEPTION_END; - instructionMarks[exceptionInfo.u2handlerPC] |= EXCEPTION_HANDLER; + instructionMarks[startPC] |= EXCEPTION_START; + instructionMarks[endPC] |= EXCEPTION_END; + instructionMarks[handlerPC] |= EXCEPTION_HANDLER; + + // Mark the handler as part of a subroutine if necessary. + if (subroutineStarts[handlerPC] == UNKNOWN && + subroutineStarts[startPC] != UNKNOWN) + { + subroutineStarts[handlerPC] = subroutineStarts[startPC]; + + // We'll have to go over all instructions again. + repeat = true; + } } @@ -737,12 +756,6 @@ implements AttributeVisitor, { // Mark the subroutine start. subroutineStarts[offset] = currentSubroutineStart; - - if (currentSubroutineStart >= 0) - { - // Mark the subroutine end at the subroutine start. - subroutineEnds[currentSubroutineStart] = offset; - } } } } diff --git a/src/proguard/optimize/peephole/ClassFinalizer.java b/src/proguard/optimize/peephole/ClassFinalizer.java index 378f972..1529c2c 100644 --- a/src/proguard/optimize/peephole/ClassFinalizer.java +++ b/src/proguard/optimize/peephole/ClassFinalizer.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2014 Eric Lafortune (eric@graphics.cornell.edu) * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -21,8 +21,8 @@ package proguard.optimize.peephole; import proguard.classfile.*; -import proguard.classfile.util.*; -import proguard.classfile.visitor.*; +import proguard.classfile.util.SimplifiedVisitor; +import proguard.classfile.visitor.ClassVisitor; import proguard.optimize.KeepMarker; /** @@ -66,13 +66,13 @@ implements ClassVisitor // and it is not being kept, // and it doesn't have any subclasses, // then make it final. - if ((programClass.u2accessFlags & (ClassConstants.INTERNAL_ACC_FINAL | - ClassConstants.INTERNAL_ACC_INTERFACE | - ClassConstants.INTERNAL_ACC_ABSTRACT)) == 0 && + if ((programClass.u2accessFlags & (ClassConstants.ACC_FINAL | + ClassConstants.ACC_INTERFACE | + ClassConstants.ACC_ABSTRACT)) == 0 && !KeepMarker.isKept(programClass) && programClass.subClasses == null) { - programClass.u2accessFlags |= ClassConstants.INTERNAL_ACC_FINAL; + programClass.u2accessFlags |= ClassConstants.ACC_FINAL; // Visit the class, if required. if (extraClassVisitor != null) diff --git a/src/proguard/optimize/peephole/ClassMerger.java b/src/proguard/optimize/peephole/ClassMerger.java index aa40c75..9bcc993 100644 --- a/src/proguard/optimize/peephole/ClassMerger.java +++ b/src/proguard/optimize/peephole/ClassMerger.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2014 Eric Lafortune (eric@graphics.cornell.edu) * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -49,9 +49,11 @@ implements ClassVisitor, ConstantVisitor { //* - private static final boolean DEBUG = false; + private static final boolean DEBUG = false; + private static final boolean DETAILS = false; /*/ - private static boolean DEBUG = System.getProperty("cm") != null; + private static boolean DEBUG = System.getProperty("cm") != null; + private static boolean DETAILS = System.getProperty("cmd") != null; //*/ @@ -152,7 +154,9 @@ implements ClassVisitor, // Don't merge annotation classes, with all their introspection and // infinite recursion. - (programClass.getAccessFlags() & ClassConstants.INTERNAL_ACC_ANNOTATTION) == 0 && + (programClass.getAccessFlags() & ClassConstants.ACC_ANNOTATTION) == 0 && + + (!DETAILS || print(programClass, "Package visibility?")) && // Only merge classes if we can change the access permissions, or // if they are in the same package, or @@ -161,77 +165,117 @@ implements ClassVisitor, (allowAccessModification || ((programClass.getAccessFlags() & targetClass.getAccessFlags() & - ClassConstants.INTERNAL_ACC_PUBLIC) != 0 && + ClassConstants.ACC_PUBLIC) != 0 && !PackageVisibleMemberContainingClassMarker.containsPackageVisibleMembers(programClass) && !PackageVisibleMemberInvokingClassMarker.invokesPackageVisibleMembers(programClass)) || ClassUtil.internalPackageName(programClass.getName()).equals( ClassUtil.internalPackageName(targetClass.getName()))) && + (!DETAILS || print(programClass, "Interface/abstract/single?")) && + // Only merge two classes or two interfaces or two abstract classes, - // or a class into an interface with a single implementation. + // or a single implementation into its interface. ((programClass.getAccessFlags() & - (ClassConstants.INTERNAL_ACC_INTERFACE | - ClassConstants.INTERNAL_ACC_ABSTRACT)) == + (ClassConstants.ACC_INTERFACE | + ClassConstants.ACC_ABSTRACT)) == (targetClass.getAccessFlags() & - (ClassConstants.INTERNAL_ACC_INTERFACE | - ClassConstants.INTERNAL_ACC_ABSTRACT)) || + (ClassConstants.ACC_INTERFACE | + ClassConstants.ACC_ABSTRACT)) || (isOnlySubClass(programClass, targetClass) && + programClass.getSuperClass() != null && (programClass.getSuperClass().equals(targetClass) || programClass.getSuperClass().equals(targetClass.getSuperClass())))) && + (!DETAILS || print(programClass, "Indirect implementation?")) && + // One class must not implement the other class indirectly. !indirectlyImplementedInterfaces(programClass).contains(targetClass) && !targetClass.extendsOrImplements(programClass) && + (!DETAILS || print(programClass, "Interfaces same subinterfaces?")) && + + // Interfaces must have exactly the same subinterfaces, not + // counting themselves, to avoid any loops in the interface + // hierarchy. + ((programClass.getAccessFlags() & ClassConstants.ACC_INTERFACE) == 0 || + (targetClass.getAccessFlags() & ClassConstants.ACC_INTERFACE) == 0 || + subInterfaces(programClass, targetClass).equals(subInterfaces(targetClass, programClass))) && + + (!DETAILS || print(programClass, "Same initialized superclasses?")) && + // The two classes must have the same superclasses and interfaces // with static initializers. initializedSuperClasses(programClass).equals(initializedSuperClasses(targetClass)) && + (!DETAILS || print(programClass, "Same instanceofed superclasses?")) && + // The two classes must have the same superclasses and interfaces // that are tested with 'instanceof'. instanceofedSuperClasses(programClass).equals(instanceofedSuperClasses(targetClass)) && + (!DETAILS || print(programClass, "Same caught superclasses?")) && + // The two classes must have the same superclasses that are caught // as exceptions. caughtSuperClasses(programClass).equals(caughtSuperClasses(targetClass)) && + (!DETAILS || print(programClass, "Not .classed?")) && + // The two classes must not both be part of a .class construct. !(DotClassMarker.isDotClassed(programClass) && DotClassMarker.isDotClassed(targetClass)) && + (!DETAILS || print(programClass, "No clashing fields?")) && + // The classes must not have clashing fields. !haveAnyIdenticalFields(programClass, targetClass) && + (!DETAILS || print(programClass, "No unwanted fields?")) && + // The two classes must not introduce any unwanted fields. !introducesUnwantedFields(programClass, targetClass) && !introducesUnwantedFields(targetClass, programClass) && + (!DETAILS || print(programClass, "No shadowed fields?")) && + // The two classes must not shadow each others fields. !shadowsAnyFields(programClass, targetClass) && !shadowsAnyFields(targetClass, programClass) && + (!DETAILS || print(programClass, "No clashing methods?")) && + // The classes must not have clashing methods. !haveAnyIdenticalMethods(programClass, targetClass) && + (!DETAILS || print(programClass, "No abstract methods?")) && + // The classes must not introduce abstract methods, unless // explicitly allowed. (mergeInterfacesAggressively || (!introducesUnwantedAbstractMethods(programClass, targetClass) && !introducesUnwantedAbstractMethods(targetClass, programClass))) && + (!DETAILS || print(programClass, "No overridden methods?")) && + // The classes must not override each others concrete methods. !overridesAnyMethods(programClass, targetClass) && !overridesAnyMethods(targetClass, programClass) && + (!DETAILS || print(programClass, "No shadowed methods?")) && + // The classes must not shadow each others non-private methods. !shadowsAnyMethods(programClass, targetClass) && !shadowsAnyMethods(targetClass, programClass)) { + // We're not actually merging the classes, but only copying the + // contents from the source class to the target class. We'll + // then let all other classes point to it. The shrinking step + // will finally remove the source class. if (DEBUG) { System.out.println("ClassMerger ["+programClass.getName()+"] -> ["+targetClass.getName()+"]"); - System.out.println(" Source interface? ["+((programClass.getAccessFlags() & ClassConstants.INTERNAL_ACC_INTERFACE)!=0)+"]"); - System.out.println(" Target interface? ["+((targetClass.getAccessFlags() & ClassConstants.INTERNAL_ACC_INTERFACE)!=0)+"]"); + System.out.println(" Source interface? ["+((programClass.getAccessFlags() & ClassConstants.ACC_INTERFACE)!=0)+"]"); + System.out.println(" Target interface? ["+((targetClass.getAccessFlags() & ClassConstants.ACC_INTERFACE)!=0)+"]"); System.out.println(" Source subclasses ["+programClass.subClasses+"]"); System.out.println(" Target subclasses ["+targetClass.subClasses+"]"); System.out.println(" Source superclass ["+programClass.getSuperClass().getName()+"]"); @@ -249,17 +293,21 @@ implements ClassVisitor, targetClass.u2accessFlags = ((targetAccessFlags & sourceAccessFlags) & - (ClassConstants.INTERNAL_ACC_INTERFACE | - ClassConstants.INTERNAL_ACC_ABSTRACT)) | + (ClassConstants.ACC_INTERFACE | + ClassConstants.ACC_ABSTRACT)) | ((targetAccessFlags | sourceAccessFlags) & - (ClassConstants.INTERNAL_ACC_PUBLIC | - ClassConstants.INTERNAL_ACC_SUPER | - ClassConstants.INTERNAL_ACC_ANNOTATTION | - ClassConstants.INTERNAL_ACC_ENUM)); - - // Copy over the superclass, unless it's the target class itself. - //if (!targetClass.getName().equals(programClass.getSuperName())) + (ClassConstants.ACC_PUBLIC | + ClassConstants.ACC_SUPER | + ClassConstants.ACC_ANNOTATTION | + ClassConstants.ACC_ENUM)); + + // Copy over the superclass, if it's a non-interface class being + // merged into an interface class. + // However, we're currently never merging in a way that changes the + // superclass. + //if ((programClass.getAccessFlags() & ClassConstants.ACC_INTERFACE) == 0 && + // (targetClass.getAccessFlags() & ClassConstants.ACC_INTERFACE) != 0) //{ // targetClass.u2superClass = // new ConstantAdder(targetClass).addConstant(programClass, programClass.u2superClass); @@ -267,6 +315,9 @@ implements ClassVisitor, // Copy over the interfaces that aren't present yet and that // wouldn't cause loops in the class hierarchy. + // Note that the code shouldn't be iterating over the original + // list at this point. This is why we only add subclasses in + // a separate step. programClass.interfaceConstantsAccept( new ExceptClassConstantFilter(targetClass.getName(), new ImplementedClassConstantFilter(targetClass, @@ -282,10 +333,11 @@ implements ClassVisitor, // Copy over the other attributes. programClass.attributesAccept( - new AttributeNameFilter(new NotMatcher(new OrMatcher(new OrMatcher( - new FixedStringMatcher(ClassConstants.ATTR_SourceFile), - new FixedStringMatcher(ClassConstants.ATTR_InnerClasses)), - new FixedStringMatcher(ClassConstants.ATTR_EnclosingMethod))), + new AttributeNameFilter(new NotMatcher( + new OrMatcher(new FixedStringMatcher(ClassConstants.ATTR_BootstrapMethods), + new OrMatcher(new FixedStringMatcher(ClassConstants.ATTR_SourceFile), + new OrMatcher(new FixedStringMatcher(ClassConstants.ATTR_InnerClasses), + new FixedStringMatcher(ClassConstants.ATTR_EnclosingMethod))))), new AttributeAdder(targetClass, true))); // Update the optimization information of the target class. @@ -314,6 +366,14 @@ implements ClassVisitor, } + private boolean print(ProgramClass programClass, String message) + { + System.out.println("Merge ["+targetClass.getName()+"] <- ["+programClass.getName()+"] "+message); + + return true; + } + + // Small utility methods. /** @@ -352,6 +412,23 @@ implements ClassVisitor, /** + * Returns the set of interface subclasses, not including the given class. + */ + private Set subInterfaces(Clazz clazz, Clazz exceptClass) + { + Set set = new HashSet(); + + // Visit all subclasses, collecting the interface classes. + clazz.hierarchyAccept(false, false, false, true, + new ClassAccessFilter(ClassConstants.ACC_INTERFACE, 0, + new ExceptClassesFilter(new Clazz[] { exceptClass }, + new ClassCollector(set)))); + + return set; + } + + + /** * Returns the set of superclasses and interfaces that are initialized. */ private Set initializedSuperClasses(Clazz clazz) @@ -392,7 +469,7 @@ implements ClassVisitor, private Set caughtSuperClasses(Clazz clazz) { // Don't bother if this isn't an exception at all. - if (!clazz.extends_(ClassConstants.INTERNAL_NAME_JAVA_LANG_THROWABLE)) + if (!clazz.extends_(ClassConstants.NAME_JAVA_LANG_THROWABLE)) { return Collections.EMPTY_SET; } @@ -410,8 +487,8 @@ implements ClassVisitor, /** - * Returns whether the two given classes have class members with the same - * name and descriptor. + * Returns whether the two given classes have fields with the same + * names and descriptors. */ private boolean haveAnyIdenticalFields(Clazz clazz, Clazz targetClass) { @@ -445,7 +522,7 @@ implements ClassVisitor, MemberCounter counter = new MemberCounter(); // Count all non-static fields in the the source class. - programClass.fieldsAccept(new MemberAccessFilter(0, ClassConstants.INTERNAL_ACC_STATIC, + programClass.fieldsAccept(new MemberAccessFilter(0, ClassConstants.ACC_STATIC, counter)); return counter.getCount() > 0; @@ -465,7 +542,7 @@ implements ClassVisitor, clazz.hierarchyAccept(true, false, false, true, new AllFieldVisitor( new SimilarMemberVisitor(targetClass, true, true, true, false, - new MemberAccessFilter(0, ClassConstants.INTERNAL_ACC_PRIVATE, + new MemberAccessFilter(0, ClassConstants.ACC_PRIVATE, counter)))); return counter.getCount() > 0; @@ -482,9 +559,9 @@ implements ClassVisitor, // Visit all non-abstract methods, counting the ones that are also // present in the target class. - clazz.methodsAccept(new MemberAccessFilter(0, ClassConstants.INTERNAL_ACC_ABSTRACT, + clazz.methodsAccept(new MemberAccessFilter(0, ClassConstants.ACC_ABSTRACT, new SimilarMemberVisitor(targetClass, true, false, false, false, - new MemberAccessFilter(0, ClassConstants.INTERNAL_ACC_ABSTRACT, + new MemberAccessFilter(0, ClassConstants.ACC_ABSTRACT, counter)))); return counter.getCount() > 0; @@ -501,8 +578,8 @@ implements ClassVisitor, // It's ok if the target class is already abstract and it has at most // the class as a subclass. if ((targetClass.getAccessFlags() & - (ClassConstants.INTERNAL_ACC_ABSTRACT | - ClassConstants.INTERNAL_ACC_INTERFACE)) != 0 && + (ClassConstants.ACC_ABSTRACT | + ClassConstants.ACC_INTERFACE)) != 0 && (targetClass.subClasses == null || isOnlySubClass(clazz, targetClass))) { @@ -514,12 +591,12 @@ implements ClassVisitor, // Collect all abstract methods, and similar abstract methods in the // class hierarchy of the target class. - clazz.methodsAccept(new MemberAccessFilter(ClassConstants.INTERNAL_ACC_ABSTRACT, 0, + clazz.methodsAccept(new MemberAccessFilter(ClassConstants.ACC_ABSTRACT, 0, new MultiMemberVisitor(new MemberVisitor[] { counter, new SimilarMemberVisitor(targetClass, true, true, true, false, - new MemberAccessFilter(ClassConstants.INTERNAL_ACC_ABSTRACT, 0, + new MemberAccessFilter(ClassConstants.ACC_ABSTRACT, 0, new MemberCollector(targetSet))) }))); @@ -537,11 +614,11 @@ implements ClassVisitor, // Visit all non-private non-static methods, counting the ones that are // being overridden in the class hierarchy of the target class. - clazz.methodsAccept(new MemberAccessFilter(0, ClassConstants.INTERNAL_ACC_PRIVATE | ClassConstants.INTERNAL_ACC_STATIC | ClassConstants.INTERNAL_ACC_ABSTRACT, - new MemberNameFilter(new NotMatcher(new FixedStringMatcher(ClassConstants.INTERNAL_METHOD_NAME_CLINIT)), - new MemberNameFilter(new NotMatcher(new FixedStringMatcher(ClassConstants.INTERNAL_METHOD_NAME_INIT)), + clazz.methodsAccept(new MemberAccessFilter(0, ClassConstants.ACC_PRIVATE | ClassConstants.ACC_STATIC | ClassConstants.ACC_ABSTRACT, + new MemberNameFilter(new NotMatcher(new FixedStringMatcher(ClassConstants.METHOD_NAME_CLINIT)), + new MemberNameFilter(new NotMatcher(new FixedStringMatcher(ClassConstants.METHOD_NAME_INIT)), new SimilarMemberVisitor(targetClass, true, true, false, false, - new MemberAccessFilter(0, ClassConstants.INTERNAL_ACC_PRIVATE | ClassConstants.INTERNAL_ACC_STATIC | ClassConstants.INTERNAL_ACC_ABSTRACT, + new MemberAccessFilter(0, ClassConstants.ACC_PRIVATE | ClassConstants.ACC_STATIC | ClassConstants.ACC_ABSTRACT, counter)))))); return counter.getCount() > 0; @@ -560,20 +637,20 @@ implements ClassVisitor, // non-private methods in the class hierarchy of the target class. clazz.hierarchyAccept(true, false, false, true, new AllMethodVisitor( - new MemberAccessFilter(ClassConstants.INTERNAL_ACC_PRIVATE, 0, - new MemberNameFilter(new NotMatcher(new FixedStringMatcher(ClassConstants.INTERNAL_METHOD_NAME_INIT)), + new MemberAccessFilter(ClassConstants.ACC_PRIVATE, 0, + new MemberNameFilter(new NotMatcher(new FixedStringMatcher(ClassConstants.METHOD_NAME_INIT)), new SimilarMemberVisitor(targetClass, true, true, true, false, - new MemberAccessFilter(0, ClassConstants.INTERNAL_ACC_PRIVATE, + new MemberAccessFilter(0, ClassConstants.ACC_PRIVATE, counter)))))); // Visit all static methods, counting the ones that are shadowing // non-private methods in the class hierarchy of the target class. clazz.hierarchyAccept(true, false, false, true, new AllMethodVisitor( - new MemberAccessFilter(ClassConstants.INTERNAL_ACC_STATIC, 0, - new MemberNameFilter(new NotMatcher(new FixedStringMatcher(ClassConstants.INTERNAL_METHOD_NAME_CLINIT)), + new MemberAccessFilter(ClassConstants.ACC_STATIC, 0, + new MemberNameFilter(new NotMatcher(new FixedStringMatcher(ClassConstants.METHOD_NAME_CLINIT)), new SimilarMemberVisitor(targetClass, true, true, true, false, - new MemberAccessFilter(0, ClassConstants.INTERNAL_ACC_PRIVATE, + new MemberAccessFilter(0, ClassConstants.ACC_PRIVATE, counter)))))); return counter.getCount() > 0; @@ -638,4 +715,4 @@ implements ClassVisitor, // Linked methods share their optimization info. } } -}
\ No newline at end of file +} diff --git a/src/proguard/optimize/peephole/GotoCommonCodeReplacer.java b/src/proguard/optimize/peephole/GotoCommonCodeReplacer.java index 3bfd98c..a1f422f 100644 --- a/src/proguard/optimize/peephole/GotoCommonCodeReplacer.java +++ b/src/proguard/optimize/peephole/GotoCommonCodeReplacer.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2014 Eric Lafortune (eric@graphics.cornell.edu) * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/optimize/peephole/GotoGotoReplacer.java b/src/proguard/optimize/peephole/GotoGotoReplacer.java index 4a490a1..bf7292b 100644 --- a/src/proguard/optimize/peephole/GotoGotoReplacer.java +++ b/src/proguard/optimize/peephole/GotoGotoReplacer.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2014 Eric Lafortune (eric@graphics.cornell.edu) * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/optimize/peephole/GotoReturnReplacer.java b/src/proguard/optimize/peephole/GotoReturnReplacer.java index b6deec8..aa8ed9f 100644 --- a/src/proguard/optimize/peephole/GotoReturnReplacer.java +++ b/src/proguard/optimize/peephole/GotoReturnReplacer.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2014 Eric Lafortune (eric@graphics.cornell.edu) * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/optimize/peephole/HorizontalClassMerger.java b/src/proguard/optimize/peephole/HorizontalClassMerger.java index 31d3d33..ef76f9b 100644 --- a/src/proguard/optimize/peephole/HorizontalClassMerger.java +++ b/src/proguard/optimize/peephole/HorizontalClassMerger.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2014 Eric Lafortune (eric@graphics.cornell.edu) * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -20,7 +20,7 @@ */ package proguard.optimize.peephole; -import proguard.classfile.*; +import proguard.classfile.ProgramClass; import proguard.classfile.util.SimplifiedVisitor; import proguard.classfile.visitor.*; diff --git a/src/proguard/optimize/peephole/InstructionSequenceConstants.java b/src/proguard/optimize/peephole/InstructionSequenceConstants.java index 4ab9056..df96dda 100644 --- a/src/proguard/optimize/peephole/InstructionSequenceConstants.java +++ b/src/proguard/optimize/peephole/InstructionSequenceConstants.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2014 Eric Lafortune (eric@graphics.cornell.edu) * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -345,43 +345,43 @@ public class InstructionSequenceConstants new Utf8Constant("J"), new Utf8Constant("F"), new Utf8Constant("D"), - new Utf8Constant(ClassConstants.INTERNAL_NAME_JAVA_LANG_STRING), - new Utf8Constant(ClassConstants.INTERNAL_NAME_JAVA_LANG_STRING_BUFFER), - new Utf8Constant(ClassConstants.INTERNAL_NAME_JAVA_LANG_STRING_BUILDER), - new Utf8Constant(ClassConstants.INTERNAL_METHOD_NAME_EQUALS), - new Utf8Constant(ClassConstants.INTERNAL_METHOD_TYPE_EQUALS), - new Utf8Constant(ClassConstants.INTERNAL_METHOD_NAME_LENGTH), - new Utf8Constant(ClassConstants.INTERNAL_METHOD_TYPE_LENGTH), - new Utf8Constant(ClassConstants.INTERNAL_METHOD_NAME_VALUEOF), - new Utf8Constant(ClassConstants.INTERNAL_METHOD_TYPE_VALUEOF_BOOLEAN), - new Utf8Constant(ClassConstants.INTERNAL_METHOD_TYPE_VALUEOF_CHAR), - new Utf8Constant(ClassConstants.INTERNAL_METHOD_TYPE_VALUEOF_INT), - new Utf8Constant(ClassConstants.INTERNAL_METHOD_TYPE_VALUEOF_LONG), - new Utf8Constant(ClassConstants.INTERNAL_METHOD_TYPE_VALUEOF_FLOAT), - new Utf8Constant(ClassConstants.INTERNAL_METHOD_TYPE_VALUEOF_DOUBLE), - new Utf8Constant(ClassConstants.INTERNAL_METHOD_TYPE_VALUEOF_OBJECT), - new Utf8Constant(ClassConstants.INTERNAL_METHOD_NAME_INIT), - new Utf8Constant(ClassConstants.INTERNAL_METHOD_TYPE_INIT), - new Utf8Constant(ClassConstants.INTERNAL_METHOD_TYPE_STRING_VOID), - new Utf8Constant(ClassConstants.INTERNAL_METHOD_NAME_TOSTRING), - new Utf8Constant(ClassConstants.INTERNAL_METHOD_TYPE_TOSTRING), - new Utf8Constant(ClassConstants.INTERNAL_METHOD_NAME_APPEND), - new Utf8Constant(ClassConstants.INTERNAL_METHOD_TYPE_BOOLEAN_STRING_BUFFER), - new Utf8Constant(ClassConstants.INTERNAL_METHOD_TYPE_CHAR_STRING_BUFFER), - new Utf8Constant(ClassConstants.INTERNAL_METHOD_TYPE_INT_STRING_BUFFER), - new Utf8Constant(ClassConstants.INTERNAL_METHOD_TYPE_LONG_STRING_BUFFER), - new Utf8Constant(ClassConstants.INTERNAL_METHOD_TYPE_FLOAT_STRING_BUFFER), - new Utf8Constant(ClassConstants.INTERNAL_METHOD_TYPE_DOUBLE_STRING_BUFFER), - new Utf8Constant(ClassConstants.INTERNAL_METHOD_TYPE_STRING_STRING_BUFFER), - new Utf8Constant(ClassConstants.INTERNAL_METHOD_TYPE_OBJECT_STRING_BUFFER), - new Utf8Constant(ClassConstants.INTERNAL_METHOD_TYPE_BOOLEAN_STRING_BUILDER), - new Utf8Constant(ClassConstants.INTERNAL_METHOD_TYPE_CHAR_STRING_BUILDER), - new Utf8Constant(ClassConstants.INTERNAL_METHOD_TYPE_INT_STRING_BUILDER), - new Utf8Constant(ClassConstants.INTERNAL_METHOD_TYPE_LONG_STRING_BUILDER), - new Utf8Constant(ClassConstants.INTERNAL_METHOD_TYPE_FLOAT_STRING_BUILDER), - new Utf8Constant(ClassConstants.INTERNAL_METHOD_TYPE_DOUBLE_STRING_BUILDER), - new Utf8Constant(ClassConstants.INTERNAL_METHOD_TYPE_STRING_STRING_BUILDER), - new Utf8Constant(ClassConstants.INTERNAL_METHOD_TYPE_OBJECT_STRING_BUILDER), + new Utf8Constant(ClassConstants.NAME_JAVA_LANG_STRING), + new Utf8Constant(ClassConstants.NAME_JAVA_LANG_STRING_BUFFER), + new Utf8Constant(ClassConstants.NAME_JAVA_LANG_STRING_BUILDER), + new Utf8Constant(ClassConstants.METHOD_NAME_EQUALS), + new Utf8Constant(ClassConstants.METHOD_TYPE_EQUALS), + new Utf8Constant(ClassConstants.METHOD_NAME_LENGTH), + new Utf8Constant(ClassConstants.METHOD_TYPE_LENGTH), + new Utf8Constant(ClassConstants.METHOD_NAME_VALUEOF), + new Utf8Constant(ClassConstants.METHOD_TYPE_VALUEOF_BOOLEAN), + new Utf8Constant(ClassConstants.METHOD_TYPE_VALUEOF_CHAR), + new Utf8Constant(ClassConstants.METHOD_TYPE_VALUEOF_INT), + new Utf8Constant(ClassConstants.METHOD_TYPE_VALUEOF_LONG), + new Utf8Constant(ClassConstants.METHOD_TYPE_VALUEOF_FLOAT), + new Utf8Constant(ClassConstants.METHOD_TYPE_VALUEOF_DOUBLE), + new Utf8Constant(ClassConstants.METHOD_TYPE_VALUEOF_OBJECT), + new Utf8Constant(ClassConstants.METHOD_NAME_INIT), + new Utf8Constant(ClassConstants.METHOD_TYPE_INIT), + new Utf8Constant(ClassConstants.METHOD_TYPE_STRING_VOID), + new Utf8Constant(ClassConstants.METHOD_NAME_TOSTRING), + new Utf8Constant(ClassConstants.METHOD_TYPE_TOSTRING), + new Utf8Constant(ClassConstants.METHOD_NAME_APPEND), + new Utf8Constant(ClassConstants.METHOD_TYPE_BOOLEAN_STRING_BUFFER), + new Utf8Constant(ClassConstants.METHOD_TYPE_CHAR_STRING_BUFFER), + new Utf8Constant(ClassConstants.METHOD_TYPE_INT_STRING_BUFFER), + new Utf8Constant(ClassConstants.METHOD_TYPE_LONG_STRING_BUFFER), + new Utf8Constant(ClassConstants.METHOD_TYPE_FLOAT_STRING_BUFFER), + new Utf8Constant(ClassConstants.METHOD_TYPE_DOUBLE_STRING_BUFFER), + new Utf8Constant(ClassConstants.METHOD_TYPE_STRING_STRING_BUFFER), + new Utf8Constant(ClassConstants.METHOD_TYPE_OBJECT_STRING_BUFFER), + new Utf8Constant(ClassConstants.METHOD_TYPE_BOOLEAN_STRING_BUILDER), + new Utf8Constant(ClassConstants.METHOD_TYPE_CHAR_STRING_BUILDER), + new Utf8Constant(ClassConstants.METHOD_TYPE_INT_STRING_BUILDER), + new Utf8Constant(ClassConstants.METHOD_TYPE_LONG_STRING_BUILDER), + new Utf8Constant(ClassConstants.METHOD_TYPE_FLOAT_STRING_BUILDER), + new Utf8Constant(ClassConstants.METHOD_TYPE_DOUBLE_STRING_BUILDER), + new Utf8Constant(ClassConstants.METHOD_TYPE_STRING_STRING_BUILDER), + new Utf8Constant(ClassConstants.METHOD_TYPE_OBJECT_STRING_BUILDER), }; public static final Instruction[][][] VARIABLE = new Instruction[][][] diff --git a/src/proguard/optimize/peephole/InstructionSequenceReplacer.java b/src/proguard/optimize/peephole/InstructionSequenceReplacer.java index 7ec1a95..6a1d872 100644 --- a/src/proguard/optimize/peephole/InstructionSequenceReplacer.java +++ b/src/proguard/optimize/peephole/InstructionSequenceReplacer.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2014 Eric Lafortune (eric@graphics.cornell.edu) * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -153,7 +153,8 @@ implements InstructionVisitor, { // Reset the instruction sequence matcher if the instruction is a branch // target or if it has already been modified. - if (branchTargetFinder.isTarget(offset) || + if ((branchTargetFinder != null && + branchTargetFinder.isTarget(offset)) || codeAttributeEditor.isModified(offset)) { instructionSequenceMatcher.reset(); diff --git a/src/proguard/optimize/peephole/InstructionSequencesReplacer.java b/src/proguard/optimize/peephole/InstructionSequencesReplacer.java index 22fb6cd..9aca05a 100644 --- a/src/proguard/optimize/peephole/InstructionSequencesReplacer.java +++ b/src/proguard/optimize/peephole/InstructionSequencesReplacer.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2014 Eric Lafortune (eric@graphics.cornell.edu) * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/optimize/peephole/MemberPrivatizer.java b/src/proguard/optimize/peephole/MemberPrivatizer.java index f57281c..420d80a 100644 --- a/src/proguard/optimize/peephole/MemberPrivatizer.java +++ b/src/proguard/optimize/peephole/MemberPrivatizer.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2014 Eric Lafortune (eric@graphics.cornell.edu) * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -72,7 +72,7 @@ implements MemberVisitor // Make the field private. programField.u2accessFlags = AccessUtil.replaceAccessFlags(programField.u2accessFlags, - ClassConstants.INTERNAL_ACC_PRIVATE); + ClassConstants.ACC_PRIVATE); // Visit the field, if required. if (extraMemberVisitor != null) @@ -88,10 +88,10 @@ implements MemberVisitor // Is the method unmarked? if (NonPrivateMemberMarker.canBeMadePrivate(programMethod)) { - // Make the method private. + // Make the method private and no longer final. programMethod.u2accessFlags = AccessUtil.replaceAccessFlags(programMethod.u2accessFlags, - ClassConstants.INTERNAL_ACC_PRIVATE); + ClassConstants.ACC_PRIVATE); // Visit the method, if required. if (extraMemberVisitor != null) diff --git a/src/proguard/optimize/peephole/MethodFinalizer.java b/src/proguard/optimize/peephole/MethodFinalizer.java index 89174ac..2ce3029 100644 --- a/src/proguard/optimize/peephole/MethodFinalizer.java +++ b/src/proguard/optimize/peephole/MethodFinalizer.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2014 Eric Lafortune (eric@graphics.cornell.edu) * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -22,7 +22,7 @@ package proguard.optimize.peephole; import proguard.classfile.*; import proguard.classfile.util.*; -import proguard.classfile.visitor.*; +import proguard.classfile.visitor.MemberVisitor; import proguard.optimize.KeepMarker; /** @@ -61,7 +61,7 @@ implements MemberVisitor // Implementations for MemberVisitor. - + public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) { String name = programMethod.getName(programClass); @@ -71,17 +71,17 @@ implements MemberVisitor // and its class is final, // or it is not being kept and it is not overridden, // then make it final. - if ((programMethod.u2accessFlags & (ClassConstants.INTERNAL_ACC_PRIVATE | - ClassConstants.INTERNAL_ACC_STATIC | - ClassConstants.INTERNAL_ACC_FINAL | - ClassConstants.INTERNAL_ACC_ABSTRACT)) == 0 && - !name.equals(ClassConstants.INTERNAL_METHOD_NAME_INIT) && - ((programClass.u2accessFlags & ClassConstants.INTERNAL_ACC_FINAL) != 0 || + if ((programMethod.u2accessFlags & (ClassConstants.ACC_PRIVATE | + ClassConstants.ACC_STATIC | + ClassConstants.ACC_FINAL | + ClassConstants.ACC_ABSTRACT)) == 0 && + !name.equals(ClassConstants.METHOD_NAME_INIT) && + ((programClass.u2accessFlags & ClassConstants.ACC_FINAL) != 0 || (!KeepMarker.isKept(programMethod) && (programClass.subClasses == null || !memberFinder.isOverriden(programClass, programMethod))))) { - programMethod.u2accessFlags |= ClassConstants.INTERNAL_ACC_FINAL; + programMethod.u2accessFlags |= ClassConstants.ACC_FINAL; // Visit the method, if required. if (extraMemberVisitor != null) diff --git a/src/proguard/optimize/peephole/MethodInliner.java b/src/proguard/optimize/peephole/MethodInliner.java index 947cd43..5068965 100644 --- a/src/proguard/optimize/peephole/MethodInliner.java +++ b/src/proguard/optimize/peephole/MethodInliner.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2014 Eric Lafortune (eric@graphics.cornell.edu) * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -30,7 +30,7 @@ import proguard.classfile.instruction.*; import proguard.classfile.instruction.visitor.InstructionVisitor; import proguard.classfile.util.*; import proguard.classfile.visitor.*; -import proguard.optimize.*; +import proguard.optimize.KeepMarker; import proguard.optimize.info.*; import java.util.*; @@ -155,6 +155,8 @@ implements AttributeVisitor, System.err.println(" Inlined method = ["+method.getName(clazz)+method.getDescriptor(clazz)+"]"); } System.err.println(" Exception = ["+ex.getClass().getName()+"] ("+ex.getMessage()+")"); + + ex.printStackTrace(); System.err.println("Not inlining this method"); if (DEBUG) @@ -185,7 +187,7 @@ implements AttributeVisitor, exceptionInfoAdder = new ExceptionInfoAdder(targetClass, codeAttributeComposer); estimatedResultingCodeLength = codeAttribute.u4codeLength; inliningMethods.clear(); - uninitializedObjectCount = method.getName(clazz).equals(ClassConstants.INTERNAL_METHOD_NAME_INIT) ? 1 : 0; + uninitializedObjectCount = method.getName(clazz).equals(ClassConstants.METHOD_NAME_INIT) ? 1 : 0; inlinedAny = false; codeAttributeComposer.reset(); stackSizeComputer.visitCodeAttribute(clazz, method, codeAttribute); @@ -252,7 +254,7 @@ implements AttributeVisitor, String descriptor = method.getDescriptor(clazz); boolean isStatic = - (method.getAccessFlags() & ClassConstants.INTERNAL_ACC_STATIC) != 0; + (method.getAccessFlags() & ClassConstants.ACC_STATIC) != 0; // Count the number of parameters, taking into account their categories. int parameterCount = ClassUtil.internalMethodParameterCount(descriptor); @@ -287,23 +289,23 @@ implements AttributeVisitor, byte opcode; switch (parameterType.charAt(0)) { - case ClassConstants.INTERNAL_TYPE_BOOLEAN: - case ClassConstants.INTERNAL_TYPE_BYTE: - case ClassConstants.INTERNAL_TYPE_CHAR: - case ClassConstants.INTERNAL_TYPE_SHORT: - case ClassConstants.INTERNAL_TYPE_INT: + case ClassConstants.TYPE_BOOLEAN: + case ClassConstants.TYPE_BYTE: + case ClassConstants.TYPE_CHAR: + case ClassConstants.TYPE_SHORT: + case ClassConstants.TYPE_INT: opcode = InstructionConstants.OP_ISTORE; break; - case ClassConstants.INTERNAL_TYPE_LONG: + case ClassConstants.TYPE_LONG: opcode = InstructionConstants.OP_LSTORE; break; - case ClassConstants.INTERNAL_TYPE_FLOAT: + case ClassConstants.TYPE_FLOAT: opcode = InstructionConstants.OP_FSTORE; break; - case ClassConstants.INTERNAL_TYPE_DOUBLE: + case ClassConstants.TYPE_DOUBLE: opcode = InstructionConstants.OP_DSTORE; break; @@ -471,12 +473,9 @@ implements AttributeVisitor, // Implementations for ConstantVisitor. - public void visitInterfaceMethodrefConstant(Clazz clazz, InterfaceMethodrefConstant interfaceMethodrefConstant) {} - - - public void visitMethodrefConstant(Clazz clazz, MethodrefConstant methodrefConstant) + public void visitAnyMethodrefConstant(Clazz clazz, RefConstant refConstant) { - methodrefConstant.referencedMemberAccept(this); + refConstant.referencedMemberAccept(this); } @@ -493,22 +492,23 @@ implements AttributeVisitor, !KeepMarker.isKept(programMethod) && // Only inline the method if it is private, static, or final. - (accessFlags & (ClassConstants.INTERNAL_ACC_PRIVATE | - ClassConstants.INTERNAL_ACC_STATIC | - ClassConstants.INTERNAL_ACC_FINAL)) != 0 && + // This currently precludes default interface methods, because + // they can't be final. + (accessFlags & (ClassConstants.ACC_PRIVATE | + ClassConstants.ACC_STATIC | + ClassConstants.ACC_FINAL)) != 0 && // Only inline the method if it is not synchronized, etc. - (accessFlags & (ClassConstants.INTERNAL_ACC_SYNCHRONIZED | - ClassConstants.INTERNAL_ACC_NATIVE | - ClassConstants.INTERNAL_ACC_INTERFACE | - ClassConstants.INTERNAL_ACC_ABSTRACT)) == 0 && + (accessFlags & (ClassConstants.ACC_SYNCHRONIZED | + ClassConstants.ACC_NATIVE | + ClassConstants.ACC_ABSTRACT)) == 0 && // Don't inline an <init> method, except in an <init> method in the // same class. -// (!programMethod.getName(programClass).equals(ClassConstants.INTERNAL_METHOD_NAME_INIT) || +// (!programMethod.getName(programClass).equals(ClassConstants.METHOD_NAME_INIT) || // (programClass.equals(targetClass) && -// targetMethod.getName(targetClass).equals(ClassConstants.INTERNAL_METHOD_NAME_INIT))) && - !programMethod.getName(programClass).equals(ClassConstants.INTERNAL_METHOD_NAME_INIT) && +// targetMethod.getName(targetClass).equals(ClassConstants.METHOD_NAME_INIT))) && + !programMethod.getName(programClass).equals(ClassConstants.METHOD_NAME_INIT) && // Don't inline a method into itself. (!programMethod.equals(targetMethod) || @@ -522,9 +522,10 @@ implements AttributeVisitor, // introducing incompatible constructs. targetClass.u4version >= programClass.u4version && - // Only inline the method if it doesn't invoke a super method, or if - // it is in the same class. - (!SuperInvocationMarker.invokesSuperMethods(programMethod) || + // Only inline the method if it doesn't invoke a super method or a + // dynamic method, or if it is in the same class. + (!SuperInvocationMarker.invokesSuperMethods(programMethod) && + !DynamicInvocationMarker.invokesDynamically(programMethod) || programClass.equals(targetClass)) && // Only inline the method if it doesn't branch backward while there @@ -554,9 +555,11 @@ implements AttributeVisitor, // Only inline the method if it comes from the a class with at most // a subset of the initialized superclasses. - (programClass.equals(targetClass) || + ((accessFlags & ClassConstants.ACC_STATIC) == 0 || + programClass.equals(targetClass) || initializedSuperClasses(targetClass).containsAll(initializedSuperClasses(programClass)))) - { boolean oldInlining = inlining; + { + boolean oldInlining = inlining; inlining = true; inliningMethods.push(programMethod); @@ -574,7 +577,7 @@ implements AttributeVisitor, inlining = oldInlining; inliningMethods.pop(); } - else if (programMethod.getName(programClass).equals(ClassConstants.INTERNAL_METHOD_NAME_INIT)) + else if (programMethod.getName(programClass).equals(ClassConstants.METHOD_NAME_INIT)) { uninitializedObjectCount--; } diff --git a/src/proguard/optimize/peephole/NopRemover.java b/src/proguard/optimize/peephole/NopRemover.java index 9396c40..b869045 100644 --- a/src/proguard/optimize/peephole/NopRemover.java +++ b/src/proguard/optimize/peephole/NopRemover.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2014 Eric Lafortune (eric@graphics.cornell.edu) * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/optimize/peephole/PeepholeOptimizer.java b/src/proguard/optimize/peephole/PeepholeOptimizer.java index 2a602ee..35c4426 100644 --- a/src/proguard/optimize/peephole/PeepholeOptimizer.java +++ b/src/proguard/optimize/peephole/PeepholeOptimizer.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2014 Eric Lafortune (eric@graphics.cornell.edu) * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/optimize/peephole/ReachableCodeMarker.java b/src/proguard/optimize/peephole/ReachableCodeMarker.java index b6fcf18..2b8fb10 100644 --- a/src/proguard/optimize/peephole/ReachableCodeMarker.java +++ b/src/proguard/optimize/peephole/ReachableCodeMarker.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2014 Eric Lafortune (eric@graphics.cornell.edu) * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/optimize/peephole/RetargetedInnerClassAttributeRemover.java b/src/proguard/optimize/peephole/RetargetedInnerClassAttributeRemover.java index a67c6ff..5d3ccd3 100644 --- a/src/proguard/optimize/peephole/RetargetedInnerClassAttributeRemover.java +++ b/src/proguard/optimize/peephole/RetargetedInnerClassAttributeRemover.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2014 Eric Lafortune (eric@graphics.cornell.edu) * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/optimize/peephole/TargetClassChanger.java b/src/proguard/optimize/peephole/TargetClassChanger.java index f997e03..a65cad5 100644 --- a/src/proguard/optimize/peephole/TargetClassChanger.java +++ b/src/proguard/optimize/peephole/TargetClassChanger.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2014 Eric Lafortune (eric@graphics.cornell.edu) * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -21,14 +21,14 @@ package proguard.optimize.peephole; import proguard.classfile.*; -import proguard.classfile.editor.*; import proguard.classfile.attribute.*; import proguard.classfile.attribute.annotation.*; import proguard.classfile.attribute.annotation.visitor.*; import proguard.classfile.attribute.visitor.*; import proguard.classfile.constant.*; import proguard.classfile.constant.visitor.ConstantVisitor; -import proguard.classfile.util.*; +import proguard.classfile.editor.*; +import proguard.classfile.util.SimplifiedVisitor; import proguard.classfile.visitor.*; /** @@ -58,6 +58,9 @@ implements ClassVisitor, public void visitProgramClass(ProgramClass programClass) { + // We're only making changes locally in the class. + // Not all other classes may have been retargeted yet. + // Change the references of the constant pool. programClass.constantPoolEntriesAccept(this); @@ -68,10 +71,41 @@ implements ClassVisitor, // Change the references of the attributes. programClass.attributesAccept(this); - // Is the class itself being retargeted? + // Remove duplicate interfaces and interface classes that have ended + // up pointing to the class itself. + boolean[] delete = null; + for (int index = 0; index < programClass.u2interfacesCount; index++) + { + Clazz interfaceClass = programClass.getInterface(index); + if (interfaceClass != null && + (programClass.equals(interfaceClass) || + containsInterfaceClass(programClass, + index, + interfaceClass))) + { + // Lazily create the array. + if (delete == null) + { + delete = new boolean[programClass.u2interfacesCount]; + } + + delete[index] = true; + } + } + + if (delete != null) + { + new InterfaceDeleter(delete).visitProgramClass(programClass); + } + + // Is the class being retargeted? Clazz targetClass = ClassMerger.getTargetClass(programClass); if (targetClass != null) { + // We're not changing anything special in the superclass and + // interface hierarchy of the retargeted class. The shrinking + // step will remove the class for us. + // Restore the class name. We have to add a new class entry // to avoid an existing entry with the same name being reused. The // names have to be fixed later, based on their referenced classes. @@ -80,29 +114,14 @@ implements ClassVisitor, programClass.getName(), programClass); - // This class will loose all its interfaces. - programClass.u2interfacesCount = 0; - - // This class will loose all its subclasses. + // This class will no longer have any subclasses, because their + // subclasses and interfaces will be retargeted. programClass.subClasses = null; } else { - // Remove interface classes that are pointing to this class. - int newInterfacesCount = 0; - for (int index = 0; index < programClass.u2interfacesCount; index++) - { - Clazz interfaceClass = programClass.getInterface(index); - if (!programClass.equals(interfaceClass)) - { - programClass.u2interfaces[newInterfacesCount++] = - programClass.u2interfaces[index]; - } - } - programClass.u2interfacesCount = newInterfacesCount; - - // Update the subclasses of the superclass and interfaces of the - // target class. + // This class has become the subclass of its possibly new + // superclass and of any new interfaces. ConstantVisitor subclassAdder = new ReferencedClassVisitor( new SubclassFilter(programClass, @@ -279,8 +298,7 @@ implements ClassVisitor, } - - // Implementations for LocalVariableInfoVisitor. + // Implementations for LocalVariableInfoVisitor. public void visitLocalVariableInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableInfo localVariableInfo) { @@ -289,6 +307,7 @@ implements ClassVisitor, updateReferencedClass(localVariableInfo.referencedClass); } + // Implementations for LocalVariableTypeInfoVisitor. public void visitLocalVariableTypeInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeInfo localVariableTypeInfo) @@ -297,6 +316,7 @@ implements ClassVisitor, updateReferencedClasses(localVariableTypeInfo.referencedClasses); } + // Implementations for AnnotationVisitor. public void visitAnnotation(Clazz clazz, Annotation annotation) @@ -379,7 +399,27 @@ implements ClassVisitor, // Small utility methods. - /** + /** + * Returns whether the given class contains the given interface + * class in its first given number of interfaces. + */ + private boolean containsInterfaceClass(Clazz clazz, + int interfaceCount, + Clazz interfaceClass) + { + for (int index = 0; index < interfaceCount; index++) + { + if (interfaceClass.equals(clazz.getInterface(index))) + { + return true; + } + } + + return false; + } + + + /** * Updates the retargeted classes in the given array of classes. */ private void updateReferencedClasses(Clazz[] referencedClasses) diff --git a/src/proguard/optimize/peephole/UnreachableCodeRemover.java b/src/proguard/optimize/peephole/UnreachableCodeRemover.java index 570b3ca..f4e3603 100644 --- a/src/proguard/optimize/peephole/UnreachableCodeRemover.java +++ b/src/proguard/optimize/peephole/UnreachableCodeRemover.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2014 Eric Lafortune (eric@graphics.cornell.edu) * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free diff --git a/src/proguard/optimize/peephole/UnreachableExceptionRemover.java b/src/proguard/optimize/peephole/UnreachableExceptionRemover.java index 8e77716..150c4c5 100644 --- a/src/proguard/optimize/peephole/UnreachableExceptionRemover.java +++ b/src/proguard/optimize/peephole/UnreachableExceptionRemover.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2014 Eric Lafortune (eric@graphics.cornell.edu) * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -41,9 +41,6 @@ implements AttributeVisitor, private final ExceptionInfoVisitor extraExceptionInfoVisitor; - private final ExceptionInstructionChecker exceptionInstructionChecker = new ExceptionInstructionChecker(); - - /** * Creates a new UnreachableExceptionRemover. */ @@ -123,11 +120,7 @@ implements AttributeVisitor, Instruction instruction = InstructionFactory.create(code, offset); // Check if it may be throwing exceptions. - if (exceptionInstructionChecker.mayThrowExceptions(clazz, - method, - codeAttribute, - offset, - instruction)) + if (instruction.mayThrowExceptions()) { return true; } diff --git a/src/proguard/optimize/peephole/VariableShrinker.java b/src/proguard/optimize/peephole/VariableShrinker.java index 6c05944..6387f1e 100644 --- a/src/proguard/optimize/peephole/VariableShrinker.java +++ b/src/proguard/optimize/peephole/VariableShrinker.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2014 Eric Lafortune (eric@graphics.cornell.edu) * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -78,7 +78,7 @@ implements AttributeVisitor public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) { - if ((method.getAccessFlags() & ClassConstants.INTERNAL_ACC_ABSTRACT) == 0) + if ((method.getAccessFlags() & ClassConstants.ACC_ABSTRACT) == 0) { // Compute the parameter size. int parameterSize = diff --git a/src/proguard/optimize/peephole/VerticalClassMerger.java b/src/proguard/optimize/peephole/VerticalClassMerger.java index 825de94..560e989 100644 --- a/src/proguard/optimize/peephole/VerticalClassMerger.java +++ b/src/proguard/optimize/peephole/VerticalClassMerger.java @@ -2,7 +2,7 @@ * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * - * Copyright (c) 2002-2013 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2014 Eric Lafortune (eric@graphics.cornell.edu) * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free @@ -21,7 +21,6 @@ package proguard.optimize.peephole; import proguard.classfile.ProgramClass; -import proguard.classfile.constant.visitor.ConstantVisitor; import proguard.classfile.util.SimplifiedVisitor; import proguard.classfile.visitor.ClassVisitor; @@ -80,6 +79,7 @@ implements ClassVisitor public void visitProgramClass(ProgramClass programClass) { + // Try inlining all immediate subclasses into this class. programClass.subclassesAccept(new ClassMerger(programClass, allowAccessModification, mergeInterfacesAggressively, |