diff options
author | Brint E. Kriebel <bekit@cyngn.com> | 2014-09-29 17:19:43 -0700 |
---|---|---|
committer | Brint E. Kriebel <bekit@cyngn.com> | 2014-09-29 17:19:43 -0700 |
commit | b64a23b7cc2761f03879ceb6b030ae80171f11e2 (patch) | |
tree | 86ba49fd1b7275a7e293c3c3bc44e6317bebf67f /src/proguard/optimize | |
parent | fe926bab25e3af81265d4e0bbb79bcdb41b10635 (diff) | |
parent | 9961286c06c25cd03464d3e2b00bd9b9dedf96ba (diff) | |
download | android_external_proguard-stable/cm-11.0-XNG3C.tar.gz android_external_proguard-stable/cm-11.0-XNG3C.tar.bz2 android_external_proguard-stable/cm-11.0-XNG3C.zip |
Merge remote-tracking branch 'aosp/l-preview' into cm-11.0cm-11.0-XNPH05Q-tomato-9828f8e9cccm-11.0-XNPH05Q-bacon-5229c4ef56stable/cm-11.0-XNG3Cstable/cm-11.0-XNG2Sstable/cm-11.0-XNF9Xstable/cm-11.0-XNF8Ystable/cm-11.0shipping/cm-11.0cm-11.0
Diffstat (limited to 'src/proguard/optimize')
78 files changed, 4698 insertions, 1304 deletions
diff --git a/src/proguard/optimize/BootstrapMethodArgumentShrinker.java b/src/proguard/optimize/BootstrapMethodArgumentShrinker.java new file mode 100644 index 0000000..26f1349 --- /dev/null +++ b/src/proguard/optimize/BootstrapMethodArgumentShrinker.java @@ -0,0 +1,103 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 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; + +import proguard.classfile.*; +import proguard.classfile.attribute.*; +import proguard.classfile.attribute.annotation.*; +import proguard.classfile.attribute.visitor.*; +import proguard.classfile.constant.*; +import proguard.classfile.constant.visitor.ConstantVisitor; +import proguard.classfile.editor.ConstantPoolEditor; +import proguard.classfile.util.*; +import proguard.classfile.visitor.MemberVisitor; +import proguard.optimize.info.*; +import proguard.optimize.peephole.VariableShrinker; + +/** + * This BootstrapMethodInfoVisitor removes unused constant arguments from + * bootstrap method entries that it visits. + * + * @see ParameterUsageMarker + * @see VariableUsageMarker + * @see VariableShrinker + * @author Eric Lafortune + */ +public class BootstrapMethodArgumentShrinker +extends SimplifiedVisitor +implements BootstrapMethodInfoVisitor, + ConstantVisitor, + MemberVisitor +{ + private long usedParameters; + + + // Implementations for BootstrapMethodInfoVisitor. + + public void visitBootstrapMethodInfo(Clazz clazz, BootstrapMethodInfo bootstrapMethodInfo) + { + // Check which method parameters are used. + usedParameters = -1L; + clazz.constantPoolEntryAccept(bootstrapMethodInfo.u2methodHandleIndex, this); + + // Remove the unused arguments. + int methodArgumentCount = bootstrapMethodInfo.u2methodArgumentCount; + int[] methodArguments = bootstrapMethodInfo.u2methodArguments; + + int newArgumentIndex = 0; + + for (int argumentIndex = 0; argumentIndex < methodArgumentCount; argumentIndex++) + { + if (argumentIndex >= 64 || + (usedParameters & (1L << argumentIndex)) != 0L) + { + methodArguments[newArgumentIndex++] = methodArguments[argumentIndex]; + } + } + + // Update the number of arguments. + bootstrapMethodInfo.u2methodArgumentCount = newArgumentIndex; + } + + + // Implementations for ConstantVisitor. + + public void visitMethodHandleConstant(Clazz clazz, MethodHandleConstant methodHandleConstant) + { + // Check the referenced bootstrap method. + clazz.constantPoolEntryAccept(methodHandleConstant.u2referenceIndex, this); + } + + + public void visitAnyRefConstant(Clazz clazz, RefConstant refConstant) + { + // Check the referenced class member itself. + refConstant.referencedMemberAccept(this); + } + + + // Implementations for MemberVisitor. + + 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 67a79ab..668d43d 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-2009 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2013 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 @@ -54,6 +54,12 @@ implements AttributeVisitor } + public void visitBootstrapMethodsAttribute(Clazz clazz, BootstrapMethodsAttribute bootstrapMethodsAttribute) + { + attributeVisitor.visitBootstrapMethodsAttribute(clazz, bootstrapMethodsAttribute); + } + + public void visitSourceFileAttribute(Clazz clazz, SourceFileAttribute sourceFileAttribute) { attributeVisitor.visitSourceFileAttribute(clazz, sourceFileAttribute); diff --git a/src/proguard/optimize/ConstantMemberFilter.java b/src/proguard/optimize/ConstantMemberFilter.java index 56437c3..1f30a30 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-2009 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2013 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 24a7040..1500fd0 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-2009 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2013 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/DuplicateInitializerFixer.java b/src/proguard/optimize/DuplicateInitializerFixer.java index 746d182..95bc2f1 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-2009 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2013 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,7 +86,7 @@ implements MemberVisitor, if (!programMethod.equals(similarMethod)) { // Should this initializer be preserved? - if (!KeepMarker.isKept(programMethod)) + if (KeepMarker.isKept(programMethod)) { // Fix the other initializer. programMethod = (ProgramMethod)similarMethod; @@ -95,12 +95,23 @@ implements MemberVisitor, int index = descriptor.indexOf(ClassConstants.INTERNAL_METHOD_ARGUMENTS_CLOSE); // Try to find a new, unique descriptor. - for (int typeIndex = 0; typeIndex < TYPES.length; typeIndex++) + int typeCounter = 0; + while (true) { - String newDescriptor = - descriptor.substring(0, index) + - TYPES[typeIndex] + - descriptor.substring(index); + // Construct the new descriptor by inserting a new type + // as an additional last argument. + StringBuffer newDescriptorBuffer = + new StringBuffer(descriptor.substring(0, index)); + + for (int arrayDimension = 0; arrayDimension < typeCounter / TYPES.length; arrayDimension++) + { + newDescriptorBuffer.append(ClassConstants.INTERNAL_TYPE_ARRAY); + } + + newDescriptorBuffer.append(TYPES[typeCounter % TYPES.length]); + newDescriptorBuffer.append(descriptor.substring(index)); + + String newDescriptor = newDescriptorBuffer.toString(); // Is the new initializer descriptor unique? if (programClass.findMethod(name, newDescriptor) == null) @@ -108,7 +119,7 @@ implements MemberVisitor, if (DEBUG) { System.out.println("DuplicateInitializerFixer:"); - System.out.println(" ["+programClass.getName()+"]: "+name+descriptor+" -> "+newDescriptor); + System.out.println(" ["+programClass.getName()+"."+name+descriptor+"] ("+ClassUtil.externalClassAccessFlags(programMethod.getAccessFlags())+") -> ["+newDescriptor+"]"); } // Update the descriptor. @@ -130,12 +141,9 @@ implements MemberVisitor, // We're done with this constructor. return; } - } - throw new IllegalStateException("Can't find unique constructor descriptor for ["+ - programClass.getName()+"."+ - programMethod.getName(programClass)+ - programMethod.getDescriptor(programClass)+"]"); + typeCounter++; + } } } } diff --git a/src/proguard/optimize/DuplicateInitializerInvocationFixer.java b/src/proguard/optimize/DuplicateInitializerInvocationFixer.java index ca24481..5edaba0 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-2009 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2013 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 @@ -28,7 +28,7 @@ import proguard.classfile.constant.visitor.ConstantVisitor; import proguard.classfile.editor.CodeAttributeEditor; import proguard.classfile.instruction.*; import proguard.classfile.instruction.visitor.InstructionVisitor; -import proguard.classfile.util.SimplifiedVisitor; +import proguard.classfile.util.*; import proguard.classfile.visitor.MemberVisitor; /** @@ -48,12 +48,12 @@ implements AttributeVisitor, private final CodeAttributeEditor codeAttributeEditor = new CodeAttributeEditor(); - private String descriptor; - private boolean hasBeenFixed; + private String descriptor; + private int descriptorLengthDelta; /** - * Creates a new EvaluationSimplifier. + * Creates a new DuplicateInitializerInvocationFixer. */ public DuplicateInitializerInvocationFixer() { @@ -62,7 +62,7 @@ implements AttributeVisitor, /** - * Creates a new EvaluationSimplifier. + * Creates a new DuplicateInitializerInvocationFixer. * @param extraAddedInstructionVisitor an optional extra visitor for all * added instructions. */ @@ -102,21 +102,22 @@ implements AttributeVisitor, { if (constantInstruction.opcode == InstructionConstants.OP_INVOKESPECIAL) { - hasBeenFixed = false; + descriptorLengthDelta = 0; clazz.constantPoolEntryAccept(constantInstruction.constantIndex, this); - if (hasBeenFixed) + if (descriptorLengthDelta > 0) { Instruction extraInstruction = - new SimpleInstruction(InstructionConstants.OP_ICONST_0); + new SimpleInstruction(descriptorLengthDelta == 1 ? + InstructionConstants.OP_ICONST_0 : + InstructionConstants.OP_ACONST_NULL); codeAttributeEditor.insertBeforeInstruction(offset, extraInstruction); if (DEBUG) { - System.out.println("DuplicateInitializerInvocationFixer:"); - System.out.println(" Inserting "+extraInstruction.toString()+" before "+constantInstruction.toString(offset)); + System.out.println(" ["+clazz.getName()+"."+method.getName(clazz)+method.getDescriptor(clazz)+"] Inserting "+extraInstruction.toString()+" before "+constantInstruction.toString(offset)); } if (extraAddedInstructionVisitor != null) @@ -145,6 +146,16 @@ implements AttributeVisitor, public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) { - hasBeenFixed = !descriptor.equals(programMethod.getDescriptor(programClass)); + descriptorLengthDelta = + programMethod.getDescriptor(programClass).length() - descriptor.length(); + + if (DEBUG) + { + if (descriptorLengthDelta > 0) + { + System.out.println("DuplicateInitializerInvocationFixer:"); + System.out.println(" ["+programClass.getName()+"."+programMethod.getName(programClass)+programMethod.getDescriptor(programClass)+"] ("+ClassUtil.externalClassAccessFlags(programMethod.getAccessFlags())+") referenced by:"); + } + } } -}
\ No newline at end of file +} diff --git a/src/proguard/optimize/KeepMarker.java b/src/proguard/optimize/KeepMarker.java index 4297996..b0eab7b 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-2009 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2013 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,6 +23,7 @@ package proguard.optimize; import proguard.classfile.*; import proguard.classfile.util.MethodLinker; import proguard.classfile.visitor.*; +import proguard.optimize.info.NoSideEffectMethodMarker; /** @@ -30,6 +31,7 @@ import proguard.classfile.visitor.*; * marks classes and class members it visits. The marked elements * will remain unchanged as necessary in the optimization step. * + * @see NoSideEffectMethodMarker * @author Eric Lafortune */ public class KeepMarker @@ -90,6 +92,12 @@ implements ClassVisitor, public static boolean isKept(VisitorAccepter visitorAccepter) { - return MethodLinker.lastVisitorAccepter(visitorAccepter).getVisitorInfo() == KEPT; + // We're also checking for the constant in NoSideEffectMethodMarker, + // to keep things simple. + Object visitorInfo = + MethodLinker.lastVisitorAccepter(visitorAccepter).getVisitorInfo(); + + return visitorInfo == KEPT || + visitorInfo == NoSideEffectMethodMarker.KEPT_BUT_NO_SIDE_EFFECTS; } } diff --git a/src/proguard/optimize/KeptClassFilter.java b/src/proguard/optimize/KeptClassFilter.java new file mode 100644 index 0000000..60a9d3e --- /dev/null +++ b/src/proguard/optimize/KeptClassFilter.java @@ -0,0 +1,69 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 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; + +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 marked as kept. + * + * @see KeepMarker + * + * @author Eric Lafortune + */ +public class KeptClassFilter +implements ClassVisitor +{ + private final ClassVisitor classVisitor; + + + /** + * Creates a new KeptClassFilter. + * @param classVisitor the class visitor to which the visiting will be + * delegated. + */ + public KeptClassFilter(ClassVisitor classVisitor) + { + this.classVisitor = classVisitor; + } + + + // Implementations for ClassVisitor. + + public void visitProgramClass(ProgramClass programClass) + { + if (KeepMarker.isKept(programClass)) + { + classVisitor.visitProgramClass(programClass); + } + } + + + public void visitLibraryClass(LibraryClass libraryClass) + { + if (KeepMarker.isKept(libraryClass)) + { + classVisitor.visitLibraryClass(libraryClass); + } + } +}
\ No newline at end of file diff --git a/src/proguard/optimize/KeptMemberFilter.java b/src/proguard/optimize/KeptMemberFilter.java new file mode 100644 index 0000000..1bdadb4 --- /dev/null +++ b/src/proguard/optimize/KeptMemberFilter.java @@ -0,0 +1,87 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 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; + +import proguard.classfile.*; +import proguard.classfile.visitor.MemberVisitor; + +/** + * This MemberVisitor delegates all its method calls to another MemberVisitor, + * but only for Member objects that are marked as kept. + * + * @see KeepMarker + * + * @author Eric Lafortune + */ +public class KeptMemberFilter +implements MemberVisitor +{ + private final MemberVisitor memberVisitor; + + + /** + * Creates a new KeptMemberFilter. + * @param memberVisitor the member visitor to which the visiting will be + * delegated. + */ + public KeptMemberFilter(MemberVisitor memberVisitor) + { + this.memberVisitor = memberVisitor; + } + + + // Implementations for MemberVisitor. + + public void visitProgramField(ProgramClass programClass, ProgramField programField) + { + if (KeepMarker.isKept(programField)) + { + memberVisitor.visitProgramField(programClass, programField); + } + } + + + public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) + { + if (KeepMarker.isKept(programMethod)) + { + memberVisitor.visitProgramMethod(programClass, programMethod); + } + } + + + public void visitLibraryField(LibraryClass libraryClass, LibraryField libraryField) + { + if (KeepMarker.isKept(libraryField)) + { + memberVisitor.visitLibraryField(libraryClass, libraryField); + } + } + + + public void visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryMethod) + { + if (KeepMarker.isKept(libraryMethod)) + { + memberVisitor.visitLibraryMethod(libraryClass, libraryMethod); + } + } +}
\ No newline at end of file diff --git a/src/proguard/optimize/MemberDescriptorSpecializer.java b/src/proguard/optimize/MemberDescriptorSpecializer.java index 0d0b841..4dce62e 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-2009 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2013 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 @@ public class MemberDescriptorSpecializer extends SimplifiedVisitor implements MemberVisitor { - private static final boolean DEBUG = true; + private static final boolean DEBUG = false; private final MemberVisitor extraParameterMemberVisitor; diff --git a/src/proguard/optimize/MethodDescriptorShrinker.java b/src/proguard/optimize/MethodDescriptorShrinker.java index 48374e7..d8d4425 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-2009 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2013 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.annotation.*; -import proguard.classfile.attribute.visitor.AttributeVisitor; +import proguard.classfile.attribute.visitor.*; import proguard.classfile.editor.ConstantPoolEditor; import proguard.classfile.util.*; import proguard.classfile.visitor.MemberVisitor; @@ -95,11 +95,9 @@ implements MemberVisitor, if (DEBUG) { System.out.println("MethodDescriptorShrinker:"); - System.out.println(" Class file = "+programClass.getName()); - System.out.println(" Method name = "+name); - System.out.println(" -> "+newName); - System.out.println(" Method descriptor = "+descriptor); - System.out.println(" -> "+newDescriptor); + System.out.println(" ["+programClass.getName()+"."+ + name+descriptor+"] -> ["+ + newName+newDescriptor+"]"); } ConstantPoolEditor constantPoolEditor = diff --git a/src/proguard/optimize/MethodStaticizer.java b/src/proguard/optimize/MethodStaticizer.java index 8dd11e1..c8bdd11 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-2009 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2013 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/OptimizationInfoMemberFilter.java b/src/proguard/optimize/OptimizationInfoMemberFilter.java index 8760aee..2c5454c 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-2009 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2013 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 a3e8a6e..8042825 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-2009 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2013 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,8 @@ package proguard.optimize; import proguard.*; import proguard.classfile.*; import proguard.classfile.attribute.visitor.*; -import proguard.classfile.constant.visitor.AllConstantVisitor; +import proguard.classfile.constant.Constant; +import proguard.classfile.constant.visitor.*; import proguard.classfile.editor.*; import proguard.classfile.instruction.visitor.*; import proguard.classfile.util.MethodLinker; @@ -66,6 +67,7 @@ public class Optimizer private static final String CODE_SIMPLIFICATION_CAST = "code/simplification/cast"; private static final String CODE_SIMPLIFICATION_FIELD = "code/simplification/field"; private static final String CODE_SIMPLIFICATION_BRANCH = "code/simplification/branch"; + private static final String CODE_SIMPLIFICATION_STRING = "code/simplification/string"; private static final String CODE_SIMPLIFICATION_ADVANCED = "code/simplification/advanced"; private static final String CODE_REMOVAL_ADVANCED = "code/removal/advanced"; private static final String CODE_REMOVAL_SIMPLE = "code/removal/simple"; @@ -80,6 +82,7 @@ public class Optimizer CLASS_MERGING_VERTICAL, CLASS_MERGING_HORIZONTAL, FIELD_REMOVAL_WRITEONLY, + FIELD_MARKING_PRIVATE, FIELD_PROPAGATION_VALUE, METHOD_MARKING_PRIVATE, METHOD_MARKING_STATIC, @@ -96,6 +99,7 @@ public class Optimizer CODE_SIMPLIFICATION_CAST, CODE_SIMPLIFICATION_FIELD, CODE_SIMPLIFICATION_BRANCH, + CODE_SIMPLIFICATION_STRING, CODE_SIMPLIFICATION_ADVANCED, CODE_REMOVAL_ADVANCED, CODE_REMOVAL_SIMPLE, @@ -157,6 +161,7 @@ public class Optimizer boolean codeSimplificationCast = filter.matches(CODE_SIMPLIFICATION_CAST); boolean codeSimplificationField = filter.matches(CODE_SIMPLIFICATION_FIELD); boolean codeSimplificationBranch = filter.matches(CODE_SIMPLIFICATION_BRANCH); + boolean codeSimplificationString = filter.matches(CODE_SIMPLIFICATION_STRING); boolean codeSimplificationAdvanced = filter.matches(CODE_SIMPLIFICATION_ADVANCED); boolean codeRemovalAdvanced = filter.matches(CODE_REMOVAL_ADVANCED); boolean codeRemovalSimple = filter.matches(CODE_REMOVAL_SIMPLE); @@ -186,13 +191,15 @@ public class Optimizer InstructionCounter codeSimplificationCastCounter = new InstructionCounter(); InstructionCounter codeSimplificationFieldCounter = new InstructionCounter(); InstructionCounter codeSimplificationBranchCounter = new InstructionCounter(); + InstructionCounter codeSimplificationStringCounter = new InstructionCounter(); InstructionCounter codeSimplificationAdvancedCounter = new InstructionCounter(); InstructionCounter deletedCounter = new InstructionCounter(); InstructionCounter addedCounter = new InstructionCounter(); MemberCounter codeRemovalVariableCounter = new MemberCounter(); ExceptionCounter codeRemovalExceptionCounter = new ExceptionCounter(); MemberCounter codeAllocationVariableCounter = new MemberCounter(); - MemberCounter initializerFixCounter = new MemberCounter(); + MemberCounter initializerFixCounter1 = new MemberCounter(); + MemberCounter initializerFixCounter2 = new MemberCounter(); // Some optimizations are required by other optimizations. codeSimplificationAdvanced = @@ -250,10 +257,27 @@ public class Optimizer new AllInstructionVisitor( new DotClassClassVisitor(keepMarker))))); - // We also keep all classes that are involved in Class.forName constructs. + // We also keep all classes that are accessed dynamically. programClassPool.classesAccept( new AllConstantVisitor( - new ClassForNameClassVisitor(keepMarker))); + new ConstantTagFilter(ClassConstants.CONSTANT_String, + new ReferencedClassVisitor(keepMarker)))); + + // We also keep all class members that are accessed dynamically. + programClassPool.classesAccept( + new AllConstantVisitor( + new ConstantTagFilter(ClassConstants.CONSTANT_String, + new ReferencedMemberVisitor(keepMarker)))); + + // We also keep all bootstrap method signatures. + programClassPool.classesAccept( + new ClassVersionFilter(ClassConstants.INTERNAL_CLASS_VERSION_1_7, + new AllAttributeVisitor( + new AttributeNameFilter(ClassConstants.ATTR_BootstrapMethods, + new AllBootstrapMethodInfoVisitor( + new BootstrapMethodHandleTraveler( + new MethodrefTraveler( + new ReferencedMemberVisitor(keepMarker)))))))); // Attach some optimization info to all classes and class members, so // it can be filled out later. @@ -320,6 +344,9 @@ public class Optimizer new ParameterUsageMarker(!methodMarkingStatic, !methodRemovalParameter)))); + // Mark all classes that have static initializers. + programClassPool.classesAccept(new StaticInitializerContainingClassMarker()); + // Mark all methods that have side effects. programClassPool.accept(new SideEffectMethodMarker()); @@ -347,18 +374,29 @@ public class Optimizer new AllAttributeVisitor( new PartialEvaluator(valueFactory, storingInvocationUnit, false)))); - // Count the constant fields and methods. - programClassPool.classesAccept( - new MultiClassVisitor( - new ClassVisitor[] - { + if (fieldPropagationValue) + { + // Count the constant fields. + programClassPool.classesAccept( new AllFieldVisitor( - new ConstantMemberFilter(fieldPropagationValueCounter)), + new ConstantMemberFilter(fieldPropagationValueCounter))); + } + + if (methodPropagationParameter) + { + // Count the constant method parameters. + programClassPool.classesAccept( new AllMethodVisitor( - new ConstantParameterFilter(methodPropagationParameterCounter)), + new ConstantParameterFilter(methodPropagationParameterCounter))); + } + + if (methodPropagationReturnvalue) + { + // Count the constant method return values. + programClassPool.classesAccept( new AllMethodVisitor( - new ConstantMemberFilter(methodPropagationReturnvalueCounter)), - })); + new ConstantMemberFilter(methodPropagationReturnvalueCounter))); + } } InvocationUnit loadingInvocationUnit = @@ -418,6 +456,12 @@ public class Optimizer // This operation also updates the stack sizes. programClassPool.classesAccept( new MemberReferenceFixer()); + + // Remove unused bootstrap method arguments. + programClassPool.classesAccept( + new AllAttributeVisitor( + new AllBootstrapMethodInfoVisitor( + new BootstrapMethodArgumentShrinker()))); } if (methodRemovalParameter || @@ -441,17 +485,39 @@ public class Optimizer new StackSizeUpdater()))); } -// // Specializing the class member descriptors seems to increase the -// // class file size, on average. -// // Specialize all class member descriptors. -// programClassPool.classesAccept(new AllMemberVisitor( -// new OptimizationInfoMemberFilter( -// new MemberDescriptorSpecializer()))); -// -// // Fix all references to classes, for MemberDescriptorSpecializer. -// programClassPool.classesAccept(new AllMemberVisitor( -// new OptimizationInfoMemberFilter( -// new ClassReferenceFixer(true)))); + if (methodRemovalParameter && + methodRemovalParameterCounter.getCount() > 0) + { + // Tweak the descriptors of duplicate initializers, due to removed + // method parameters. + programClassPool.classesAccept( + new AllMethodVisitor( + new DuplicateInitializerFixer(initializerFixCounter1))); + + if (initializerFixCounter1.getCount() > 0) + { + // Fix all invocations of tweaked initializers. + programClassPool.classesAccept( + new AllMethodVisitor( + new AllAttributeVisitor( + new DuplicateInitializerInvocationFixer(addedCounter)))); + + // Fix all references to tweaked initializers. + programClassPool.classesAccept(new MemberReferenceFixer()); + } + } + + //// Specializing the class member descriptors seems to increase the + //// class file size, on average. + //// Specialize all class member descriptors. + //programClassPool.classesAccept(new AllMemberVisitor( + // new OptimizationInfoMemberFilter( + // new MemberDescriptorSpecializer()))); + // + //// Fix all references to classes, for MemberDescriptorSpecializer. + //programClassPool.classesAccept(new AllMemberVisitor( + // new OptimizationInfoMemberFilter( + // new ClassReferenceFixer(true)))); // Mark all classes with package visible members. // Mark all exception catches of methods. @@ -461,13 +527,13 @@ public class Optimizer new MultiClassVisitor( new ClassVisitor[] { + new PackageVisibleMemberContainingClassMarker(), new AllConstantVisitor( new PackageVisibleMemberInvokingClassMarker()), new AllMethodVisitor( new MultiMemberVisitor( new MemberVisitor[] { - new PackageVisibleMemberContainingClassMarker(), new AllAttributeVisitor( new MultiAttributeVisitor( new AttributeVisitor[] @@ -511,8 +577,8 @@ public class Optimizer classMergingHorizontalCounter)); } - if (classMergingVertical || - classMergingHorizontal) + if (classMergingVerticalCounter .getCount() > 0 || + classMergingHorizontalCounter.getCount() > 0) { // Clean up inner class attributes to avoid loops. programClassPool.classesAccept(new RetargetedInnerClassAttributeRemover()); @@ -530,18 +596,20 @@ public class Optimizer new AllConstantVisitor( new AccessFixer())); } - } - if (methodRemovalParameter || - classMergingVertical || - classMergingHorizontal) - { - // Tweak the descriptors of duplicate initializers. + // Fix the access flags of the inner classes information. + programClassPool.classesAccept( + new AllAttributeVisitor( + new AllInnerClassesInfoVisitor( + new InnerClassesAccessFixer()))); + + // Tweak the descriptors of duplicate initializers, due to merged + // parameter classes. programClassPool.classesAccept( new AllMethodVisitor( - new DuplicateInitializerFixer(initializerFixCounter))); + new DuplicateInitializerFixer(initializerFixCounter2))); - if (initializerFixCounter.getCount() > 0) + if (initializerFixCounter2.getCount() > 0) { // Fix all invocations of tweaked initializers. programClassPool.classesAccept( @@ -595,14 +663,14 @@ public class Optimizer new NonPrivateMemberMarker()); } - if (fieldMarkingPrivate || - methodMarkingPrivate) + if (fieldMarkingPrivate) { // Make all non-private fields private, whereever possible. programClassPool.classesAccept( + new ClassAccessFilter(0, ClassConstants.INTERNAL_ACC_INTERFACE, new AllFieldVisitor( new MemberAccessFilter(0, ClassConstants.INTERNAL_ACC_PRIVATE, - new MemberPrivatizer(fieldMarkingPrivateCounter)))); + new MemberPrivatizer(fieldMarkingPrivateCounter))))); } if (methodMarkingPrivate) @@ -615,9 +683,9 @@ public class Optimizer new MemberPrivatizer(methodMarkingPrivateCounter))))); } - if ((methodInliningUnique || - methodInliningShort || - methodInliningTailrecursion) && + if ((methodInliningUniqueCounter .getCount() > 0 || + methodInliningShortCounter .getCount() > 0 || + methodInliningTailrecursionCounter.getCount() > 0) && configuration.allowAccessModification) { // Fix the access flags of referenced classes and class members, @@ -627,10 +695,10 @@ public class Optimizer new AccessFixer())); } - if (methodRemovalParameter || - classMergingVertical || - classMergingHorizontal || - methodMarkingPrivate) + if (methodRemovalParameterCounter .getCount() > 0 || + classMergingVerticalCounter .getCount() > 0 || + classMergingHorizontalCounter .getCount() > 0 || + methodMarkingPrivateCounter .getCount() > 0 ) { // Fix invocations of interface methods, of methods that have become // non-abstract or private, and of methods that have moved to a @@ -707,6 +775,15 @@ public class Optimizer new GotoReturnReplacer(codeAttributeEditor, codeSimplificationBranchCounter)); } + if (codeSimplificationString) + { + // Peephole optimizations involving branches. + peepholeOptimizations.add( + new InstructionSequencesReplacer(InstructionSequenceConstants.CONSTANTS, + InstructionSequenceConstants.STRING, + branchTargetFinder, codeAttributeEditor, codeSimplificationStringCounter)); + } + if (!peepholeOptimizations.isEmpty()) { // Convert the list into an array. @@ -749,14 +826,6 @@ public class Optimizer new AllAttributeVisitor( new VariableShrinker(codeRemovalVariableCounter)))); } - else - { - // Clean up all unused local variables. - programClassPool.classesAccept( - new AllMethodVisitor( - new AllAttributeVisitor( - new VariableCleaner()))); - } if (codeAllocationVariable) { @@ -767,6 +836,11 @@ public class Optimizer new VariableOptimizer(false, codeAllocationVariableCounter)))); } + + // Remove unused constants. + programClassPool.classesAccept( + new ConstantPoolShrinker()); + int classMarkingFinalCount = classMarkingFinalCounter .getCount(); int classMergingVerticalCount = classMergingVerticalCounter .getCount(); int classMergingHorizontalCount = classMergingHorizontalCounter .getCount(); @@ -776,7 +850,7 @@ public class Optimizer int methodMarkingPrivateCount = methodMarkingPrivateCounter .getCount(); int methodMarkingStaticCount = methodMarkingStaticCounter .getCount(); int methodMarkingFinalCount = methodMarkingFinalCounter .getCount(); - int methodRemovalParameterCount = methodRemovalParameterCounter .getCount() - methodMarkingStaticCounter.getCount() - initializerFixCounter.getCount(); + int methodRemovalParameterCount = methodRemovalParameterCounter .getCount() - methodMarkingStaticCounter.getCount() - initializerFixCounter1.getCount() - initializerFixCounter2.getCount(); int methodPropagationParameterCount = methodPropagationParameterCounter .getCount(); int methodPropagationReturnvalueCount = methodPropagationReturnvalueCounter.getCount(); int methodInliningShortCount = methodInliningShortCounter .getCount(); @@ -788,12 +862,23 @@ public class Optimizer int codeSimplificationCastCount = codeSimplificationCastCounter .getCount(); int codeSimplificationFieldCount = codeSimplificationFieldCounter .getCount(); int codeSimplificationBranchCount = codeSimplificationBranchCounter .getCount(); + int codeSimplificationStringCount = codeSimplificationStringCounter .getCount(); int codeSimplificationAdvancedCount = codeSimplificationAdvancedCounter .getCount(); int codeRemovalCount = deletedCounter .getCount() - addedCounter.getCount(); int codeRemovalVariableCount = codeRemovalVariableCounter .getCount(); int codeRemovalExceptionCount = codeRemovalExceptionCounter .getCount(); int codeAllocationVariableCount = codeAllocationVariableCounter .getCount(); + // Forget about constant fields, parameters, and return values, if they + // didn't lead to any useful optimizations. We want to avoid fruitless + // additional optimization passes. + if (codeSimplificationAdvancedCount == 0) + { + fieldPropagationValueCount = 0; + methodPropagationParameterCount = 0; + methodPropagationReturnvalueCount = 0; + } + if (configuration.verbose) { System.out.println(" Number of finalized classes: " + classMarkingFinalCount + disabled(classMarkingFinal)); @@ -817,6 +902,7 @@ public class Optimizer System.out.println(" Number of cast peephole optimizations: " + codeSimplificationCastCount + disabled(codeSimplificationCast)); System.out.println(" Number of field peephole optimizations: " + codeSimplificationFieldCount + disabled(codeSimplificationField)); System.out.println(" Number of branch peephole optimizations: " + codeSimplificationBranchCount + disabled(codeSimplificationBranch)); + System.out.println(" Number of string peephole optimizations: " + codeSimplificationStringCount + disabled(codeSimplificationString)); System.out.println(" Number of simplified instructions: " + codeSimplificationAdvancedCount + disabled(codeSimplificationAdvanced)); System.out.println(" Number of removed instructions: " + codeRemovalCount + disabled(codeRemovalAdvanced)); System.out.println(" Number of removed local variables: " + codeRemovalVariableCount + disabled(codeRemovalVariable)); @@ -845,6 +931,7 @@ public class Optimizer codeSimplificationCastCount > 0 || codeSimplificationFieldCount > 0 || codeSimplificationBranchCount > 0 || + codeSimplificationStringCount > 0 || codeSimplificationAdvancedCount > 0 || codeRemovalCount > 0 || codeRemovalVariableCount > 0 || diff --git a/src/proguard/optimize/ParameterShrinker.java b/src/proguard/optimize/ParameterShrinker.java index a2bc6d3..33d37d1 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-2009 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2013 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.AttributeVisitor; -import proguard.classfile.editor.*; +import proguard.classfile.editor.VariableRemapper; import proguard.classfile.util.*; import proguard.classfile.visitor.MemberVisitor; import proguard.optimize.info.ParameterUsageMarker; diff --git a/src/proguard/optimize/TailRecursionSimplifier.java b/src/proguard/optimize/TailRecursionSimplifier.java index 0946b6a..f820566 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-2009 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2013 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 @@ -54,9 +54,9 @@ implements AttributeVisitor, private final CodeAttributeComposer codeAttributeComposer = new CodeAttributeComposer(); + private final MyRecursionChecker recursionChecker = new MyRecursionChecker(); private Method targetMethod; - private boolean recursive; private boolean inlinedAny; @@ -105,38 +105,31 @@ implements AttributeVisitor, // clazz.getName().equals("abc/Def") && // method.getName(clazz).equals("abc"); - targetMethod = method; - inlinedAny = false; + targetMethod = method; + inlinedAny = false; codeAttributeComposer.reset(); - // Append the body of the code. - copyCode(clazz, method, codeAttribute); + // The code may expand, due to expanding constant and variable + // instructions. + codeAttributeComposer.beginCodeFragment(codeAttribute.u4codeLength); + + // Copy the instructions. + codeAttribute.instructionsAccept(clazz, method, this); // Update the code attribute if any code has been inlined. if (inlinedAny) { - codeAttributeComposer.visitCodeAttribute(clazz, method, codeAttribute); - } - } - } + // Copy the exceptions. + codeAttribute.exceptionsAccept(clazz, method, this); + // Append a label just after the code. + codeAttributeComposer.appendLabel(codeAttribute.u4codeLength); - /** - * Appends the code of the given code attribute. - */ - private void copyCode(Clazz clazz, Method method, CodeAttribute codeAttribute) - { - // The code may expand, due to expanding constant and variable - // instructions. - codeAttributeComposer.beginCodeFragment(codeAttribute.u4codeLength); - - // Copy the instructions. - codeAttribute.instructionsAccept(clazz, method, this); + codeAttributeComposer.endCodeFragment(); - // Append a label just after the code. - codeAttributeComposer.appendLabel(codeAttribute.u4codeLength); - - codeAttributeComposer.endCodeFragment(); + codeAttributeComposer.visitCodeAttribute(clazz, method, codeAttribute); + } + } } @@ -145,7 +138,7 @@ implements AttributeVisitor, public void visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction) { // Copy the instruction. - codeAttributeComposer.appendInstruction(offset, instruction.shrink()); + codeAttributeComposer.appendInstruction(offset, instruction); } @@ -159,9 +152,9 @@ implements AttributeVisitor, case InstructionConstants.OP_INVOKESTATIC: { // Is it a recursive call? - clazz.constantPoolEntryAccept(constantInstruction.constantIndex, this); + clazz.constantPoolEntryAccept(constantInstruction.constantIndex, recursionChecker); - if (recursive) + if (recursionChecker.isRecursive()) { // Is the next instruction a return? int nextOffset = @@ -180,13 +173,13 @@ implements AttributeVisitor, case InstructionConstants.OP_RETURN: { // Isn't the recursive call inside a try/catch block? - codeAttribute.exceptionsAccept(clazz, method, offset, this); + codeAttribute.exceptionsAccept(clazz, method, offset, recursionChecker); - if (recursive) + if (recursionChecker.isRecursive()) { if (DEBUG) { - System.out.println("TailRecursionSimplifier.visitConstantInstruction: ["+ + System.out.println("TailRecursionSimplifier: ["+ clazz.getName()+"."+method.getName(clazz)+method.getDescriptor(clazz)+"], inlining "+constantInstruction.toString(offset)); } @@ -223,23 +216,56 @@ implements AttributeVisitor, } // Copy the instruction. - codeAttributeComposer.appendInstruction(offset, constantInstruction.shrink()); + codeAttributeComposer.appendInstruction(offset, constantInstruction); } - // Implementations for ConstantVisitor. + // Implementations for ExceptionInfoVisitor. - public void visitMethodrefConstant(Clazz clazz, MethodrefConstant methodrefConstant) + public void visitExceptionInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, ExceptionInfo exceptionInfo) { - recursive = targetMethod.equals(methodrefConstant.referencedMember); + codeAttributeComposer.appendException(new ExceptionInfo(exceptionInfo.u2startPC, + exceptionInfo.u2endPC, + exceptionInfo.u2handlerPC, + exceptionInfo.u2catchType)); } - // Implementations for ExceptionInfoVisitor. - - public void visitExceptionInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, ExceptionInfo exceptionInfo) + /** + * This ConstantVisitor and ExceptionInfoVisitor returns whether a method + * invocation can be treated as tail-recursive. + */ + private class MyRecursionChecker + extends SimplifiedVisitor + implements ConstantVisitor, + ExceptionInfoVisitor { - recursive = false; + private boolean recursive; + + + /** + * Returns whether the method invocation can be treated as + * tail-recursive. + */ + public boolean isRecursive() + { + return recursive; + } + + // Implementations for ConstantVisitor. + + public void visitMethodrefConstant(Clazz clazz, MethodrefConstant methodrefConstant) + { + recursive = targetMethod.equals(methodrefConstant.referencedMember); + } + + + // Implementations for ExceptionInfoVisitor. + + public void visitExceptionInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, ExceptionInfo exceptionInfo) + { + recursive = false; + } } @@ -257,7 +283,6 @@ implements AttributeVisitor, (method.getAccessFlags() & ClassConstants.INTERNAL_ACC_STATIC) != 0; // Count the number of parameters, taking into account their categories. - int parameterCount = ClassUtil.internalMethodParameterCount(descriptor); int parameterSize = ClassUtil.internalMethodParameterSize(descriptor); int parameterOffset = isStatic ? 0 : 1; @@ -315,7 +340,7 @@ implements AttributeVisitor, } codeAttributeComposer.appendInstruction(parameterSize-parameterIndex-1, - new VariableInstruction(opcode, parameterOffset + parameterIndex).shrink()); + new VariableInstruction(opcode, parameterOffset + parameterIndex)); } } @@ -323,7 +348,7 @@ implements AttributeVisitor, if (!isStatic) { codeAttributeComposer.appendInstruction(parameterSize, - new VariableInstruction(InstructionConstants.OP_ASTORE, 0).shrink()); + new VariableInstruction(InstructionConstants.OP_ASTORE, 0)); } codeAttributeComposer.endCodeFragment(); diff --git a/src/proguard/optimize/WriteOnlyFieldFilter.java b/src/proguard/optimize/WriteOnlyFieldFilter.java index 578beb2..762bd91 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-2009 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2013 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 1463feb..2e86532 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-2009 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2013 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,8 @@ import proguard.evaluation.*; import proguard.evaluation.value.*; import proguard.optimize.info.*; +import java.util.Arrays; + /** * This AttributeVisitor simplifies the code attributes that it visits, based * on partial evaluation. @@ -52,21 +54,55 @@ implements AttributeVisitor private static boolean DEBUG = true; //*/ + private static final int UNSUPPORTED = -1; + private static final int NOP = InstructionConstants.OP_NOP & 0xff; + private static final int POP = InstructionConstants.OP_POP & 0xff; + private static final int POP2 = InstructionConstants.OP_POP2 & 0xff; + private static final int DUP = InstructionConstants.OP_DUP & 0xff; + private static final int DUP_X1 = InstructionConstants.OP_DUP_X1 & 0xff; + private static final int DUP_X2 = InstructionConstants.OP_DUP_X2 & 0xff; + private static final int DUP2 = InstructionConstants.OP_DUP2 & 0xff; + private static final int DUP2_X1 = InstructionConstants.OP_DUP2_X1 & 0xff; + private static final int DUP2_X2 = InstructionConstants.OP_DUP2_X2 & 0xff; + private static final int SWAP = InstructionConstants.OP_SWAP & 0xff; + private static final int MOV_X2 = DUP_X2 | (POP << 8); + private static final int MOV2_X1 = DUP2_X1 | (POP2 << 8); + private static final int MOV2_X2 = DUP2_X2 | (POP2 << 8); + private static final int POP_X1 = SWAP | (POP << 8); + private static final int POP_X2 = DUP2_X1 | (POP2 << 8) | (POP << 16); + private static final int POP_X3 = UNSUPPORTED; + private static final int POP2_X1 = DUP_X2 | (POP << 8) | (POP2 << 16); + private static final int POP2_X2 = DUP2_X2 | (POP2 << 8) | (POP2 << 16); + private static final int POP3 = POP2 | (POP << 8); + private static final int POP4 = POP2 | (POP2 << 8); + private static final int POP_DUP = POP | (DUP << 8); + private static final int POP_SWAP_POP = POP | (SWAP << 8) | (POP << 16); + private static final int POP2_SWAP_POP = POP2 | (SWAP << 8) | (POP << 16); + private static final int SWAP_DUP_X1 = SWAP | (DUP_X1 << 8); + private static final int SWAP_DUP_X1_SWAP = SWAP | (DUP_X1 << 8) | (SWAP << 16); + private static final int SWAP_POP_DUP = SWAP | (POP << 8) | (DUP << 16); + private static final int SWAP_POP_DUP_X1 = SWAP | (POP << 8) | (DUP_X1 << 16); + private static final int DUP_X2_POP2 = DUP_X2 | (POP2 << 8); + private static final int DUP2_X1_POP3 = DUP2_X1 | (POP2 << 8) | (POP << 16); + private static final int DUP2_X2_POP3 = DUP2_X2 | (POP2 << 8) | (POP << 16); + private static final int DUP2_X2_SWAP_POP = DUP2_X2 | (SWAP << 8) | (POP << 16); + + private final InstructionVisitor extraDeletedInstructionVisitor; private final InstructionVisitor extraAddedInstructionVisitor; - private final PartialEvaluator partialEvaluator; - private final PartialEvaluator simplePartialEvaluator = new PartialEvaluator(); - private final SideEffectInstructionChecker sideEffectInstructionChecker = new SideEffectInstructionChecker(true); - private final MyUnusedParameterSimplifier unusedParameterSimplifier = new MyUnusedParameterSimplifier(); - private final MyProducerMarker producerMarker = new MyProducerMarker(); - private final MyStackConsistencyFixer stackConsistencyFixer = new MyStackConsistencyFixer(); - private final CodeAttributeEditor codeAttributeEditor = new CodeAttributeEditor(false); + private final PartialEvaluator partialEvaluator; + private final PartialEvaluator simplePartialEvaluator = new PartialEvaluator(); + private final SideEffectInstructionChecker sideEffectInstructionChecker = new SideEffectInstructionChecker(true, true); + private final MyUnusedParameterSimplifier unusedParameterSimplifier = new MyUnusedParameterSimplifier(); + private final MyProducerMarker producerMarker = new MyProducerMarker(); + private final MyVariableInitializationMarker variableInitializationMarker = new MyVariableInitializationMarker(); + private final MyStackConsistencyFixer stackConsistencyFixer = new MyStackConsistencyFixer(); + private final CodeAttributeEditor codeAttributeEditor = new CodeAttributeEditor(false, false); - private boolean[][] variablesNecessaryAfter = new boolean[ClassConstants.TYPICAL_CODE_LENGTH][ClassConstants.TYPICAL_VARIABLES_SIZE]; - private boolean[][] stacksNecessaryAfter = new boolean[ClassConstants.TYPICAL_CODE_LENGTH][ClassConstants.TYPICAL_STACK_SIZE]; - private boolean[][] stacksSimplifiedBefore = new boolean[ClassConstants.TYPICAL_CODE_LENGTH][ClassConstants.TYPICAL_STACK_SIZE]; - private boolean[] instructionsNecessary = new boolean[ClassConstants.TYPICAL_CODE_LENGTH]; + private boolean[][] stacksNecessaryAfter = new boolean[ClassConstants.TYPICAL_CODE_LENGTH][ClassConstants.TYPICAL_STACK_SIZE]; + private boolean[][] stacksSimplifiedBefore = new boolean[ClassConstants.TYPICAL_CODE_LENGTH][ClassConstants.TYPICAL_STACK_SIZE]; + private boolean[] instructionsNecessary = new boolean[ClassConstants.TYPICAL_CODE_LENGTH]; private int maxMarkedOffset; @@ -154,6 +190,9 @@ implements AttributeVisitor // Evaluate the method. partialEvaluator.visitCodeAttribute(clazz, method, codeAttribute); + // Evaluate the method the way the JVM verifier would do it. + simplePartialEvaluator.visitCodeAttribute(clazz, method, codeAttribute); + int codeLength = codeAttribute.u4codeLength; // Reset the code changes. @@ -272,22 +311,13 @@ implements AttributeVisitor for (int offset = 0; offset < codeLength; offset++) { - // Is it a variable initialization that hasn't been marked yet? - if (partialEvaluator.isTraced(offset) && - !isInstructionNecessary(offset)) + if (isInstructionNecessary(offset)) { - // Is the corresponding variable necessary anywhere in the code, - // accoriding to a simple partial evaluation? - int variableIndex = partialEvaluator.initializedVariable(offset); - if (variableIndex >= 0 && - isVariableInitializationNecessary(clazz, - method, - codeAttribute, - offset, - variableIndex)) - { - markInstruction(offset); - } + // Mark initializations of the required instruction. + Instruction instruction = InstructionFactory.create(codeAttribute.code, + offset); + + instruction.accept(clazz, method, codeAttribute, offset, variableInitializationMarker); } } if (DEBUG) System.out.println(); @@ -383,12 +413,9 @@ implements AttributeVisitor offset); if (!isInstructionNecessary(offset)) { + codeAttributeEditor.clearModifications(offset); codeAttributeEditor.deleteInstruction(offset); - codeAttributeEditor.insertBeforeInstruction(offset, (Instruction)null); - codeAttributeEditor.replaceInstruction(offset, (Instruction)null); - codeAttributeEditor.insertAfterInstruction(offset, (Instruction)null); - // Visit the instruction, if required. if (extraDeletedInstructionVisitor != null) { @@ -467,7 +494,9 @@ implements AttributeVisitor */ private class MyUnusedParameterSimplifier extends SimplifiedVisitor - implements InstructionVisitor, ConstantVisitor, MemberVisitor + implements InstructionVisitor, + ConstantVisitor, + MemberVisitor { private int invocationOffset; private ConstantInstruction invocationInstruction; @@ -614,8 +643,8 @@ implements AttributeVisitor public void visitVariableInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VariableInstruction variableInstruction) { - // Is the variable being loaded (or incremented)? - if (variableInstruction.opcode < InstructionConstants.OP_ISTORE) + // Is the variable being loaded or incremented? + if (variableInstruction.isLoad()) { markVariableProducers(offset, variableInstruction.variableIndex); } @@ -657,6 +686,32 @@ implements AttributeVisitor /** + * This InstructionVisitor marks variable initializations that are + * necessary to appease the JVM. + */ + private class MyVariableInitializationMarker + extends SimplifiedVisitor + implements InstructionVisitor + { + // Implementations for InstructionVisitor. + + public void visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction) {} + + + public void visitVariableInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VariableInstruction variableInstruction) + { + // Is the variable being loaded or incremented? + if (variableInstruction.isLoad()) + { + // Mark any variable initializations for this variable load that + // are required according to the JVM. + markVariableInitializers(offset, variableInstruction.variableIndex); + } + } + } + + + /** * This InstructionVisitor fixes instructions locally, popping any unused * produced stack entries after marked instructions, and popping produced * stack entries and pushing missing stack entries instead of unmarked @@ -682,17 +737,25 @@ implements AttributeVisitor TracedStack tracedStack = partialEvaluator.getStackBefore(offset); - int top = tracedStack.size() - 1; + int stackSize = tracedStack.size(); int requiredPushCount = 0; - for (int stackIndex = 0; stackIndex < popCount; stackIndex++) + for (int stackIndex = stackSize - popCount; stackIndex < stackSize; stackIndex++) { - // Is the stack entry required by other consumers? - if (!isStackSimplifiedBefore(offset, top - stackIndex) && - !isAnyStackEntryNecessaryAfter(tracedStack.getTopProducerValue(stackIndex).instructionOffsetValue(), top - stackIndex)) + if (!isStackSimplifiedBefore(offset, stackIndex)) { - // Remember to push it. - requiredPushCount++; + // Is this stack entry pushed by any producer + // (because it is required by other consumers)? + if (isStackEntryPresentBefore(offset, stackIndex)) + { + // Mark all produced stack entries. + markStackEntryProducers(offset, stackIndex); + } + else + { + // Remember to push it. + requiredPushCount++; + } } } @@ -703,13 +766,39 @@ implements AttributeVisitor if (requiredPushCount > (instruction.isCategory2() ? 2 : 1)) { - throw new IllegalArgumentException("Unsupported stack size increment ["+requiredPushCount+"]"); + throw new IllegalArgumentException("Unsupported stack size increment ["+requiredPushCount+"] at ["+offset+"]"); } insertPushInstructions(offset, false, tracedStack.getTop(0).computationalType()); } } + // Check all other stack entries, if this is a return + // instruction. + // Typical case: the code returns, but there are still other + // entries left on the stack. These have to be consistent. + InstructionOffsetValue branchTargets = + partialEvaluator.branchTargets(offset); + if (branchTargets != null && + branchTargets.instructionOffsetCount() == 0) + { + TracedStack tracedStack = + partialEvaluator.getStackBefore(offset); + + int unpoppedStackSize = tracedStack.size() - popCount; + + for (int stackIndex = 0; stackIndex < unpoppedStackSize; stackIndex++) + { + // Is this stack entry pushed by any producer + // (because it is required by other consumers)? + if (isStackEntryPresentBefore(offset, stackIndex)) + { + // Mark all produced stack entries. + markStackEntryProducers(offset, stackIndex); + } + } + } + // Check all stack entries that are pushed. // Typical case: a return value that wasn't really required and // that should be popped. @@ -719,13 +808,13 @@ implements AttributeVisitor TracedStack tracedStack = partialEvaluator.getStackAfter(offset); - int top = tracedStack.size() - 1; + int stackSize = tracedStack.size(); int requiredPopCount = 0; - for (int stackIndex = 0; stackIndex < pushCount; stackIndex++) + for (int stackIndex = stackSize - pushCount; stackIndex < stackSize; stackIndex++) { // Is the stack entry required by consumers? - if (!isStackEntryNecessaryAfter(offset, top - stackIndex)) + if (!isStackEntryNecessaryAfter(offset, stackIndex)) { // Remember to pop it. requiredPopCount++; @@ -752,14 +841,18 @@ implements AttributeVisitor TracedStack tracedStack = partialEvaluator.getStackBefore(offset); - int top = tracedStack.size() - 1; + int stackSize = tracedStack.size(); int expectedPopCount = 0; - for (int stackIndex = 0; stackIndex < popCount; stackIndex++) + for (int stackIndex = stackSize - popCount; stackIndex < stackSize; stackIndex++) { - // Is the stack entry required by other consumers? - if (isAnyStackEntryNecessaryAfter(tracedStack.getTopProducerValue(stackIndex).instructionOffsetValue(), top - stackIndex)) + // Is this stack entry pushed by any producer + // (because it is required by other consumers)? + if (isStackEntryPresentBefore(offset, stackIndex)) { + // Mark all produced stack entries. + markStackEntryProducers(offset, stackIndex); + // Remember to pop it. expectedPopCount++; } @@ -782,13 +875,13 @@ implements AttributeVisitor TracedStack tracedStack = partialEvaluator.getStackAfter(offset); - int top = tracedStack.size() - 1; + int stackSize = tracedStack.size(); int expectedPushCount = 0; - for (int stackIndex = 0; stackIndex < pushCount; stackIndex++) + for (int stackIndex = stackSize - pushCount; stackIndex < stackSize; stackIndex++) { // Is the stack entry required by consumers? - if (isStackEntryNecessaryAfter(offset, top - stackIndex)) + if (isStackEntryNecessaryAfter(offset, stackIndex)) { // Remember to push it. expectedPushCount++; @@ -812,44 +905,546 @@ implements AttributeVisitor if (isInstructionNecessary(offset) && isDupOrSwap(simpleInstruction)) { - fixDupInstruction(clazz, codeAttribute, offset, simpleInstruction); + int stackSizeBefore = partialEvaluator.getStackBefore(offset).size(); + + // Check all stack entries that are popped. + // Typical case: a freshly marked variable initialization that + // requires some value on the stack. + int popCount = simpleInstruction.stackPopCount(clazz); + if (popCount > 0) + { + for (int stackIndex = stackSizeBefore - popCount; stackIndex < stackSizeBefore; stackIndex++) + { + // Is this stack entry pushed by any producer + // (because it is required by other consumers)? + if (isStackEntryPresentBefore(offset, stackIndex)) + { + // Mark all produced stack entries. + markStackEntryProducers(offset, stackIndex); + } + } + } + + int topBefore = stackSizeBefore - 1; + int topAfter = partialEvaluator.getStackAfter(offset).size() - 1; + + byte oldOpcode = simpleInstruction.opcode; + + // Simplify the dup/swap instruction if possible. + int newOpcodes = fixDupSwap(offset, oldOpcode, topBefore, topAfter); + + // Did we find a suitabe (extended) opcode? + if (newOpcodes == UNSUPPORTED) + { + // We can't easily emulate some constructs. + throw new UnsupportedOperationException("Can't handle "+simpleInstruction.toString()+" instruction at ["+offset +"]"); + } + + // Is there a single replacement opcode? + if ((newOpcodes & ~0xff) == 0) + { + byte newOpcode = (byte)newOpcodes; + + if (newOpcode == InstructionConstants.OP_NOP) + { + // Delete the instruction. + codeAttributeEditor.deleteInstruction(offset); + + if (extraDeletedInstructionVisitor != null) + { + extraDeletedInstructionVisitor.visitSimpleInstruction(null, null, null, offset, null); + } + + if (DEBUG) System.out.println(" Deleting marked instruction "+simpleInstruction.toString(offset)); + } + else if (newOpcode == oldOpcode) + { + // Leave the instruction unchanged. + codeAttributeEditor.undeleteInstruction(offset); + + if (DEBUG) System.out.println(" Marking unchanged instruction "+simpleInstruction.toString(offset)); + } + else + { + // Replace the instruction. + Instruction replacementInstruction = new SimpleInstruction(newOpcode); + codeAttributeEditor.replaceInstruction(offset, + replacementInstruction); + + if (DEBUG) System.out.println(" Replacing instruction "+simpleInstruction.toString(offset)+" by "+replacementInstruction.toString()); + } + } + else + { + // Collect the replacement instructions. + Instruction[] replacementInstructions = new Instruction[4]; + + if (DEBUG) System.out.println(" Replacing instruction "+simpleInstruction.toString(offset)+" by"); + int count = 0; + while (newOpcodes != 0) + { + SimpleInstruction replacementInstruction = new SimpleInstruction((byte)newOpcodes); + replacementInstructions[count++] = replacementInstruction; + + if (DEBUG) System.out.println(" "+replacementInstruction.toString()); + newOpcodes >>>= 8; + } + + // Create a properly sized array. + if (count < 4) + { + Instruction[] newInstructions = new Instruction[count]; + System.arraycopy(replacementInstructions, 0, newInstructions, 0, count); + replacementInstructions = newInstructions; + } + + codeAttributeEditor.replaceInstruction(offset, + replacementInstructions); + } } else { visitAnyInstruction(clazz, method, codeAttribute, offset, simpleInstruction); } } + + + /** + * Returns a dup/swap opcode that is corrected for the stack entries + * that are present before the instruction and necessary after the + * instruction. The returned integer opcode may contain multiple byte + * opcodes (least significant byte first). + * @param instructionOffset the offset of the dup/swap instruction. + * @param dupSwapOpcode the original dup/swap opcode. + * @param topBefore the index of the top stack entry before + * the instruction (counting from the bottom). + * @param topAfter the index of the top stack entry after + * the instruction (counting from the bottom). + * @return the corrected opcode. + */ + private int fixDupSwap(int instructionOffset, + byte dupSwapOpcode, + int topBefore, + int topAfter) + { + switch (dupSwapOpcode) + { + case InstructionConstants.OP_DUP: return fixedDup (instructionOffset, topBefore, topAfter); + case InstructionConstants.OP_DUP_X1: return fixedDup_x1 (instructionOffset, topBefore, topAfter); + case InstructionConstants.OP_DUP_X2: return fixedDup_x2 (instructionOffset, topBefore, topAfter); + case InstructionConstants.OP_DUP2: return fixedDup2 (instructionOffset, topBefore, topAfter); + case InstructionConstants.OP_DUP2_X1: return fixedDup2_x1(instructionOffset, topBefore, topAfter); + case InstructionConstants.OP_DUP2_X2: return fixedDup2_x2(instructionOffset, topBefore, topAfter); + case InstructionConstants.OP_SWAP: return fixedSwap (instructionOffset, topBefore, topAfter); + default: throw new IllegalArgumentException("Not a dup/swap opcode ["+dupSwapOpcode+"]"); + } + } + + + private int fixedDup(int instructionOffset, int topBefore, int topAfter) + { + boolean stackEntryPresent0 = isStackEntryPresentBefore(instructionOffset, topBefore - 0); + + boolean stackEntryNecessary0 = isStackEntryNecessaryAfter(instructionOffset, topAfter - 0); + boolean stackEntryNecessary1 = isStackEntryNecessaryAfter(instructionOffset, topAfter - 1); + + // Figure out which stack entries should be moved, + // copied, or removed. + return + stackEntryNecessary0 ? + stackEntryNecessary1 ? DUP : // ...O -> ...OO + NOP : // ...O -> ...O + stackEntryNecessary1 ? NOP : // ...O -> ...O + stackEntryPresent0 ? POP : // ...O -> ... + NOP; // ... -> ... + } + + + private int fixedDup_x1(int instructionOffset, int topBefore, int topAfter) + { + boolean stackEntryPresent0 = isStackEntryPresentBefore(instructionOffset, topBefore - 0); + boolean stackEntryPresent1 = isStackEntryPresentBefore(instructionOffset, topBefore - 1); + + boolean stackEntryNecessary0 = isStackEntryNecessaryAfter(instructionOffset, topAfter - 0); + boolean stackEntryNecessary1 = isStackEntryNecessaryAfter(instructionOffset, topAfter - 1); + boolean stackEntryNecessary2 = isStackEntryNecessaryAfter(instructionOffset, topAfter - 2); + + // Figure out which stack entries should be moved, + // copied, or removed. + return + stackEntryNecessary1 ? + stackEntryNecessary2 ? + stackEntryNecessary0 ? DUP_X1 : // ...XO -> ...OXO + SWAP : // ...XO -> ...OX + // !stackEntryNecessary2 + stackEntryNecessary0 ? NOP : // ...XO -> ...XO + stackEntryPresent0 ? POP : // ...XO -> ...X + NOP : // ...X -> ...X + stackEntryPresent1 ? + stackEntryNecessary2 ? + stackEntryNecessary0 ? SWAP_POP_DUP : // ...XO -> ...OO + POP_X1 : // ...XO -> ...O + // !stackEntryNecessary2 + stackEntryNecessary0 ? POP_X1 : // ...XO -> ...O + stackEntryPresent0 ? POP2 : // ...XO -> ... + POP : // ...X -> ... + // !stackEntryPresent1 + stackEntryNecessary2 ? + stackEntryNecessary0 ? DUP : // ...O -> ...OO + NOP : // ...O -> ...O + // !stackEntryNecessary2 + stackEntryNecessary0 ? NOP : // ...O -> ...O + stackEntryPresent0 ? POP : // ...O -> ... + NOP; // ... -> ... + } + + + private int fixedDup_x2(int instructionOffset, int topBefore, int topAfter) + { + boolean stackEntryPresent0 = isStackEntryPresentBefore(instructionOffset, topBefore - 0); + boolean stackEntryPresent1 = isStackEntryPresentBefore(instructionOffset, topBefore - 1); + boolean stackEntryPresent2 = isStackEntryPresentBefore(instructionOffset, topBefore - 2); + + boolean stackEntryNecessary0 = isStackEntryNecessaryAfter(instructionOffset, topAfter - 0); + boolean stackEntryNecessary1 = isStackEntryNecessaryAfter(instructionOffset, topAfter - 1); + boolean stackEntryNecessary2 = isStackEntryNecessaryAfter(instructionOffset, topAfter - 2); + boolean stackEntryNecessary3 = isStackEntryNecessaryAfter(instructionOffset, topAfter - 3); + + // Figure out which stack entries should be moved, + // copied, or removed. + return + stackEntryNecessary1 ? + stackEntryNecessary2 ? + stackEntryNecessary3 ? + stackEntryNecessary0 ? DUP_X2 : // ...XYO -> ...OXYO + MOV_X2 : // ...XYO -> ...OXY + // !stackEntryNecessary3 + stackEntryNecessary0 ? NOP : // ...XYO -> ...XYO + stackEntryPresent0 ? POP : // ...XYO -> ...XY + NOP : // ...XY -> ...XY + stackEntryPresent2 ? + stackEntryNecessary3 ? + // stackEntryNecessary0 ? UNSUPPORTED : // ...XYO -> ...OYO + UNSUPPORTED : // ...XYO -> ...OY + // !stackEntryNecessary3 + stackEntryNecessary0 ? POP_X2 : // ...XYO -> ...YO + stackEntryPresent0 ? POP_SWAP_POP : // ...XYO -> ...Y + POP_X1 : // ...XY -> ...Y + // !stackEntryPresent2 + stackEntryNecessary3 ? + stackEntryNecessary0 ? DUP_X1 : // ...YO -> ...OYO + SWAP : // ...YO -> ...OY + // !stackEntryNecessary3 + stackEntryNecessary0 ? NOP : // ...YO -> ...YO + stackEntryPresent0 ? POP : // ...YO -> ...Y + NOP : // ...Y -> ...Y + stackEntryPresent1 ? + stackEntryNecessary2 ? + stackEntryNecessary3 ? + stackEntryNecessary0 ? SWAP_POP_DUP_X1 : // ...XYO -> ...OXO + DUP_X2_POP2 : // ...XYO -> ...OX + // !stackEntryNecessary3 + stackEntryNecessary0 ? POP_X1 : // ...XYO -> ...XO + stackEntryPresent0 ? POP2 : // ...XYO -> ...X + POP : // ...XY -> ...X + stackEntryPresent2 ? + stackEntryNecessary3 ? + stackEntryNecessary0 ? UNSUPPORTED : // ...XYO -> ...OO + POP2_X1 : // ...XYO -> ...O + // !stackEntryNecessary3 + stackEntryNecessary0 ? POP2_X1 : // ...XYO -> ...O + stackEntryPresent0 ? POP3 : // ...XYO -> ... + POP2 : // ...XY -> ... + // !stackEntryPresent2 + stackEntryNecessary3 ? + stackEntryNecessary0 ? SWAP_POP_DUP : // ...YO -> ...OO + POP_X1 : // ...YO -> ...O + // !stackEntryNecessary3 + stackEntryNecessary0 ? POP_X1 : // ...YO -> ...O + stackEntryPresent0 ? POP2 : // ...YO -> ... + POP : // ...Y -> ... + // !stackEntryPresent1 + stackEntryNecessary2 ? + stackEntryNecessary3 ? + stackEntryNecessary0 ? DUP_X1 : // ...XO -> ...OXO + SWAP : // ...XO -> ...OX + // !stackEntryNecessary3 + stackEntryNecessary0 ? NOP : // ...XO -> ...XO + stackEntryPresent0 ? POP : // ...XO -> ...X + NOP : // ...X -> ...X + stackEntryPresent2 ? + stackEntryNecessary3 ? + stackEntryNecessary0 ? SWAP_POP_DUP : // ...XO -> ...OO + POP_X1 : // ...XO -> ...O + // !stackEntryNecessary3 + stackEntryNecessary0 ? POP_X1 : // ...XO -> ...O + stackEntryPresent0 ? POP2 : // ...XO -> ... + POP : // ...X -> ... + // !stackEntryPresent2 + stackEntryNecessary3 ? + stackEntryNecessary0 ? DUP : // ...O -> ...OO + NOP : // ...O -> ...O + // !stackEntryNecessary3 + stackEntryNecessary0 ? NOP : // ...O -> ...O + stackEntryPresent0 ? POP : // ...O -> ... + NOP; // ... -> ... + } + + + private int fixedDup2(int instructionOffset, int topBefore, int topAfter) + { + boolean stackEntryPresent0 = isStackEntryPresentBefore(instructionOffset, topBefore - 0); + boolean stackEntryPresent1 = isStackEntryPresentBefore(instructionOffset, topBefore - 1); + + boolean stackEntryNecessary0 = isStackEntryNecessaryAfter(instructionOffset, topAfter - 0); + boolean stackEntryNecessary1 = isStackEntryNecessaryAfter(instructionOffset, topAfter - 1); + boolean stackEntryNecessary2 = isStackEntryNecessaryAfter(instructionOffset, topAfter - 2); + boolean stackEntryNecessary3 = isStackEntryNecessaryAfter(instructionOffset, topAfter - 3); + + return + stackEntryNecessary3 ? + stackEntryNecessary2 ? + stackEntryNecessary1 ? + stackEntryNecessary0 ? DUP2 : // ...AB -> ...ABAB + SWAP_DUP_X1 : // ...AB -> ...ABA + // !stackEntryNecessary1 + stackEntryNecessary0 ? DUP : // ...AB -> ...ABB + NOP : // ...AB -> ...AB + // !stackEntryNecessary2 + stackEntryNecessary1 ? + stackEntryNecessary0 ? SWAP_DUP_X1_SWAP : // ...AB -> ...AAB + stackEntryPresent0 ? POP_DUP : // ...AB -> ...AA + DUP : // ...A -> ...AA + // !stackEntryNecessary1 + stackEntryNecessary0 ? NOP : // ...AB -> ...AB + stackEntryPresent0 ? POP : // ...AB -> ...A + NOP : // ...A -> ...A + // !stackEntryNecessary3 + stackEntryNecessary2 ? + stackEntryNecessary1 ? + stackEntryNecessary0 ? DUP_X1 : // ...AB -> ...BAB + SWAP : // ...AB -> ...BA + stackEntryPresent1 ? + stackEntryNecessary0 ? SWAP_POP_DUP : // ...AB -> ...BB + POP_X1 : // ...AB -> ...B + // !stackEntryPresent1 + stackEntryNecessary0 ? POP : // ...B -> ...BB + NOP : // ...B -> ...B + // !stackEntryNecessary2 + stackEntryNecessary1 ? + stackEntryNecessary0 ? NOP : // ...AB -> ...AB + stackEntryPresent0 ? POP : // ...AB -> ...A + NOP : // ...A -> ...A + stackEntryPresent1 ? + stackEntryNecessary0 ? POP_X1 : // ...AB -> ...B + stackEntryPresent0 ? POP2 : // ...AB -> ... + POP : // ...A -> ... + // !stackEntryPresent1 + stackEntryNecessary0 ? NOP : // ...B -> ...B + stackEntryPresent0 ? POP : // ...B -> ... + NOP; // ... -> ... + } + + + private int fixedDup2_x1(int instructionOffset, int topBefore, int topAfter) + { + // We're currently assuming the value to be duplicated + // is a long or a double, taking up two slots, or at + // least consistent. + boolean stackEntriesPresent01 = isStackEntriesPresentBefore(instructionOffset, topBefore - 0, topBefore - 1); + boolean stackEntryPresent2 = isStackEntryPresentBefore( instructionOffset, topBefore - 2); + + boolean stackEntriesNecessary01 = isStackEntriesNecessaryAfter(instructionOffset, topAfter - 0, topAfter - 1); + boolean stackEntryNecessary2 = isStackEntryNecessaryAfter( instructionOffset, topAfter - 2); + boolean stackEntriesNecessary34 = isStackEntriesNecessaryAfter(instructionOffset, topAfter - 3, topAfter - 4); + + // Figure out which stack entries should be moved, + // copied, or removed. + return + stackEntryNecessary2 ? + stackEntriesNecessary34 ? + stackEntriesNecessary01 ? DUP2_X1 : // ...XAB -> ...ABXAB + MOV2_X1 : // ...XAB -> ...ABX + // !stackEntriesNecessary34 + stackEntriesNecessary01 ? NOP : // ...XAB -> ...XAB + stackEntriesPresent01 ? POP2 : // ...XAB -> ...X + NOP : // ...X -> ...X + stackEntryPresent2 ? + stackEntriesNecessary34 ? + stackEntriesNecessary01 ? UNSUPPORTED : // ...XAB -> ...ABAB + POP_X2 : // ...XAB -> ...AB + // !stackEntriesNecessary34 + stackEntriesNecessary01 ? DUP2_X1_POP3 : // ...XAB -> ...AB + stackEntriesPresent01 ? POP3 : // ...XAB -> ... + POP : // ...X -> ... + // !stackEntryPresent2 + stackEntriesNecessary34 ? + stackEntriesNecessary01 ? DUP2 : // ...AB -> ...ABAB + NOP : // ...AB -> ...AB + // !stackEntriesNecessary34 + stackEntriesNecessary01 ? NOP : // ...AB -> ...AB + stackEntriesPresent01 ? POP2 : // ...AB -> ... + NOP; // ... -> ... + } + + + private int fixedDup2_x2(int instructionOffset, int topBefore, int topAfter) + { + // We're currently assuming the value to be duplicated + // is a long or a double, taking up two slots, or at + // least consistent. + boolean stackEntriesPresent01 = isStackEntriesPresentBefore(instructionOffset, topBefore - 0, topBefore - 1); + boolean stackEntryPresent2 = isStackEntryPresentBefore( instructionOffset, topBefore - 2); + boolean stackEntryPresent3 = isStackEntryPresentBefore( instructionOffset, topBefore - 3); + + boolean stackEntriesNecessary01 = isStackEntriesNecessaryAfter(instructionOffset, topAfter - 0, topAfter - 1); + boolean stackEntryNecessary2 = isStackEntryNecessaryAfter( instructionOffset, topAfter - 2); + boolean stackEntryNecessary3 = isStackEntryNecessaryAfter( instructionOffset, topAfter - 3); + boolean stackEntriesNecessary45 = isStackEntriesNecessaryAfter(instructionOffset, topAfter - 4, topAfter - 5); + + // Figure out which stack entries should be moved, + // copied, or removed. + return + stackEntryNecessary2 ? + stackEntryNecessary3 ? + stackEntriesNecessary45 ? + stackEntriesNecessary01 ? DUP2_X2 : // ...XYAB -> ...ABXYAB + MOV2_X2 : // ...XYAB -> ...ABXY + // !stackEntriesNecessary45 + stackEntriesNecessary01 ? NOP : // ...XYAB -> ...XYAB + stackEntriesPresent01 ? POP2 : // ...XYAB -> ...XY + NOP : // ...XY -> ...XY + stackEntryPresent3 ? + stackEntriesNecessary45 ? + stackEntriesNecessary01 ? UNSUPPORTED : // ...XYAB -> ...ABYAB + DUP2_X2_SWAP_POP : // ...XYAB -> ...ABY + // !stackEntriesNecessary45 + stackEntriesNecessary01 ? POP_X3 : // ...XYAB -> ...YAB + stackEntriesPresent01 ? POP2_SWAP_POP : // ...XYAB -> ...Y + POP_X1 : // ...XY -> ...Y + // !stackEntryPresent3 + stackEntriesNecessary45 ? + stackEntriesNecessary01 ? DUP2_X1 : // ...YAB -> ...ABYAB + MOV2_X1 : // ...YAB -> ...ABY + // !stackEntriesNecessary45 + stackEntriesNecessary01 ? NOP : // ...YAB -> ...YAB + stackEntriesPresent01 ? POP2 : // ...YAB -> ...Y + NOP : // ...Y -> ...Y + stackEntryPresent2 ? + stackEntryNecessary3 ? + stackEntriesNecessary45 ? + stackEntriesNecessary01 ? UNSUPPORTED : // ...XYAB -> ...ABXAB + DUP2_X2_POP3 : // ...XYAB -> ...ABX + // !stackEntriesNecessary45 + stackEntriesNecessary01 ? POP_X2 : // ...XYAB -> ...XAB + stackEntriesPresent01 ? POP3 : // ...XYAB -> ...X + POP : // ...XY -> ...X + stackEntryPresent3 ? + stackEntriesNecessary45 ? + stackEntriesNecessary01 ? UNSUPPORTED : // ...XYAB -> ...ABAB + POP2_X2 : // ...XYAB -> ...AB + // !stackEntriesNecessary45 + stackEntriesNecessary01 ? POP2_X2 : // ...XYAB -> ...AB + stackEntriesPresent01 ? POP4 : // ...XYAB -> ... + POP2 : // ...XY -> ... + // !stackEntryPresent3 + stackEntriesNecessary45 ? + stackEntriesNecessary01 ? UNSUPPORTED : // ...YAB -> ...ABAB + POP_X2 : // ...YAB -> ...AB + // !stackEntriesNecessary45 + stackEntriesNecessary01 ? POP_X2 : // ...YAB -> ...AB + stackEntriesPresent01 ? POP3 : // ...YAB -> ... + POP : // ...Y -> ... + // !stackEntryPresent2 + stackEntryNecessary3 ? + stackEntriesNecessary45 ? + stackEntriesNecessary01 ? DUP2_X1 : // ...XAB -> ...ABXAB + MOV2_X1 : // ...XAB -> ...ABX + // !stackEntriesNecessary45 + stackEntriesNecessary01 ? NOP : // ...XAB -> ...XAB + stackEntriesPresent01 ? POP2 : // ...XAB -> ...X + NOP : // ...X -> ...X + stackEntryPresent3 ? + stackEntriesNecessary45 ? + stackEntriesNecessary01 ? UNSUPPORTED : // ...XAB -> ...ABAB + POP_X2 : // ...XAB -> ...AB + // !stackEntriesNecessary45 + stackEntriesNecessary01 ? POP_X2 : // ...XAB -> ...AB + stackEntriesPresent01 ? POP3 : // ...XAB -> ... + POP : // ...X -> ... + // !stackEntryPresent3 + stackEntriesNecessary45 ? + stackEntriesNecessary01 ? DUP2 : // ...AB -> ...ABAB + NOP : // ...AB -> ...AB + // !stackEntriesNecessary45 + stackEntriesNecessary01 ? NOP : // ...AB -> ...AB + stackEntriesPresent01 ? POP2 : // ...AB -> ... + NOP; // ... -> ... + } + + + private int fixedSwap(int instructionOffset, int topBefore, int topAfter) + { + boolean stackEntryPresent0 = isStackEntryPresentBefore(instructionOffset, topBefore - 0); + boolean stackEntryPresent1 = isStackEntryPresentBefore(instructionOffset, topBefore - 1); + + boolean stackEntryNecessary0 = isStackEntryNecessaryAfter(instructionOffset, topAfter - 0); + boolean stackEntryNecessary1 = isStackEntryNecessaryAfter(instructionOffset, topAfter - 1); + + // Figure out which stack entries should be moved + // or removed. + return + stackEntryNecessary0 ? + stackEntryNecessary1 ? SWAP : // ...AB -> ...BA + stackEntryPresent0 ? POP : // ...AB -> ...A + NOP : // ...A -> ...A + stackEntryPresent1 ? POP_X1 : // ...AB -> ...B + NOP; // ...B -> ...B + } } // Small utility methods. /** - * Marks the variable and the corresponding producing instructions - * of the consumer at the given offset. - * @param consumerOffset the offset of the consumer. - * @param variableIndex the index of the variable to be marked. + * Marks the producing 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. */ private void markVariableProducers(int consumerOffset, int variableIndex) { - TracedVariables tracedVariables = - partialEvaluator.getVariablesBefore(consumerOffset); + InstructionOffsetValue producerOffsets = + partialEvaluator.getVariablesBefore(consumerOffset).getProducerValue(variableIndex).instructionOffsetValue(); - // Mark the producer of the loaded value. - markVariableProducers(tracedVariables.getProducerValue(variableIndex).instructionOffsetValue(), - variableIndex); + if (producerOffsets != null) + { + int offsetCount = producerOffsets.instructionOffsetCount(); + for (int offsetIndex = 0; offsetIndex < offsetCount; offsetIndex++) + { + // Make sure the variable and the instruction are marked + // at the producing offset. + int offset = producerOffsets.instructionOffset(offsetIndex); + + markInstruction(offset); + } + } } /** - * Marks the variable and its producing instructions at the given offsets. - * @param producerOffsets the offsets of the producers to be marked. - * @param variableIndex the index of the variable to be marked. + * 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. */ - private void markVariableProducers(InstructionOffsetValue producerOffsets, - int variableIndex) + private void markVariableInitializers(int consumerOffset, + int variableIndex) { + InstructionOffsetValue producerOffsets = + simplePartialEvaluator.getVariablesBefore(consumerOffset).getProducerValue(variableIndex).instructionOffsetValue(); + if (producerOffsets != null) { int offsetCount = producerOffsets.instructionOffsetCount(); @@ -859,8 +1454,15 @@ implements AttributeVisitor // at the producing offset. int offset = producerOffsets.instructionOffset(offsetIndex); - markVariableAfter(offset, variableIndex); - markInstruction(offset); + if (!isInstructionNecessary(offset) && + isVariableInitialization(offset, variableIndex)) + { + if (DEBUG) System.out.print(" Marking initialization of v"+variableIndex+" at "); + + markInstruction(offset); + + if (DEBUG) System.out.println(); + } } } } @@ -877,9 +1479,14 @@ implements AttributeVisitor int consumerOffset, Instruction consumer) { + TracedStack tracedStack = + partialEvaluator.getStackBefore(consumerOffset); + + int stackSize = tracedStack.size(); + // Mark the producers of the popped values. int popCount = consumer.stackPopCount(clazz); - for (int stackIndex = 0; stackIndex < popCount; stackIndex++) + for (int stackIndex = stackSize - popCount; stackIndex < stackSize; stackIndex++) { markStackEntryProducers(consumerOffset, stackIndex); } @@ -891,20 +1498,22 @@ implements AttributeVisitor * of the consumer at the given offset, if the stack entry of the * consumer is marked. * @param consumerOffset the offset of the consumer. - * @param consumerStackIndex the index of the stack entry to be checked + * @param consumerTopStackIndex the index of the stack entry to be checked * (counting from the top). - * @param producerStackIndex the index of the stack entry to be marked + * @param producerTopStackIndex the index of the stack entry to be marked * (counting from the top). */ private void conditionallyMarkStackEntryProducers(int consumerOffset, - int consumerStackIndex, - int producerStackIndex) + int consumerTopStackIndex, + int producerTopStackIndex) { - int top = partialEvaluator.getStackAfter(consumerOffset).size() - 1; + int consumerBottomStackIndex = partialEvaluator.getStackAfter(consumerOffset).size() - consumerTopStackIndex - 1; - if (isStackEntryNecessaryAfter(consumerOffset, top - consumerStackIndex)) + if (isStackEntryNecessaryAfter(consumerOffset, consumerBottomStackIndex)) { - markStackEntryProducers(consumerOffset, producerStackIndex); + int producerBottomStackIndex = partialEvaluator.getStackBefore(consumerOffset).size() - producerTopStackIndex - 1; + + markStackEntryProducers(consumerOffset, producerBottomStackIndex); } } @@ -914,20 +1523,15 @@ implements AttributeVisitor * of the consumer at the given offset. * @param consumerOffset the offset of the consumer. * @param stackIndex the index of the stack entry to be marked - * (counting from the top). + * (counting from the bottom). */ private void markStackEntryProducers(int consumerOffset, int stackIndex) { - TracedStack tracedStack = - partialEvaluator.getStackBefore(consumerOffset); - - int stackBottomIndex = tracedStack.size() - 1 - stackIndex; - - if (!isStackSimplifiedBefore(consumerOffset, stackBottomIndex)) + if (!isStackSimplifiedBefore(consumerOffset, stackIndex)) { - markStackEntryProducers(tracedStack.getTopProducerValue(stackIndex).instructionOffsetValue(), - stackBottomIndex); + markStackEntryProducers(partialEvaluator.getStackBefore(consumerOffset).getBottomProducerValue(stackIndex).instructionOffsetValue(), + stackIndex); } } @@ -1036,244 +1640,6 @@ implements AttributeVisitor /** - * Marks the specified instruction if it is a required dup/swap instruction, - * replacing it by an appropriate variant if necessary. - * @param clazz the class that is being checked. - * @param codeAttribute the code that is being checked. - * @param dupOffset the offset of the dup/swap instruction. - * @param instruction the dup/swap instruction. - */ - private void fixDupInstruction(Clazz clazz, - CodeAttribute codeAttribute, - int dupOffset, - Instruction instruction) - { - int top = partialEvaluator.getStackAfter(dupOffset).size() - 1; - - byte oldOpcode = instruction.opcode; - byte newOpcode = 0; - - // Simplify the popping instruction if possible. - switch (oldOpcode) - { - case InstructionConstants.OP_DUP: - { - boolean stackEntryPresent0 = isStackEntryNecessaryAfter(dupOffset, top - 0); - boolean stackEntryPresent1 = isStackEntryNecessaryAfter(dupOffset, top - 1); - - // Should either the original element or the copy be present? - if (stackEntryPresent0 || - stackEntryPresent1) - { - // Should both the original element and the copy be present? - if (stackEntryPresent0 && - stackEntryPresent1) - { - newOpcode = InstructionConstants.OP_DUP; - } - } - break; - } - case InstructionConstants.OP_DUP_X1: - { - boolean stackEntryPresent0 = isStackEntryNecessaryAfter(dupOffset, top - 0); - boolean stackEntryPresent1 = isStackEntryNecessaryAfter(dupOffset, top - 1); - boolean stackEntryPresent2 = isStackEntryNecessaryAfter(dupOffset, top - 2); - - // Should either the original element or the copy be present? - if (stackEntryPresent0 || - stackEntryPresent2) - { - // Should the copy be present? - if (stackEntryPresent2) - { - // Compute the number of elements to be skipped. - int skipCount = stackEntryPresent1 ? 1 : 0; - - // Should the original element be present? - if (stackEntryPresent0) - { - // Copy the original element. - newOpcode = (byte)(InstructionConstants.OP_DUP + skipCount); - } - else if (skipCount == 1) - { - // Move the original element. - newOpcode = InstructionConstants.OP_SWAP; - } - } - } - break; - } - case InstructionConstants.OP_DUP_X2: - { - boolean stackEntryPresent0 = isStackEntryNecessaryAfter(dupOffset, top - 0); - boolean stackEntryPresent1 = isStackEntryNecessaryAfter(dupOffset, top - 1); - boolean stackEntryPresent2 = isStackEntryNecessaryAfter(dupOffset, top - 2); - boolean stackEntryPresent3 = isStackEntryNecessaryAfter(dupOffset, top - 3); - - // Should either the original element or the copy be present? - if (stackEntryPresent0 || - stackEntryPresent3) - { - // Should the copy be present? - if (stackEntryPresent3) - { - int skipCount = (stackEntryPresent1 ? 1 : 0) + - (stackEntryPresent2 ? 1 : 0); - - // Should the original element be present? - if (stackEntryPresent0) - { - // Copy the original element. - newOpcode = (byte)(InstructionConstants.OP_DUP + skipCount); - } - else if (skipCount == 1) - { - // Move the original element. - newOpcode = InstructionConstants.OP_SWAP; - } - else if (skipCount == 2) - { - // We can't easily move the original element. - throw new UnsupportedOperationException("Can't handle dup_x2 instruction moving original element across two elements at ["+dupOffset +"]"); - } - } - } - break; - } - case InstructionConstants.OP_DUP2: - { - boolean stackEntriesPresent01 = isStackEntriesNecessaryAfter(dupOffset, top - 0, top - 1); - boolean stackEntriesPresent23 = isStackEntriesNecessaryAfter(dupOffset, top - 2, top - 3); - - // Should either the original element or the copy be present? - if (stackEntriesPresent01 || - stackEntriesPresent23) - { - // Should both the original element and the copy be present? - if (stackEntriesPresent01 && - stackEntriesPresent23) - { - newOpcode = InstructionConstants.OP_DUP2; - } - } - break; - } - case InstructionConstants.OP_DUP2_X1: - { - boolean stackEntriesPresent01 = isStackEntriesNecessaryAfter(dupOffset, top - 0, top - 1); - boolean stackEntryPresent2 = isStackEntryNecessaryAfter(dupOffset, top - 2); - boolean stackEntriesPresent34 = isStackEntriesNecessaryAfter(dupOffset, top - 3, top - 4); - - // Should either the original element or the copy be present? - if (stackEntriesPresent01 || - stackEntriesPresent34) - { - // Should the copy be present? - if (stackEntriesPresent34) - { - int skipCount = stackEntryPresent2 ? 1 : 0; - - // Should the original element be present? - if (stackEntriesPresent01) - { - // Copy the original element. - newOpcode = (byte)(InstructionConstants.OP_DUP2 + skipCount); - } - else if (skipCount > 0) - { - // We can't easily move the original element. - throw new UnsupportedOperationException("Can't handle dup2_x1 instruction moving original element across "+skipCount+" elements at ["+dupOffset +"]"); - } - } - } - break; - } - case InstructionConstants.OP_DUP2_X2: - { - boolean stackEntriesPresent01 = isStackEntriesNecessaryAfter(dupOffset, top - 0, top - 1); - boolean stackEntryPresent2 = isStackEntryNecessaryAfter(dupOffset, top - 2); - boolean stackEntryPresent3 = isStackEntryNecessaryAfter(dupOffset, top - 3); - boolean stackEntriesPresent45 = isStackEntriesNecessaryAfter(dupOffset, top - 4, top - 5); - - // Should either the original element or the copy be present? - if (stackEntriesPresent01 || - stackEntriesPresent45) - { - // Should the copy be present? - if (stackEntriesPresent45) - { - int skipCount = (stackEntryPresent2 ? 1 : 0) + - (stackEntryPresent3 ? 1 : 0); - - // Should the original element be present? - if (stackEntriesPresent01) - { - // Copy the original element. - newOpcode = (byte)(InstructionConstants.OP_DUP2 + skipCount); - } - else if (skipCount > 0) - { - // We can't easily move the original element. - throw new UnsupportedOperationException("Can't handle dup2_x2 instruction moving original element across "+skipCount+" elements at ["+dupOffset +"]"); - } - } - } - break; - } - case InstructionConstants.OP_SWAP: - { - boolean stackEntryPresent0 = isStackEntryNecessaryAfter(dupOffset, top - 0); - boolean stackEntryPresent1 = isStackEntryNecessaryAfter(dupOffset, top - 1); - - // Will either element be present? - if (stackEntryPresent0 || - stackEntryPresent1) - { - // Will both elements be present? - if (stackEntryPresent0 && - stackEntryPresent1) - { - newOpcode = InstructionConstants.OP_SWAP; - } - } - break; - } - } - - if (newOpcode == 0) - { - // Delete the instruction. - codeAttributeEditor.deleteInstruction(dupOffset); - - if (extraDeletedInstructionVisitor != null) - { - extraDeletedInstructionVisitor.visitSimpleInstruction(null, null, null, dupOffset, null); - } - - if (DEBUG) System.out.println(" Marking but deleting instruction "+instruction.toString(dupOffset)); - } - else if (newOpcode == oldOpcode) - { - // Leave the instruction unchanged. - codeAttributeEditor.undeleteInstruction(dupOffset); - - if (DEBUG) System.out.println(" Marking unchanged instruction "+instruction.toString(dupOffset)); - } - else - { - // Replace the instruction. - Instruction replacementInstruction = new SimpleInstruction(newOpcode); - codeAttributeEditor.replaceInstruction(dupOffset, - replacementInstruction); - - if (DEBUG) System.out.println(" Replacing instruction "+instruction.toString(dupOffset)+" by "+replacementInstruction.toString()); - } - } - - - /** * Pushes a specified type of stack entry before or at the given offset. * The instruction is marked as necessary. */ @@ -1445,7 +1811,7 @@ implements AttributeVisitor // Remember the replacement instruction. Instruction replacementInstruction = new ConstantInstruction(InstructionConstants.OP_INVOKESTATIC, - constantInstruction.constantIndex).shrink(); + constantInstruction.constantIndex); if (DEBUG) System.out.println(" Replacing by static invocation "+constantInstruction.toString(offset)+" -> "+replacementInstruction.toString()); @@ -1587,22 +1953,6 @@ implements AttributeVisitor int maxStack = codeAttribute.u2maxStack; // Create new arrays for storing information at each instruction offset. - if (variablesNecessaryAfter.length < codeLength || - variablesNecessaryAfter[0].length < maxLocals) - { - variablesNecessaryAfter = new boolean[codeLength][maxLocals]; - } - else - { - for (int offset = 0; offset < codeLength; offset++) - { - for (int index = 0; index < maxLocals; index++) - { - variablesNecessaryAfter[offset][index] = false; - } - } - } - if (stacksNecessaryAfter.length < codeLength || stacksNecessaryAfter[0].length < maxStack) { @@ -1612,10 +1962,7 @@ implements AttributeVisitor { for (int offset = 0; offset < codeLength; offset++) { - for (int index = 0; index < maxStack; index++) - { - stacksNecessaryAfter[offset][index] = false; - } + Arrays.fill(stacksNecessaryAfter[offset], 0, maxStack, false); } } @@ -1628,10 +1975,7 @@ implements AttributeVisitor { for (int offset = 0; offset < codeLength; offset++) { - for (int index = 0; index < maxStack; index++) - { - stacksSimplifiedBefore[offset][index] = false; - } + Arrays.fill(stacksSimplifiedBefore[offset], 0, maxStack, false); } } @@ -1641,103 +1985,61 @@ implements AttributeVisitor } else { - for (int index = 0; index < codeLength; index++) - { - instructionsNecessary[index] = false; - } + Arrays.fill(instructionsNecessary, 0, codeLength, false); } } /** - * Returns whether the given stack entry is present after execution of the - * instruction at the given offset. + * Returns whether the specified variable is initialized at the specified + * offset. */ - private boolean isStackEntriesNecessaryAfter(int instructionOffset, - int stackIndex1, - int stackIndex2) - { - boolean present1 = isStackEntryNecessaryAfter(instructionOffset, stackIndex1); - boolean present2 = isStackEntryNecessaryAfter(instructionOffset, stackIndex2); - -// if (present1 ^ present2) -// { -// throw new UnsupportedOperationException("Can't handle partial use of dup2 instructions"); -// } - - return present1 || present2; - } - - - /** - * Returns whether the specified variable must be initialized at the - * specified offset, according to the verifier of the JVM. - */ - private boolean isVariableInitializationNecessary(Clazz clazz, - Method method, - CodeAttribute codeAttribute, - int initializationOffset, - int variableIndex) + private boolean isVariableInitialization(int instructionOffset, + int variableIndex) { - int codeLength = codeAttribute.u4codeLength; - - // Is the variable necessary anywhere at all? - if (isVariableNecessaryAfterAny(0, codeLength, variableIndex)) + // Wasn't the variable set yet? + Value valueBefore = partialEvaluator.getVariablesBefore(instructionOffset).getValue(variableIndex); + if (valueBefore == null) { - if (DEBUG) System.out.println("Simple partial evaluation for initialization of variable v"+variableIndex+" at ["+initializationOffset+"]"); - - // Lazily compute perform simple partial evaluation, the way the - // JVM preverifier would do it. - simplePartialEvaluator.visitCodeAttribute(clazz, method, codeAttribute); + return true; + } - if (DEBUG) System.out.println("End of simple partial evaluation for initialization of variable v"+variableIndex+" at ["+initializationOffset+"]"); + // Is the computational type different now? + Value valueAfter = partialEvaluator.getVariablesAfter(instructionOffset).getValue(variableIndex); + if (valueAfter.computationalType() != valueBefore.computationalType()) + { + return true; + } - // Check if the variable is necessary elsewhere. - for (int offset = 0; offset < codeLength; offset++) - { - if (isInstructionNecessary(offset)) - { - Value producer = partialEvaluator.getVariablesBefore(offset).getProducerValue(variableIndex); - if (producer != null) - { - Value simpleProducer = simplePartialEvaluator.getVariablesBefore(offset).getProducerValue(variableIndex); - if (simpleProducer != null) - { - InstructionOffsetValue producerOffsets = - producer.instructionOffsetValue(); - InstructionOffsetValue simpleProducerOffsets = - simpleProducer.instructionOffsetValue(); - - // Does the sophisticated partial evaluation have fewer - // producers than the simple one? - // And does the simple partial evaluation point to an - // initialization of the variable? - if (producerOffsets.instructionOffsetCount() < - simpleProducerOffsets.instructionOffsetCount() && - isVariableNecessaryAfterAny(producerOffsets, variableIndex) && - simpleProducerOffsets.contains(initializationOffset)) - { - // Then the initialization is necessary. - return true; - } - } - } - } - } + // Is the reference type different now? + if (valueAfter.computationalType() == Value.TYPE_REFERENCE && + (valueAfter.referenceValue().isNull() == Value.ALWAYS || + !valueAfter.referenceValue().getType().equals(valueBefore.referenceValue().getType()))) + { + return true; } - return false; + // Was the producer an argument (which may be removed)? + Value producersBefore = partialEvaluator.getVariablesBefore(instructionOffset).getProducerValue(variableIndex); + return producersBefore.instructionOffsetValue().instructionOffsetCount() == 1 && + producersBefore.instructionOffsetValue().instructionOffset(0) == PartialEvaluator.AT_METHOD_ENTRY; } - private void markVariableAfter(int instructionOffset, - int variableIndex) + /** + * Marks the stack entry after the given offset. + * @param instructionOffset the offset of the stack entry to be marked. + * @param stackIndex the index of the stack entry to be marked + * (counting from the bottom). + */ + private void markStackEntryAfter(int instructionOffset, + int stackIndex) { - if (!isVariableNecessaryAfter(instructionOffset, variableIndex)) + if (!isStackEntryNecessaryAfter(instructionOffset, stackIndex)) { - if (DEBUG) System.out.print("["+instructionOffset+".v"+variableIndex+"],"); + if (DEBUG) System.out.print("["+instructionOffset+".s"+stackIndex+"],"); - variablesNecessaryAfter[instructionOffset][variableIndex] = true; + stacksNecessaryAfter[instructionOffset][stackIndex] = true; if (maxMarkedOffset < instructionOffset) { @@ -1747,73 +2049,74 @@ implements AttributeVisitor } + /** - * Returns whether the specified variable is ever necessary after any - * instruction in the specified block. + * Returns whether the stack specified entries before the given offset are + * present. */ - private boolean isVariableNecessaryAfterAny(int startOffset, - int endOffset, - int variableIndex) + private boolean isStackEntriesPresentBefore(int instructionOffset, + int stackIndex1, + int stackIndex2) { - for (int offset = startOffset; offset < endOffset; offset++) - { - if (isVariableNecessaryAfter(offset, variableIndex)) - { - return true; - } - } + boolean present1 = isStackEntryPresentBefore(instructionOffset, stackIndex1); + boolean present2 = isStackEntryPresentBefore(instructionOffset, stackIndex2); - return false; +// if (present1 ^ present2) +// { +// throw new UnsupportedOperationException("Can't handle partial use of dup2 instructions"); +// } + + return present1 || present2; } /** - * Returns whether the specified variable is ever necessary after any - * instruction in the specified set of instructions offsets. + * Returns whether the specified stack entry before the given offset is + * present. + * @param instructionOffset the offset of the stack entry to be checked. + * @param stackIndex the index of the stack entry to be checked + * (counting from the bottom). */ - private boolean isVariableNecessaryAfterAny(InstructionOffsetValue instructionOffsetValue, - int variableIndex) + private boolean isStackEntryPresentBefore(int instructionOffset, + int stackIndex) { - int count = instructionOffsetValue.instructionOffsetCount(); - - for (int index = 0; index < count; index++) - { - if (isVariableNecessaryAfter(instructionOffsetValue.instructionOffset(index), - variableIndex)) - { - return true; - } - } - - return false; - } + TracedStack tracedStack = + partialEvaluator.getStackBefore(instructionOffset); + InstructionOffsetValue producerOffsets = + tracedStack.getBottomProducerValue(stackIndex).instructionOffsetValue(); - private boolean isVariableNecessaryAfter(int instructionOffset, - int variableIndex) - { - return instructionOffset == PartialEvaluator.AT_METHOD_ENTRY || - variablesNecessaryAfter[instructionOffset][variableIndex]; + return isAnyStackEntryNecessaryAfter(producerOffsets, stackIndex); } - private void markStackEntryAfter(int instructionOffset, - int stackIndex) + /** + * Returns whether the stack specified entries after the given offset are + * necessary. + */ + private boolean isStackEntriesNecessaryAfter(int instructionOffset, + int stackIndex1, + int stackIndex2) { - if (!isStackEntryNecessaryAfter(instructionOffset, stackIndex)) - { - if (DEBUG) System.out.print("["+instructionOffset+".s"+stackIndex+"],"); + boolean present1 = isStackEntryNecessaryAfter(instructionOffset, stackIndex1); + boolean present2 = isStackEntryNecessaryAfter(instructionOffset, stackIndex2); - stacksNecessaryAfter[instructionOffset][stackIndex] = true; +// if (present1 ^ present2) +// { +// throw new UnsupportedOperationException("Can't handle partial use of dup2 instructions"); +// } - if (maxMarkedOffset < instructionOffset) - { - maxMarkedOffset = instructionOffset; - } - } + return present1 || present2; } + /** + * Returns whether any of the stack entries after the given offsets are + * necessary. + * @param instructionOffsets the offsets of the stack entries to be checked. + * @param stackIndex the index of the stack entries to be checked + * (counting from the bottom). + */ private boolean isAnyStackEntryNecessaryAfter(InstructionOffsetValue instructionOffsets, int stackIndex) { @@ -1831,6 +2134,13 @@ implements AttributeVisitor } + /** + * Returns whether the specified stack entry after the given offset is + * necessary. + * @param instructionOffset the offset of the stack entry to be checked. + * @param stackIndex the index of the stack entry to be checked + * (counting from the bottom). + */ private boolean isStackEntryNecessaryAfter(int instructionOffset, int stackIndex) { @@ -1909,4 +2219,4 @@ implements AttributeVisitor return instructionOffset == PartialEvaluator.AT_METHOD_ENTRY || instructionsNecessary[instructionOffset]; } -}
\ No newline at end of file +} diff --git a/src/proguard/optimize/evaluation/EvaluationSimplifier.java b/src/proguard/optimize/evaluation/EvaluationSimplifier.java index 0c3a9c7..e6e73d9 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-2009 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2013 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 @@ -43,17 +43,20 @@ extends SimplifiedVisitor implements AttributeVisitor, InstructionVisitor { + private static final int POS_ZERO_FLOAT_BITS = Float.floatToIntBits(0.0f); + private static final long POS_ZERO_DOUBLE_BITS = Double.doubleToLongBits(0.0); + //* private static final boolean DEBUG = false; /*/ - private static boolean DEBUG = true; + private static boolean DEBUG = true; //*/ private final InstructionVisitor extraInstructionVisitor; private final PartialEvaluator partialEvaluator; - private final SideEffectInstructionChecker sideEffectInstructionChecker = new SideEffectInstructionChecker(true); - private final CodeAttributeEditor codeAttributeEditor = new CodeAttributeEditor(false); + private final SideEffectInstructionChecker sideEffectInstructionChecker = new SideEffectInstructionChecker(true, true); + private final CodeAttributeEditor codeAttributeEditor = new CodeAttributeEditor(false, true); /** @@ -426,8 +429,9 @@ implements AttributeVisitor, Value pushedValue = partialEvaluator.getStackAfter(offset).getTop(0); if (pushedValue.isParticular()) { + // Push a constant instead. int value = pushedValue.integerValue().value(); - if (value << 16 >> 16 == value) + if ((short)value == value) { replaceConstantPushInstruction(clazz, offset, @@ -442,13 +446,14 @@ implements AttributeVisitor, Instruction replacementInstruction = new ConstantInstruction(InstructionConstants.OP_LDC, - constantPoolEditor.addIntegerConstant(value)).shrink(); + constantPoolEditor.addIntegerConstant(value)); replaceInstruction(clazz, offset, instruction, replacementInstruction); } } else if (pushedValue.isSpecific()) { + // Load an equivalent lower-numbered variable instead, if any. TracedVariables variables = partialEvaluator.getVariablesBefore(offset); for (int variableIndex = 0; variableIndex < maxVariableIndex; variableIndex++) { @@ -459,6 +464,7 @@ implements AttributeVisitor, instruction, InstructionConstants.OP_ILOAD, variableIndex); + break; } } } @@ -492,6 +498,7 @@ implements AttributeVisitor, Value pushedValue = partialEvaluator.getStackAfter(offset).getTop(0); if (pushedValue.isParticular()) { + // Push a constant instead. long value = pushedValue.longValue().value(); if (value == 0L || value == 1L) @@ -509,17 +516,21 @@ implements AttributeVisitor, Instruction replacementInstruction = new ConstantInstruction(InstructionConstants.OP_LDC2_W, - constantPoolEditor.addLongConstant(value)).shrink(); + constantPoolEditor.addLongConstant(value)); replaceInstruction(clazz, offset, instruction, replacementInstruction); } } else if (pushedValue.isSpecific()) { + // Load an equivalent lower-numbered variable instead, if any. TracedVariables variables = partialEvaluator.getVariablesBefore(offset); for (int variableIndex = 0; variableIndex < maxVariableIndex; variableIndex++) { - if (pushedValue.equals(variables.load(variableIndex))) + // Note that we have to check the second part as well. + if (pushedValue.equals(variables.load(variableIndex)) && + variables.load(variableIndex + 1) != null && + variables.load(variableIndex + 1).computationalType() == Value.TYPE_TOP) { replaceVariablePushInstruction(clazz, offset, @@ -559,10 +570,12 @@ implements AttributeVisitor, Value pushedValue = partialEvaluator.getStackAfter(offset).getTop(0); if (pushedValue.isParticular()) { + // Push a constant instead. + // Make sure to distinguish between +0.0 and -0.0. float value = pushedValue.floatValue().value(); - if (value == 0f || - value == 1f || - value == 2f) + if (value == 0.0f && Float.floatToIntBits(value) == POS_ZERO_FLOAT_BITS || + value == 1.0f || + value == 2.0f) { replaceConstantPushInstruction(clazz, offset, @@ -577,13 +590,14 @@ implements AttributeVisitor, Instruction replacementInstruction = new ConstantInstruction(InstructionConstants.OP_LDC, - constantPoolEditor.addFloatConstant(value)).shrink(); + constantPoolEditor.addFloatConstant(value)); replaceInstruction(clazz, offset, instruction, replacementInstruction); } } else if (pushedValue.isSpecific()) { + // Load an equivalent lower-numbered variable instead, if any. TracedVariables variables = partialEvaluator.getVariablesBefore(offset); for (int variableIndex = 0; variableIndex < maxVariableIndex; variableIndex++) { @@ -627,8 +641,10 @@ implements AttributeVisitor, Value pushedValue = partialEvaluator.getStackAfter(offset).getTop(0); if (pushedValue.isParticular()) { + // Push a constant instead. + // Make sure to distinguish between +0.0 and -0.0. double value = pushedValue.doubleValue().value(); - if (value == 0.0 || + if (value == 0.0 && Double.doubleToLongBits(value) == POS_ZERO_DOUBLE_BITS || value == 1.0) { replaceConstantPushInstruction(clazz, @@ -644,17 +660,21 @@ implements AttributeVisitor, Instruction replacementInstruction = new ConstantInstruction(InstructionConstants.OP_LDC2_W, - constantPoolEditor.addDoubleConstant(value)).shrink(); + constantPoolEditor.addDoubleConstant(value)); replaceInstruction(clazz, offset, instruction, replacementInstruction); } } else if (pushedValue.isSpecific()) { + // Load an equivalent lower-numbered variable instead, if any. TracedVariables variables = partialEvaluator.getVariablesBefore(offset); for (int variableIndex = 0; variableIndex < maxVariableIndex; variableIndex++) { - if (pushedValue.equals(variables.load(variableIndex))) + // Note that we have to check the second part as well. + if (pushedValue.equals(variables.load(variableIndex)) && + variables.load(variableIndex + 1) != null && + variables.load(variableIndex + 1).computationalType() == Value.TYPE_TOP) { replaceVariablePushInstruction(clazz, offset, @@ -699,7 +719,7 @@ implements AttributeVisitor, int value) { Instruction replacementInstruction = - new SimpleInstruction(replacementOpcode, value).shrink(); + new SimpleInstruction(replacementOpcode, value); replaceInstruction(clazz, offset, instruction, replacementInstruction); } @@ -716,7 +736,7 @@ implements AttributeVisitor, int variableIndex) { Instruction replacementInstruction = - new VariableInstruction(replacementOpcode, variableIndex).shrink(); + new VariableInstruction(replacementOpcode, variableIndex); replaceInstruction(clazz, offset, instruction, replacementInstruction); } @@ -794,8 +814,8 @@ implements AttributeVisitor, { // Replace the branch instruction by a simple branch instruction. Instruction replacementInstruction = - new BranchInstruction(InstructionConstants.OP_GOTO_W, - branchOffset).shrink(); + new BranchInstruction(InstructionConstants.OP_GOTO, + branchOffset); replaceInstruction(clazz, offset, instruction, replacementInstruction); } diff --git a/src/proguard/optimize/evaluation/LivenessAnalyzer.java b/src/proguard/optimize/evaluation/LivenessAnalyzer.java index 9915027..5ce8ccb 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-2009 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2013 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 @@ -79,6 +79,16 @@ implements AttributeVisitor, /** + * Returns whether the instruction at the given offset has ever been + * executed during the partial evaluation. + */ + public boolean isTraced(int instructionOffset) + { + return partialEvaluator.isTraced(instructionOffset); + } + + + /** * Returns whether the specified variable is alive before the instruction * at the given offset. */ diff --git a/src/proguard/optimize/evaluation/LoadingInvocationUnit.java b/src/proguard/optimize/evaluation/LoadingInvocationUnit.java index 8379c57..d6baa67 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-2009 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2013 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 @@ -35,9 +35,9 @@ import proguard.evaluation.value.*; public class LoadingInvocationUnit extends BasicInvocationUnit { - private boolean loadFieldValues; - private boolean loadMethodParameterValues; - private boolean loadMethodReturnValues; + private final boolean loadFieldValues; + private final boolean loadMethodParameterValues; + private final boolean loadMethodReturnValues; /** @@ -84,8 +84,6 @@ extends BasicInvocationUnit value.isParticular()) { return value; -// // Make sure the value is refreshed. -// return refresh(value); } } } @@ -110,8 +108,6 @@ extends BasicInvocationUnit value.isParticular()) { return value; -// // Make sure the value is refreshed. -// return refresh(value); } } } @@ -134,8 +130,6 @@ extends BasicInvocationUnit value.isParticular()) { return value; -// // Make sure the value is refreshed. -// return refresh(value); } } @@ -163,8 +157,6 @@ extends BasicInvocationUnit value.isParticular()) { return value; -// // Make sure the value is refreshed. -// return refresh(value); } } } @@ -173,8 +165,8 @@ extends BasicInvocationUnit refConstant, type); } -// -// + + // // Small utility methods. // // private Value refresh(Value value) diff --git a/src/proguard/optimize/evaluation/PartialEvaluator.java b/src/proguard/optimize/evaluation/PartialEvaluator.java index 5790a36..6a5bedf 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-2009 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2013 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 @@ -31,6 +31,8 @@ import proguard.evaluation.*; import proguard.evaluation.value.*; import proguard.optimize.peephole.BranchTargetFinder; +import java.util.Arrays; + /** * This AttributeVisitor performs partial evaluation on the code attributes * that it visits. @@ -68,7 +70,6 @@ implements AttributeVisitor, private TracedStack[] stacksAfter = new TracedStack[ClassConstants.TYPICAL_CODE_LENGTH]; private boolean[] generalizedContexts = new boolean[ClassConstants.TYPICAL_CODE_LENGTH]; private int[] evaluationCounts = new int[ClassConstants.TYPICAL_CODE_LENGTH]; - private int[] initializedVariables = new int[ClassConstants.TYPICAL_CODE_LENGTH]; private boolean evaluateExceptions; private final BasicBranchUnit branchUnit; @@ -189,6 +190,47 @@ implements AttributeVisitor, if (DEBUG) { method.accept(clazz, new ClassPrinter()); + + System.out.println("Evaluation results:"); + + int offset = 0; + do + { + if (isBranchOrExceptionTarget(offset)) + { + System.out.println("Branch target from ["+branchOriginValues[offset]+"]:"); + if (isTraced(offset)) + { + System.out.println(" Vars: "+variablesBefore[offset]); + System.out.println(" Stack: "+stacksBefore[offset]); + } + } + + Instruction instruction = InstructionFactory.create(codeAttribute.code, + offset); + System.out.println(instruction.toString(offset)); + + if (isTraced(offset)) + { + int initializationOffset = branchTargetFinder.initializationOffset(offset); + if (initializationOffset != NONE) + { + System.out.println(" is to be initialized at ["+initializationOffset+"]"); + } + + InstructionOffsetValue branchTargets = branchTargets(offset); + if (branchTargets != null) + { + System.out.println(" has overall been branching to "+branchTargets); + } + + System.out.println(" Vars: "+variablesAfter[offset]); + System.out.println(" Stack: "+stacksAfter[offset]); + } + + offset += instruction.length(offset); + } + while (offset < codeAttribute.u4codeLength); } throw ex; @@ -212,7 +254,8 @@ implements AttributeVisitor, TracedStack stack = new TracedStack(codeAttribute.u2maxStack); // Initialize the reusable arrays and variables. - initializeVariables(clazz, method, codeAttribute, variables, stack); + initializeArrays(codeAttribute); + initializeParameters(clazz, method, codeAttribute, variables); // Find all instruction offsets,... codeAttribute.accept(clazz, method, branchTargetFinder); @@ -249,12 +292,6 @@ implements AttributeVisitor, if (isTraced(offset)) { - int variableIndex = initializedVariable(offset); - if (variableIndex >= 0) - { - System.out.println(" is initializing variable v"+variableIndex); - } - int initializationOffset = branchTargetFinder.initializationOffset(offset); if (initializationOffset != NONE) { @@ -479,16 +516,6 @@ implements AttributeVisitor, } - /** - * Returns the variable that is initialized at the given instruction offset, - * or <code>NONE</code> if no variable was initialized. - */ - public int initializedVariable(int instructionOffset) - { - return initializedVariables[instructionOffset]; - } - - // Utility methods to evaluate instruction blocks. /** @@ -702,9 +729,6 @@ implements AttributeVisitor, // Reset the trace value. InstructionOffsetValue traceValue = InstructionOffsetValue.EMPTY_VALUE; - // Reset the initialization flag. - variables.resetInitialization(); - // Note that the instruction is only volatile. Instruction instruction = InstructionFactory.create(code, instructionOffset); @@ -743,9 +767,6 @@ implements AttributeVisitor, throw ex; } - // Collect the offsets of the instructions whose results were used. - initializedVariables[instructionOffset] = variables.getInitializationIndex(); - // Collect the branch targets from the branch unit. InstructionOffsetValue branchTargets = branchUnit.getTraceBranchTargets(); int branchTargetCount = branchTargets.instructionOffsetCount(); @@ -844,8 +865,7 @@ implements AttributeVisitor, if (instruction.opcode == InstructionConstants.OP_JSR || instruction.opcode == InstructionConstants.OP_JSR_W) { - // Evaluate the subroutine, possibly in another partial - // evaluator. + // Evaluate the subroutine in another partial evaluator. evaluateSubroutine(clazz, method, codeAttribute, @@ -887,21 +907,13 @@ implements AttributeVisitor, if (DEBUG) System.out.println("Evaluating subroutine from "+subroutineStart+" to "+subroutineEnd); - PartialEvaluator subroutinePartialEvaluator = this; - - // Create a temporary partial evaluator if necessary. - if (evaluationCounts[subroutineStart] > 0) - { - if (DEBUG) System.out.println("Creating new partial evaluator for subroutine"); - - subroutinePartialEvaluator = new PartialEvaluator(this); + // Create a temporary partial evaluator, so there are no conflicts + // with variables that are alive across subroutine invocations, between + // different invocations. + PartialEvaluator subroutinePartialEvaluator = + new PartialEvaluator(this); - subroutinePartialEvaluator.initializeVariables(clazz, - method, - codeAttribute, - variables, - stack); - } + subroutinePartialEvaluator.initializeArrays(codeAttribute); // Evaluate the subroutine. subroutinePartialEvaluator.evaluateInstructionBlockAndExceptionHandlers(clazz, @@ -912,11 +924,9 @@ implements AttributeVisitor, subroutineStart, subroutineEnd); - // Merge back the temporary partial evaluator if necessary. - if (subroutinePartialEvaluator != this) - { - generalize(subroutinePartialEvaluator, 0, codeAttribute.u4codeLength); - } + // Merge back the temporary partial evaluator. This way, we'll get + // the lowest common denominator of stacks and variables. + generalize(subroutinePartialEvaluator, 0, codeAttribute.u4codeLength); if (DEBUG) System.out.println("Ending subroutine from "+subroutineStart+" to "+subroutineEnd); } @@ -952,13 +962,12 @@ implements AttributeVisitor, if (evaluationCounts[offset] == 0) { - variablesBefore[offset] = other.variablesBefore[offset]; - stacksBefore[offset] = other.stacksBefore[offset]; - variablesAfter[offset] = other.variablesAfter[offset]; - stacksAfter[offset] = other.stacksAfter[offset]; - generalizedContexts[offset] = other.generalizedContexts[offset]; - evaluationCounts[offset] = other.evaluationCounts[offset]; - initializedVariables[offset] = other.initializedVariables[offset]; + variablesBefore[offset] = other.variablesBefore[offset]; + stacksBefore[offset] = other.stacksBefore[offset]; + variablesAfter[offset] = other.variablesAfter[offset]; + stacksAfter[offset] = other.stacksAfter[offset]; + generalizedContexts[offset] = other.generalizedContexts[offset]; + evaluationCounts[offset] = other.evaluationCounts[offset]; } else { @@ -968,7 +977,6 @@ implements AttributeVisitor, stacksAfter[offset] .generalize(other.stacksAfter[offset]); //generalizedContexts[offset] evaluationCounts[offset] += other.evaluationCounts[offset]; - //initializedVariables[offset] } } } @@ -1094,11 +1102,7 @@ implements AttributeVisitor, /** * Initializes the data structures for the variables, stack, etc. */ - private void initializeVariables(Clazz clazz, - Method method, - CodeAttribute codeAttribute, - TracedVariables variables, - TracedStack stack) + private void initializeArrays(CodeAttribute codeAttribute) { int codeLength = codeAttribute.u4codeLength; @@ -1106,33 +1110,25 @@ implements AttributeVisitor, if (variablesAfter.length < codeLength) { // Create new arrays. - branchOriginValues = new InstructionOffsetValue[codeLength]; - branchTargetValues = new InstructionOffsetValue[codeLength]; - variablesBefore = new TracedVariables[codeLength]; - stacksBefore = new TracedStack[codeLength]; - variablesAfter = new TracedVariables[codeLength]; - stacksAfter = new TracedStack[codeLength]; - generalizedContexts = new boolean[codeLength]; - evaluationCounts = new int[codeLength]; - initializedVariables = new int[codeLength]; - - // Reset the arrays. - for (int index = 0; index < codeLength; index++) - { - initializedVariables[index] = NONE; - } + branchOriginValues = new InstructionOffsetValue[codeLength]; + branchTargetValues = new InstructionOffsetValue[codeLength]; + variablesBefore = new TracedVariables[codeLength]; + stacksBefore = new TracedStack[codeLength]; + variablesAfter = new TracedVariables[codeLength]; + stacksAfter = new TracedStack[codeLength]; + generalizedContexts = new boolean[codeLength]; + evaluationCounts = new int[codeLength]; } else { // Reset the arrays. + Arrays.fill(branchOriginValues, null); + Arrays.fill(branchTargetValues, null); + Arrays.fill(generalizedContexts, false); + Arrays.fill(evaluationCounts, 0); + for (int index = 0; index < codeLength; index++) { - branchOriginValues[index] = null; - branchTargetValues[index] = null; - generalizedContexts[index] = false; - evaluationCounts[index] = 0; - initializedVariables[index] = NONE; - if (variablesBefore[index] != null) { variablesBefore[index].reset(codeAttribute.u2maxLocals); @@ -1154,7 +1150,17 @@ implements AttributeVisitor, } } } + } + + /** + * Initializes the data structures for the variables, stack, etc. + */ + private void initializeParameters(Clazz clazz, + Method method, + CodeAttribute codeAttribute, + TracedVariables variables) + { // Create the method parameters. TracedVariables parameters = new TracedVariables(codeAttribute.u2maxLocals); diff --git a/src/proguard/optimize/evaluation/StoringInvocationUnit.java b/src/proguard/optimize/evaluation/StoringInvocationUnit.java index bcbb69f..846f685 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-2009 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2013 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/TracedBranchUnit.java b/src/proguard/optimize/evaluation/TracedBranchUnit.java index fa5bb79..e6acf6f 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-2009 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2013 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 b3ae81c..73efddc 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-2009 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2013 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,10 +21,10 @@ package proguard.optimize.evaluation; import proguard.classfile.*; +import proguard.classfile.attribute.visitor.*; +import proguard.classfile.editor.*; import proguard.classfile.visitor.MemberVisitor; import proguard.classfile.attribute.*; -import proguard.classfile.attribute.visitor.AttributeVisitor; -import proguard.classfile.editor.VariableRemapper; import proguard.classfile.util.*; /** @@ -35,7 +35,9 @@ import proguard.classfile.util.*; */ public class VariableOptimizer extends SimplifiedVisitor -implements AttributeVisitor +implements AttributeVisitor, + LocalVariableInfoVisitor, + LocalVariableTypeInfoVisitor { //* private static final boolean DEBUG = false; @@ -51,6 +53,7 @@ implements AttributeVisitor private final LivenessAnalyzer livenessAnalyzer = new LivenessAnalyzer(); private final VariableRemapper variableRemapper = new VariableRemapper(); + private VariableCleaner variableCleaner = new VariableCleaner(); private int[] variableMap = new int[ClassConstants.TYPICAL_VARIABLES_SIZE]; @@ -101,6 +104,11 @@ implements AttributeVisitor // Analyze the liveness of the variables in the code. livenessAnalyzer.visitCodeAttribute(clazz, method, codeAttribute); + // Trim the variables in the local variable tables, because even + // clipping the tables individually may leave some inconsistencies + // between them. + codeAttribute.attributesAccept(clazz, method, this); + int startIndex = (method.getAccessFlags() & ClassConstants.INTERNAL_ACC_STATIC) != 0 || reuseThis ? 0 : 1; @@ -142,23 +150,19 @@ implements AttributeVisitor } } - // Remap the variables. + // Have we been able to remap any variables? if (remapping) { if (DEBUG) { - System.out.println("Remapping variables:"); - System.out.println(" Class "+ ClassUtil.externalClassName(clazz.getName())); - System.out.println(" Method "+ClassUtil.externalFullMethodDescription(clazz.getName(), - 0, - method.getName(clazz), - method.getDescriptor(clazz))); - for (int index = 0; index < variableSize; index++) + System.out.println("VariableOptimizer: "+clazz.getName()+"."+method.getName(clazz)+method.getDescriptor(clazz)); + for (int index= 0; index < variableSize; index++) { - System.out.println(" ["+index+"] -> ["+variableMap[index]+"]"); + System.out.println(" v"+index+" -> "+variableMap[index]); } } + // Remap the variables. variableRemapper.setVariableMap(variableMap); variableRemapper.visitCodeAttribute(clazz, method, codeAttribute); @@ -168,6 +172,71 @@ implements AttributeVisitor method.accept(clazz, extraVariableMemberVisitor); } } + else + { + // Just clean up any empty variables. + variableCleaner.visitCodeAttribute(clazz, method, codeAttribute); + } + } + + + public void visitLocalVariableTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTableAttribute localVariableTableAttribute) + { + // Trim the variables in the local variable table. + localVariableTableAttribute.localVariablesAccept(clazz, method, codeAttribute, this); + } + + + public void visitLocalVariableTypeTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeTableAttribute localVariableTypeTableAttribute) + { + // Trim the variables in the local variable type table. + localVariableTypeTableAttribute.localVariablesAccept(clazz, method, codeAttribute, this); + } + + + // Implementations for LocalVariableInfoVisitor. + + public void visitLocalVariableInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableInfo localVariableInfo) + { + // Trim the local variable to the instructions at which it is alive. + int variable = localVariableInfo.u2index; + int startPC = localVariableInfo.u2startPC; + int endPC = startPC + localVariableInfo.u2length; + + startPC = firstLiveness(startPC, endPC, variable); + endPC = lastLiveness(startPC, endPC, variable); + + // Leave the start address of unused variables unchanged. + int length = endPC - startPC; + if (length > 0) + { + localVariableInfo.u2startPC = startPC; + } + + localVariableInfo.u2length = length; + } + + + // Implementations for LocalVariableTypeInfoVisitor. + + public void visitLocalVariableTypeInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeInfo localVariableTypeInfo) + { + // Trim the local variable type to the instructions at which it is alive. + int variable = localVariableTypeInfo.u2index; + int startPC = localVariableTypeInfo.u2startPC; + int endPC = startPC + localVariableTypeInfo.u2length; + + startPC = firstLiveness(startPC, endPC, variable); + endPC = lastLiveness(startPC, endPC, variable); + + // Leave the start address of unused variables unchanged. + int length = endPC - startPC; + if (length > 0) + { + localVariableTypeInfo.u2startPC = startPC; + } + + localVariableTypeInfo.u2length = length; } @@ -241,4 +310,48 @@ implements AttributeVisitor } } } + + + /** + * Returns the first instruction offset between the given offsets at which + * the given variable goes alive. + */ + private int firstLiveness(int startOffset, int endOffset, int variableIndex) + { + for (int offset = startOffset; offset < endOffset; offset++) + { + if (livenessAnalyzer.isTraced(offset) && + livenessAnalyzer.isAliveBefore(offset, variableIndex)) + { + return offset; + } + } + + return endOffset; + } + + + /** + * Returns the last instruction offset between the given offsets before + * which the given variable is still alive. + */ + private int lastLiveness(int startOffset, int endOffset, int variableIndex) + { + int previousOffset = endOffset; + + for (int offset = endOffset-1; offset >= startOffset; offset--) + { + if (livenessAnalyzer.isTraced(offset)) + { + if (livenessAnalyzer.isAliveBefore(offset, variableIndex)) + { + return previousOffset; + } + + previousOffset = offset; + } + } + + return endOffset; + } } diff --git a/src/proguard/optimize/info/AccessMethodMarker.java b/src/proguard/optimize/info/AccessMethodMarker.java index 6965cec..e4c8d7c 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-2009 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2013 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,8 @@ package proguard.optimize.info; import proguard.classfile.*; -import proguard.classfile.attribute.CodeAttribute; +import proguard.classfile.attribute.*; +import proguard.classfile.attribute.visitor.*; import proguard.classfile.constant.*; import proguard.classfile.constant.visitor.ConstantVisitor; import proguard.classfile.instruction.*; @@ -71,6 +72,20 @@ implements InstructionVisitor, } + public void visitInvokeDynamicConstant(Clazz clazz, InvokeDynamicConstant invokeDynamicConstant) + { + // Check the bootstrap method. + invokeDynamicConstant.bootstrapMethodHandleAccept(clazz, this); + } + + + public void visitMethodHandleConstant(Clazz clazz, MethodHandleConstant methodHandleConstant) + { + // Check the method reference. + clazz.constantPoolEntryAccept(methodHandleConstant.u2referenceIndex, this); + } + + public void visitAnyRefConstant(Clazz clazz, RefConstant refConstant) { // Check the referenced class. diff --git a/src/proguard/optimize/info/BackwardBranchMarker.java b/src/proguard/optimize/info/BackwardBranchMarker.java index 9e09b0f..07bfefb 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-2009 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2013 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 3f2a06f..8f87a08 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-2009 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2013 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 5e17763..762e7de 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-2009 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2013 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 0cc350e..1752f0c 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-2009 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2013 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,8 +25,9 @@ import proguard.classfile.util.SimplifiedVisitor; import proguard.classfile.visitor.ClassVisitor; /** - * This InstructionVisitor marks all classes that are used in an 'instanceof' - * test by any of the instructions that it visits. + * This ClassVisitor marks all program classes that it visits as caught. + * This means that these classes are exception classes that occur in exception + * handlers. * * @author Eric Lafortune */ diff --git a/src/proguard/optimize/info/ClassOptimizationInfo.java b/src/proguard/optimize/info/ClassOptimizationInfo.java index 99b6e7b..dbe041e 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-2009 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2013 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 containsStaticInitializer = false; private boolean containsPackageVisibleMembers = false; private boolean invokesPackageVisibleMembers = false; private Clazz targetClass; @@ -87,6 +88,18 @@ public class ClassOptimizationInfo } + public void setContainsStaticInitializer() + { + containsStaticInitializer = true; + } + + + public boolean containsStaticInitializer() + { + return containsStaticInitializer; + } + + public void setContainsPackageVisibleMembers() { containsPackageVisibleMembers = true; @@ -129,6 +142,7 @@ public class ClassOptimizationInfo this.isInstanceofed |= other.isInstanceofed; this.isDotClassed |= other.isDotClassed; this.isCaught |= other.isCaught; + this.containsStaticInitializer |= other.containsStaticInitializer; this.containsPackageVisibleMembers |= other.containsPackageVisibleMembers; this.invokesPackageVisibleMembers |= other.invokesPackageVisibleMembers; } diff --git a/src/proguard/optimize/info/ClassOptimizationInfoSetter.java b/src/proguard/optimize/info/ClassOptimizationInfoSetter.java index 9cb167c..f3d78e2 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-2009 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2013 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/DotClassFilter.java b/src/proguard/optimize/info/DotClassFilter.java index 8cbe7f0..c3fd878 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-2009 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2013 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 b5f12a7..ef5cfd1 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-2009 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2013 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/ExceptionInstructionChecker.java b/src/proguard/optimize/info/ExceptionInstructionChecker.java index 2792d90..4bfa96f 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-2009 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2013 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 @@ -67,34 +67,39 @@ implements InstructionVisitor byte opcode = simpleInstruction.opcode; // Check for instructions that may throw exceptions. - if (opcode == InstructionConstants.OP_IDIV || - opcode == InstructionConstants.OP_LDIV || - opcode == InstructionConstants.OP_IREM || - opcode == InstructionConstants.OP_LREM || - opcode == InstructionConstants.OP_IALOAD || - opcode == InstructionConstants.OP_LALOAD || - opcode == InstructionConstants.OP_FALOAD || - opcode == InstructionConstants.OP_DALOAD || - opcode == InstructionConstants.OP_AALOAD || - opcode == InstructionConstants.OP_BALOAD || - opcode == InstructionConstants.OP_CALOAD || - opcode == InstructionConstants.OP_SALOAD || - opcode == InstructionConstants.OP_IASTORE || - opcode == InstructionConstants.OP_LASTORE || - opcode == InstructionConstants.OP_FASTORE || - opcode == InstructionConstants.OP_DASTORE || - opcode == InstructionConstants.OP_AASTORE || - opcode == InstructionConstants.OP_BASTORE || - opcode == InstructionConstants.OP_CASTORE || - opcode == InstructionConstants.OP_SASTORE || - opcode == InstructionConstants.OP_NEWARRAY || - opcode == InstructionConstants.OP_ARRAYLENGTH || - opcode == InstructionConstants.OP_ATHROW || - opcode == InstructionConstants.OP_MONITORENTER || - opcode == InstructionConstants.OP_MONITOREXIT) + // 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) { - // These instructions may throw exceptions. - mayThrowExceptions = true; + 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: + case InstructionConstants.OP_DALOAD: + case InstructionConstants.OP_AALOAD: + case InstructionConstants.OP_BALOAD: + case InstructionConstants.OP_CALOAD: + case InstructionConstants.OP_SALOAD: + case InstructionConstants.OP_IASTORE: + case InstructionConstants.OP_LASTORE: + case InstructionConstants.OP_FASTORE: + case InstructionConstants.OP_DASTORE: + case InstructionConstants.OP_AASTORE: + case InstructionConstants.OP_BASTORE: + case InstructionConstants.OP_CASTORE: + case InstructionConstants.OP_SASTORE: + case InstructionConstants.OP_NEWARRAY: + case InstructionConstants.OP_ARRAYLENGTH: + case InstructionConstants.OP_ATHROW: + case InstructionConstants.OP_MONITORENTER: + // These instructions may throw exceptions. + mayThrowExceptions = true; } } @@ -105,31 +110,32 @@ implements InstructionVisitor byte opcode = constantInstruction.opcode; // Check for instructions that may throw exceptions. - if (opcode == InstructionConstants.OP_GETSTATIC || - opcode == InstructionConstants.OP_PUTSTATIC || - opcode == InstructionConstants.OP_GETFIELD || - opcode == InstructionConstants.OP_PUTFIELD || - opcode == InstructionConstants.OP_INVOKEVIRTUAL || - opcode == InstructionConstants.OP_INVOKESPECIAL || - opcode == InstructionConstants.OP_INVOKESTATIC || - opcode == InstructionConstants.OP_INVOKEINTERFACE || - opcode == InstructionConstants.OP_NEW || - opcode == InstructionConstants.OP_ANEWARRAY || - opcode == InstructionConstants.OP_CHECKCAST || - opcode == InstructionConstants.OP_MULTIANEWARRAY) + switch (opcode) { - // These instructions may throw exceptions. - mayThrowExceptions = true; + case InstructionConstants.OP_GETSTATIC: + case InstructionConstants.OP_PUTSTATIC: + case InstructionConstants.OP_GETFIELD: + case InstructionConstants.OP_PUTFIELD: + case InstructionConstants.OP_INVOKEVIRTUAL: + case InstructionConstants.OP_INVOKESPECIAL: + case InstructionConstants.OP_INVOKESTATIC: + case InstructionConstants.OP_INVOKEINTERFACE: + case InstructionConstants.OP_INVOKEDYNAMIC: + case InstructionConstants.OP_NEW: + case InstructionConstants.OP_ANEWARRAY: + case InstructionConstants.OP_CHECKCAST: + case InstructionConstants.OP_INSTANCEOF: + case InstructionConstants.OP_MULTIANEWARRAY: + // These instructions may throw exceptions. + mayThrowExceptions = true; } -// else -// if (opcode == InstructionConstants.OP_INVOKEVIRTUAL || -// opcode == InstructionConstants.OP_INVOKESPECIAL || -// opcode == InstructionConstants.OP_INVOKESTATIC || -// opcode == InstructionConstants.OP_INVOKEINTERFACE) -// { + +// case InstructionConstants.OP_INVOKEVIRTUAL: +// case InstructionConstants.OP_INVOKESPECIAL: +// case InstructionConstants.OP_INVOKESTATIC: +// 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 7a2d068..0fa9167 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-2009 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2013 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,11 @@ package proguard.optimize.info; import proguard.classfile.*; -import proguard.classfile.util.MethodLinker; +import proguard.classfile.attribute.visitor.*; +import proguard.classfile.attribute.*; +import proguard.classfile.util.*; import proguard.evaluation.value.*; +import proguard.evaluation.ConstantValueFactory; /** * This class stores some optimization information that can be attached to @@ -31,8 +34,11 @@ import proguard.evaluation.value.*; * @author Eric Lafortune */ public class FieldOptimizationInfo +extends SimplifiedVisitor +implements AttributeVisitor { - private static final SpecificValueFactory VALUE_FACTORY = new SpecificValueFactory(); + private static final SpecificValueFactory VALUE_FACTORY = new SpecificValueFactory(); + private static final ConstantValueFactory CONSTANT_VALUE_FACTORY = new ConstantValueFactory(VALUE_FACTORY); private boolean isWritten; private boolean isRead; @@ -43,9 +49,33 @@ public class FieldOptimizationInfo public FieldOptimizationInfo(Clazz clazz, Field field) { + int accessFlags = field.getAccessFlags(); + isWritten = - isRead = (field.getAccessFlags() & ClassConstants.INTERNAL_ACC_VOLATILE) != 0; - value = initialValue(field.getDescriptor(clazz)); + isRead = (accessFlags & ClassConstants.INTERNAL_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)); + } + } + + + public FieldOptimizationInfo(FieldOptimizationInfo FieldOptimizationInfo) + { + this.isWritten = FieldOptimizationInfo.isWritten; + this.isRead = FieldOptimizationInfo.isRead; + this.canBeMadePrivate = FieldOptimizationInfo.canBeMadePrivate; + this.referencedClass = FieldOptimizationInfo.referencedClass; + this.value = FieldOptimizationInfo.value; } @@ -113,6 +143,18 @@ public class FieldOptimizationInfo } + // Implementations for AttributeVisitor. + + public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} + + + public void visitConstantValueAttribute(Clazz clazz, Field field, ConstantValueAttribute constantValueAttribute) + { + // Retrieve the initial static field value. + value = CONSTANT_VALUE_FACTORY.constantValue(clazz, constantValueAttribute.u2constantValueIndex); + } + + // Small utility methods. private Value initialValue(String type) @@ -147,13 +189,13 @@ public class FieldOptimizationInfo public static void setFieldOptimizationInfo(Clazz clazz, Field field) { - MethodLinker.lastMember(field).setVisitorInfo(new FieldOptimizationInfo(clazz, field)); + field.setVisitorInfo(new FieldOptimizationInfo(clazz, field)); } public static FieldOptimizationInfo getFieldOptimizationInfo(Field field) { - Object visitorInfo = MethodLinker.lastMember(field).getVisitorInfo(); + Object visitorInfo = field.getVisitorInfo(); return visitorInfo instanceof FieldOptimizationInfo ? (FieldOptimizationInfo)visitorInfo : diff --git a/src/proguard/optimize/info/InstanceofClassFilter.java b/src/proguard/optimize/info/InstanceofClassFilter.java index 35e1d77..7cd85bc 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-2009 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2013 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 c60e1f8..96d5baf 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-2009 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2013 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 a24e617..a659f06 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-2009 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2013 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 124c23b..b4afffd 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-2009 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2013 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 a170a8e..3c27c93 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-2009 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2013 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,22 +38,22 @@ implements MemberVisitor { // Implementations for MemberVisitor. - public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) + public void visitProgramField(ProgramClass programClass, ProgramField programField) { - if (!KeepMarker.isKept(programMethod)) + if (!KeepMarker.isKept(programField)) { - MethodOptimizationInfo.setMethodOptimizationInfo(programClass, - programMethod); + FieldOptimizationInfo.setFieldOptimizationInfo(programClass, + programField); } } - public void visitProgramField(ProgramClass programClass, ProgramField programField) + public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) { - if (!KeepMarker.isKept(programField)) + if (!KeepMarker.isKept(programMethod)) { - FieldOptimizationInfo.setFieldOptimizationInfo(programClass, - programField); + MethodOptimizationInfo.setMethodOptimizationInfo(programClass, + programMethod); } } } diff --git a/src/proguard/optimize/info/MethodInvocationMarker.java b/src/proguard/optimize/info/MethodInvocationMarker.java index 2528c94..afb2336 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-2009 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2013 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 d3b1bde..fe754e5 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-2009 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2013 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 @@ -204,7 +204,7 @@ public class MethodOptimizationInfo public void setParameterUsed(int parameterIndex) { - usedParameters |= 1 << parameterIndex; + usedParameters |= 1L << parameterIndex; } @@ -216,7 +216,7 @@ public class MethodOptimizationInfo public boolean isParameterUsed(int parameterIndex) { - return parameterIndex >= 64 || (usedParameters & (1 << parameterIndex)) != 0; + return parameterIndex >= 64 || (usedParameters & (1L << parameterIndex)) != 0; } diff --git a/src/proguard/optimize/info/NoSideEffectMethodMarker.java b/src/proguard/optimize/info/NoSideEffectMethodMarker.java index 5c78408..bf5ce45 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-2009 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2013 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,7 +38,7 @@ implements MemberVisitor { // A visitor info flag to indicate the visitor accepter is being kept, // but that it doesn't have any side effects. - private static final Object KEPT_BUT_NO_SIDE_EFFECTS = new Object(); + public static final Object KEPT_BUT_NO_SIDE_EFFECTS = new Object(); // Implementations for MemberVisitor. diff --git a/src/proguard/optimize/info/NonPrivateMemberMarker.java b/src/proguard/optimize/info/NonPrivateMemberMarker.java index d451643..06f8500 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-2009 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2013 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 @@ -77,15 +77,9 @@ implements ClassVisitor, public void visitStringConstant(Clazz clazz, StringConstant stringConstant) { - Clazz referencedClass = stringConstant.referencedClass; - - // Is it refering to another class or class member? - if (referencedClass != null && - !referencedClass.equals(clazz)) - { - // The referenced class member, if any, can never be made private. - stringConstant.referencedMemberAccept(this); - } + // The referenced class member, if any, can never be made private, + // even if it's in the same class. + stringConstant.referencedMemberAccept(this); } @@ -93,7 +87,7 @@ implements ClassVisitor, { Clazz referencedClass = refConstant.referencedClass; - // Is it refering to a class member in another class? + // Is it referring to a class member in another class? // The class member might be in another class, or // it may be referenced through another class. if (referencedClass != null && diff --git a/src/proguard/optimize/info/PackageVisibleMemberContainingClassMarker.java b/src/proguard/optimize/info/PackageVisibleMemberContainingClassMarker.java index d40bc6b..02e1a18 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-2009 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2013 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,18 +22,36 @@ package proguard.optimize.info; import proguard.classfile.*; import proguard.classfile.util.SimplifiedVisitor; -import proguard.classfile.visitor.MemberVisitor; +import proguard.classfile.visitor.*; /** - * This MemberVisitor marks all classes that contain visited package visible - * members. + * This ClassVisitor marks all classes that contain package visible members. * * @author Eric Lafortune */ public class PackageVisibleMemberContainingClassMarker extends SimplifiedVisitor -implements MemberVisitor +implements ClassVisitor, + MemberVisitor { + // Implementations for ClassVisitor. + + public void visitAnyClass(Clazz clazz) + { + // Check the class itself. + if ((clazz.getAccessFlags() & ClassConstants.INTERNAL_ACC_PUBLIC) == 0) + { + setPackageVisibleMembers(clazz); + } + else + { + // Check the members. + clazz.fieldsAccept(this); + clazz.methodsAccept(this); + } + } + + // Implementations for MemberVisitor. public void visitAnyMember(Clazz clazz, Member member) diff --git a/src/proguard/optimize/info/PackageVisibleMemberInvokingClassMarker.java b/src/proguard/optimize/info/PackageVisibleMemberInvokingClassMarker.java index 9ec8ec6..3148e3d 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-2009 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2013 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 @@ -24,39 +24,87 @@ import proguard.classfile.*; import proguard.classfile.constant.visitor.ConstantVisitor; import proguard.classfile.constant.*; import proguard.classfile.util.SimplifiedVisitor; +import proguard.classfile.visitor.*; /** - * This ConstantVisitor marks all classes that invoke package visible members - * in other classes. + * This ConstantVisitor marks all classes that refer to package visible classes + * or class members. * * @author Eric Lafortune */ public class PackageVisibleMemberInvokingClassMarker extends SimplifiedVisitor -implements ConstantVisitor +implements ConstantVisitor, + ClassVisitor, + MemberVisitor { + private Clazz referencingClass; + + // Implementations for ConstantVisitor. public void visitAnyConstant(Clazz clazz, Constant constant) {} + public void visitStringConstant(Clazz clazz, StringConstant stringConstant) + { + // Check the referenced class and class member, if any. + if (stringConstant.referencedClass != clazz) + { + referencingClass = clazz; + + stringConstant.referencedClassAccept(this); + stringConstant.referencedMemberAccept(this); + } + } + + public void visitAnyRefConstant(Clazz clazz, RefConstant refConstant) { - Clazz referencedClass = refConstant.referencedClass; - if (referencedClass != null && - (referencedClass.getAccessFlags() & + // Check the referenced class and class member. + if (refConstant.referencedClass != clazz) + { + referencingClass = clazz; + + refConstant.referencedClassAccept(this); + refConstant.referencedMemberAccept(this); + } + } + + + public void visitClassConstant(Clazz clazz, ClassConstant classConstant) + { + // Check the referenced class. + if (classConstant.referencedClass != clazz) + { + referencingClass = clazz; + + classConstant.referencedClassAccept(this); + } + } + + + // Implementations for ClassVisitor. + + public void visitAnyClass(Clazz clazz) + { + if ((clazz.getAccessFlags() & ClassConstants.INTERNAL_ACC_PUBLIC) == 0) { - setInvokesPackageVisibleMembers(clazz); + setInvokesPackageVisibleMembers(referencingClass); } + } - Member referencedMember = refConstant.referencedMember; - if (referencedMember != null && - (referencedMember.getAccessFlags() & + + // Implementations for MemberVisitor. + + public void visitAnyMember(Clazz clazz, Member member) + { + if ((member.getAccessFlags() & (ClassConstants.INTERNAL_ACC_PUBLIC | ClassConstants.INTERNAL_ACC_PRIVATE)) == 0) { - setInvokesPackageVisibleMembers(clazz); + setInvokesPackageVisibleMembers(referencingClass); } } diff --git a/src/proguard/optimize/info/ParameterUsageMarker.java b/src/proguard/optimize/info/ParameterUsageMarker.java index 15ce88a..a2a264d 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-2009 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2013 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.info; import proguard.classfile.*; +import proguard.classfile.attribute.*; +import proguard.classfile.attribute.visitor.AttributeVisitor; import proguard.classfile.instruction.*; import proguard.classfile.instruction.visitor.InstructionVisitor; -import proguard.classfile.attribute.visitor.AttributeVisitor; -import proguard.classfile.attribute.*; import proguard.classfile.util.*; import proguard.classfile.visitor.MemberVisitor; +import proguard.evaluation.value.Value; import proguard.optimize.evaluation.PartialEvaluator; -import proguard.evaluation.value.*; /** * This MemberVisitor counts the parameters and marks the used parameters diff --git a/src/proguard/optimize/info/ReadWriteFieldMarker.java b/src/proguard/optimize/info/ReadWriteFieldMarker.java index 57d8561..6bd4b2f 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-2009 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2013 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 8be9dc1..91f1f02 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-2009 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2013 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,11 +29,14 @@ import proguard.classfile.instruction.visitor.InstructionVisitor; import proguard.classfile.util.SimplifiedVisitor; import proguard.classfile.visitor.*; +import java.util.*; + /** - * This class can tell whether an instruction has any side effects. Return - * instructions can be included or not. + * This class can tell whether an instruction has any side effects outside of + * its method. Return instructions can be included or not. * * @see ReadWriteFieldMarker + * @see StaticInitializerContainingClassMarker * @see NoSideEffectMethodMarker * @see SideEffectMethodMarker * @author Eric Lafortune @@ -44,23 +47,38 @@ implements InstructionVisitor, ConstantVisitor, MemberVisitor { + private 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 Clazz referencingClass; private boolean hasSideEffects; - public SideEffectInstructionChecker(boolean includeReturnInstructions) + public SideEffectInstructionChecker(boolean includeReturnInstructions, + boolean includeLocalFieldAccess) { this.includeReturnInstructions = includeReturnInstructions; + this.includeLocalFieldAccess = includeLocalFieldAccess; } - public boolean hasSideEffects(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction) + /** + * Returns whether the given instruction has side effects outside of its + * method. + */ + public boolean hasSideEffects(Clazz clazz, + Method method, + CodeAttribute codeAttribute, + int offset, + Instruction instruction) { hasSideEffects = false; - instruction.accept(clazz, method, codeAttribute, offset, this); + instruction.accept(clazz, method, codeAttribute, offset, this); return hasSideEffects; } @@ -76,29 +94,46 @@ implements InstructionVisitor, byte opcode = simpleInstruction.opcode; // Check for instructions that might cause side effects. - if (opcode == InstructionConstants.OP_IASTORE || - opcode == InstructionConstants.OP_LASTORE || - opcode == InstructionConstants.OP_FASTORE || - opcode == InstructionConstants.OP_DASTORE || - opcode == InstructionConstants.OP_AASTORE || - opcode == InstructionConstants.OP_BASTORE || - opcode == InstructionConstants.OP_CASTORE || - opcode == InstructionConstants.OP_SASTORE || - opcode == InstructionConstants.OP_ATHROW || - opcode == InstructionConstants.OP_MONITORENTER || - opcode == InstructionConstants.OP_MONITOREXIT || - (includeReturnInstructions && - (opcode == InstructionConstants.OP_IRETURN || - opcode == InstructionConstants.OP_LRETURN || - opcode == InstructionConstants.OP_FRETURN || - opcode == InstructionConstants.OP_DRETURN || - opcode == InstructionConstants.OP_ARETURN || - opcode == InstructionConstants.OP_RETURN))) + switch (opcode) { - // These instructions always cause a side effect. - hasSideEffects = true; + case InstructionConstants.OP_IALOAD: + case InstructionConstants.OP_LALOAD: + case InstructionConstants.OP_FALOAD: + case InstructionConstants.OP_DALOAD: + case InstructionConstants.OP_AALOAD: + case InstructionConstants.OP_BALOAD: + case InstructionConstants.OP_CALOAD: + case InstructionConstants.OP_SALOAD: + // These instructions strictly taken may cause a side effect + // (NullPointerException, ArrayIndexOutOfBoundsException). + hasSideEffects = OPTIMIZE_CONSERVATIVELY; + break; + + case InstructionConstants.OP_IASTORE: + case InstructionConstants.OP_LASTORE: + case InstructionConstants.OP_FASTORE: + case InstructionConstants.OP_DASTORE: + case InstructionConstants.OP_AASTORE: + case InstructionConstants.OP_BASTORE: + case InstructionConstants.OP_CASTORE: + case InstructionConstants.OP_SASTORE: + case InstructionConstants.OP_ATHROW : + case InstructionConstants.OP_MONITORENTER: + case InstructionConstants.OP_MONITOREXIT: + // These instructions always cause a side effect. + hasSideEffects = true; + break; + + case InstructionConstants.OP_IRETURN: + case InstructionConstants.OP_LRETURN: + case InstructionConstants.OP_FRETURN: + case InstructionConstants.OP_DRETURN: + case InstructionConstants.OP_ARETURN: + case InstructionConstants.OP_RETURN: + // These instructions may have a side effect. + hasSideEffects = includeReturnInstructions; + break; } - } @@ -107,10 +142,12 @@ implements InstructionVisitor, byte opcode = variableInstruction.opcode; // Check for instructions that might cause side effects. - if (includeReturnInstructions && - opcode == InstructionConstants.OP_RET) + switch (opcode) { - hasSideEffects = true; + case InstructionConstants.OP_RET: + // This instruction may have a side effect. + hasSideEffects = includeReturnInstructions; + break; } } @@ -120,16 +157,41 @@ implements InstructionVisitor, byte opcode = constantInstruction.opcode; // Check for instructions that might cause side effects. - if (opcode == InstructionConstants.OP_PUTSTATIC || - opcode == InstructionConstants.OP_PUTFIELD || - opcode == InstructionConstants.OP_INVOKEVIRTUAL || - opcode == InstructionConstants.OP_INVOKESPECIAL || - opcode == InstructionConstants.OP_INVOKESTATIC || - opcode == InstructionConstants.OP_INVOKEINTERFACE) + switch (opcode) { - // Check if the field is write-only or volatile, or if the invoked - // method is causing any side effects. - clazz.constantPoolEntryAccept(constantInstruction.constantIndex, this); + case InstructionConstants.OP_GETSTATIC: + 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. + clazz.constantPoolEntryAccept(constantInstruction.constantIndex, this); + break; + + case InstructionConstants.OP_GETFIELD: + case InstructionConstants.OP_PUTFIELD: + case InstructionConstants.OP_INVOKEVIRTUAL: + case InstructionConstants.OP_INVOKEINTERFACE: + case InstructionConstants.OP_INVOKEDYNAMIC: + 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, or if the + // invoked method is causing any side effects. + clazz.constantPoolEntryAccept(constantInstruction.constantIndex, this); + } + break; + + case InstructionConstants.OP_CHECKCAST: + // This instructions strictly taken may cause a side effect + // (ClassCastException). + hasSideEffects = OPTIMIZE_CONSERVATIVELY; + break; } } @@ -139,59 +201,48 @@ implements InstructionVisitor, byte opcode = branchInstruction.opcode; // Check for instructions that might cause side effects. - if (includeReturnInstructions && - (opcode == InstructionConstants.OP_JSR || - opcode == InstructionConstants.OP_JSR_W)) + switch (opcode) { - hasSideEffects = true; + case InstructionConstants.OP_JSR: + case InstructionConstants.OP_JSR_W: + hasSideEffects = includeReturnInstructions; + break; } } // Implementations for ConstantVisitor. + public void visitInvokeDynamicConstant(Clazz clazz, InvokeDynamicConstant invokeDynamicConstant) + { + // We'll have to assume invoking an unknown method has side effects. + hasSideEffects = true; + } + + public void visitFieldrefConstant(Clazz clazz, FieldrefConstant fieldrefConstant) { + // Pass the referencing class. + referencingClass = clazz; + // We'll have to assume accessing an unknown field has side effects. hasSideEffects = true; - // Check the referenced field. + // Check the referenced field, if known. fieldrefConstant.referencedMemberAccept(this); } public void visitAnyMethodrefConstant(Clazz clazz, RefConstant refConstant) { - Member referencedMember = refConstant.referencedMember; + // Pass the referencing class. + referencingClass = clazz; - // Do we have a reference to the method? - if (referencedMember == null) - { - // We'll have to assume invoking the unknown method has side effects. - hasSideEffects = true; - } - else - { - // First check the referenced method itself. - refConstant.referencedMemberAccept(this); - - // If the result isn't conclusive, check down the hierarchy. - if (!hasSideEffects) - { - Clazz referencedClass = refConstant.referencedClass; - Method referencedMethod = (Method)referencedMember; - - // Check all other implementations of the method down the class - // hierarchy. - if ((referencedMethod.getAccessFlags() & ClassConstants.INTERNAL_ACC_PRIVATE) == 0) - { - clazz.hierarchyAccept(false, false, false, true, - new NamedMethodVisitor(referencedMethod.getName(referencedClass), - referencedMethod.getDescriptor(referencedClass), - this)); - } - } - } + // We'll have to assume invoking an unknown method has side effects. + hasSideEffects = true; + + // Check the referenced method, if known. + refConstant.referencedMemberAccept(this); } @@ -199,14 +250,25 @@ implements InstructionVisitor, public void visitProgramField(ProgramClass programClass, ProgramField programField) { - hasSideEffects = ReadWriteFieldMarker.isRead(programField); + 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)))); } public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) { - hasSideEffects = hasSideEffects || - SideEffectMethodMarker.hasSideEffects(programMethod); + // Note that side effects already include synchronization of some + // implementation of the method. + hasSideEffects = + !NoSideEffectMethodMarker.hasNoSideEffects(programMethod) && + (SideEffectMethodMarker.hasSideEffects(programMethod) || + (!programClass.equals(referencingClass) && + !initializedSuperClasses(referencingClass).containsAll(initializedSuperClasses(programClass)))); } @@ -218,7 +280,28 @@ implements InstructionVisitor, public void visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryMethod) { - hasSideEffects = hasSideEffects || - !NoSideEffectMethodMarker.hasNoSideEffects(libraryMethod); + hasSideEffects = + !NoSideEffectMethodMarker.hasNoSideEffects(libraryMethod); + } + + + /** + * Returns the set of superclasses and interfaces that are initialized. + */ + private Set initializedSuperClasses(Clazz clazz) + { + Set set = new HashSet(); + + // Visit all superclasses and interfaces, collecting the ones that have + // static initializers. + clazz.hierarchyAccept(true, true, true, false, + new StaticInitializerContainingClassFilter( + new NamedMethodVisitor(ClassConstants.INTERNAL_METHOD_NAME_CLINIT, + ClassConstants.INTERNAL_METHOD_TYPE_CLINIT, + new SideEffectMethodFilter( + new MemberToClassVisitor( + new ClassCollector(set)))))); + + return set; } } diff --git a/src/proguard/optimize/info/SideEffectMethodFilter.java b/src/proguard/optimize/info/SideEffectMethodFilter.java new file mode 100644 index 0000000..52e072a --- /dev/null +++ b/src/proguard/optimize/info/SideEffectMethodFilter.java @@ -0,0 +1,73 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 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.MemberVisitor; + +/** + * This MemberVisitor delegates all its method calls to another MemberVisitor, + * but only for Method objects that are marked as having side effects. + * + * @see SideEffectMethodMarker + * + * @author Eric Lafortune + */ +public class SideEffectMethodFilter +implements MemberVisitor +{ + private final MemberVisitor memberVisitor; + + + /** + * Creates a new SideEffectMethodFilter. + * @param memberVisitor the member visitor to which the visiting will be + * delegated. + */ + public SideEffectMethodFilter(MemberVisitor memberVisitor) + { + this.memberVisitor = memberVisitor; + } + + + // Implementations for MemberVisitor. + + public void visitProgramField(ProgramClass programClass, ProgramField programField) {} + public void visitLibraryField(LibraryClass libraryClass, LibraryField libraryField) {} + + + public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) + { + if (SideEffectMethodMarker.hasSideEffects(programMethod)) + { + memberVisitor.visitProgramMethod(programClass, programMethod); + } + } + + + public void visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryMethod) + { + if (SideEffectMethodMarker.hasSideEffects(libraryMethod)) + { + memberVisitor.visitLibraryMethod(libraryClass, libraryMethod); + } + } +}
\ No newline at end of file diff --git a/src/proguard/optimize/info/SideEffectMethodMarker.java b/src/proguard/optimize/info/SideEffectMethodMarker.java index 25fda72..f7953c0 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-2009 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2013 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,8 +41,9 @@ implements ClassPoolVisitor, MemberVisitor, AttributeVisitor { - // A reusable object for checking whether instructions have side effects. - private final SideEffectInstructionChecker sideEffectInstructionChecker = new SideEffectInstructionChecker(false); + // Reusable objects for checking whether instructions have side effects. + private final SideEffectInstructionChecker sideEffectInstructionChecker = new SideEffectInstructionChecker(false, true); + private final SideEffectInstructionChecker initializerSideEffectInstructionChecker = new SideEffectInstructionChecker(false, false); // Parameters and values for visitor methods. private int newSideEffectCount; @@ -130,6 +131,11 @@ implements ClassPoolVisitor, byte[] code = codeAttribute.code; int length = codeAttribute.u4codeLength; + SideEffectInstructionChecker checker = + method.getName(clazz).equals(ClassConstants.INTERNAL_METHOD_NAME_CLINIT) ? + initializerSideEffectInstructionChecker : + sideEffectInstructionChecker; + // Go over all instructions. int offset = 0; do @@ -138,11 +144,11 @@ implements ClassPoolVisitor, Instruction instruction = InstructionFactory.create(code, offset); // Check if it may be throwing exceptions. - if (sideEffectInstructionChecker.hasSideEffects(clazz, - method, - codeAttribute, - offset, - instruction)) + if (checker.hasSideEffects(clazz, + method, + codeAttribute, + offset, + instruction)) { return true; } diff --git a/src/proguard/optimize/info/StaticInitializerContainingClassFilter.java b/src/proguard/optimize/info/StaticInitializerContainingClassFilter.java new file mode 100644 index 0000000..36aa392 --- /dev/null +++ b/src/proguard/optimize/info/StaticInitializerContainingClassFilter.java @@ -0,0 +1,62 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 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 instantiated. + * + * @author Eric Lafortune + */ +public class StaticInitializerContainingClassFilter +implements ClassVisitor +{ + private final ClassVisitor classVisitor; + + + public StaticInitializerContainingClassFilter(ClassVisitor classVisitor) + { + this.classVisitor = classVisitor; + } + + + // Implementations for ClassVisitor. + + public void visitProgramClass(ProgramClass programClass) + { + if (StaticInitializerContainingClassMarker.containsStaticInitializer(programClass)) + { + classVisitor.visitProgramClass(programClass); + } + } + + + public void visitLibraryClass(LibraryClass libraryClass) + { + if (StaticInitializerContainingClassMarker.containsStaticInitializer(libraryClass)) + { + classVisitor.visitLibraryClass(libraryClass); + } + } +}
\ No newline at end of file diff --git a/src/proguard/optimize/info/StaticInitializerContainingClassMarker.java b/src/proguard/optimize/info/StaticInitializerContainingClassMarker.java new file mode 100644 index 0000000..3a7e642 --- /dev/null +++ b/src/proguard/optimize/info/StaticInitializerContainingClassMarker.java @@ -0,0 +1,65 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2013 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.util.SimplifiedVisitor; +import proguard.classfile.visitor.*; + +/** + * This ClassVisitor marks all classes that contain static initializers. + * + * @author Eric Lafortune + */ +public class StaticInitializerContainingClassMarker +extends SimplifiedVisitor +implements ClassVisitor +{ + // Implementations for ClassVisitor. + + public void visitAnyClass(Clazz clazz) + { + if (clazz.findMethod(ClassConstants.INTERNAL_METHOD_NAME_CLINIT, + ClassConstants.INTERNAL_METHOD_TYPE_CLINIT) != null) + { + setStaticInitializer(clazz); + } + } + + + // Small utility methods. + + private static void setStaticInitializer(Clazz clazz) + { + ClassOptimizationInfo info = ClassOptimizationInfo.getClassOptimizationInfo(clazz); + if (info != null) + { + info.setContainsStaticInitializer(); + } + } + + + public static boolean containsStaticInitializer(Clazz clazz) + { + ClassOptimizationInfo info = ClassOptimizationInfo.getClassOptimizationInfo(clazz); + return info == null || info.containsStaticInitializer(); + } +}
\ No newline at end of file diff --git a/src/proguard/optimize/info/SuperInvocationMarker.java b/src/proguard/optimize/info/SuperInvocationMarker.java index 6f3d3bd..37b118a 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-2009 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2013 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/VariableUsageMarker.java b/src/proguard/optimize/info/VariableUsageMarker.java index 660c4ba..b189ca9 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-2009 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2013 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,6 +27,8 @@ import proguard.classfile.instruction.*; import proguard.classfile.instruction.visitor.InstructionVisitor; import proguard.classfile.util.SimplifiedVisitor; +import java.util.Arrays; + /** * This AttributeVisitor marks the local variables that are used in the code * attributes that it visits. @@ -62,14 +64,13 @@ implements AttributeVisitor, // Try to reuse the previous array. if (variableUsed.length < maxLocals) { + // Create a new array. variableUsed = new boolean[maxLocals]; } else { - for (int index = 0; index < maxLocals; index++) - { - variableUsed[index] = false; - } + // Reset the array. + Arrays.fill(variableUsed, 0, maxLocals, false); } codeAttribute.instructionsAccept(clazz, method, this); diff --git a/src/proguard/optimize/peephole/BranchTargetFinder.java b/src/proguard/optimize/peephole/BranchTargetFinder.java index 8f650bb..79499f1 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-2009 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2013 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,6 +29,8 @@ import proguard.classfile.instruction.*; import proguard.classfile.instruction.visitor.InstructionVisitor; import proguard.classfile.util.SimplifiedVisitor; +import java.util.Arrays; + /** * This AttributeVisitor finds all instruction offsets, branch targets, and * exception targets in the CodeAttribute objects that it visits. @@ -45,12 +47,15 @@ implements AttributeVisitor, //* private static final boolean DEBUG = false; /*/ - private static boolean DEBUG = true; + 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 UNKNOWN = -1; + public static final int NO_SUBROUTINE = -2; + private static final short INSTRUCTION = 1 << 0; private static final short BRANCH_ORIGIN = 1 << 1; private static final short BRANCH_TARGET = 1 << 2; @@ -70,9 +75,10 @@ implements AttributeVisitor, private int[] creationOffsets = new int[ClassConstants.TYPICAL_CODE_LENGTH]; private int[] initializationOffsets = new int[ClassConstants.TYPICAL_CODE_LENGTH]; private int superInitializationOffset; + private boolean containsSubroutines; + private boolean repeat; private int currentSubroutineStart; - private int currentSubroutineEnd; private int[] recentCreationOffsets = new int[MAXIMUM_CREATION_OFFSETS]; private int recentCreationOffsetIndex; private boolean isInitializer; @@ -189,7 +195,7 @@ implements AttributeVisitor, */ public boolean isSubroutine(int offset) { - return subroutineStarts[offset] != NONE; + return subroutineStarts[offset] >= 0; } @@ -289,6 +295,16 @@ implements AttributeVisitor, } + /** + * Returns whether the method contains subroutines, in the CodeAttribute + * that was visited most recently. + */ + public boolean containsSubroutines() + { + return containsSubroutines; + } + + // Implementations for AttributeVisitor. public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} @@ -312,106 +328,98 @@ implements AttributeVisitor, initializationOffsets = new int[codeLength]; // Reset the arrays. - for (int index = 0; index < codeLength; index++) - { - subroutineStarts[index] = NONE; - subroutineEnds[index] = NONE; - creationOffsets[index] = NONE; - initializationOffsets[index] = NONE; - } + Arrays.fill(subroutineStarts, 0, codeLength, UNKNOWN); + Arrays.fill(subroutineEnds, 0, codeLength, UNKNOWN); + Arrays.fill(creationOffsets, 0, codeLength, NONE); + Arrays.fill(initializationOffsets, 0, codeLength, NONE); } else { // Reset the arrays. - for (int index = 0; index < codeLength; index++) - { - instructionMarks[index] = 0; - subroutineStarts[index] = NONE; - subroutineEnds[index] = NONE; - creationOffsets[index] = NONE; - initializationOffsets[index] = NONE; - } + Arrays.fill(instructionMarks, 0, codeLength, (short)0); + Arrays.fill(subroutineStarts, 0, codeLength, UNKNOWN); + Arrays.fill(subroutineEnds, 0, codeLength, UNKNOWN); + Arrays.fill(creationOffsets, 0, codeLength, NONE); + Arrays.fill(initializationOffsets, 0, codeLength, NONE); instructionMarks[codeLength] = 0; } superInitializationOffset = NONE; + containsSubroutines = false; - // We're assuming all subroutines are contiguous blocks of code. - // We're not starting in a subroutine. - currentSubroutineStart = NONE; - currentSubroutineEnd = NONE; + // Iterate until all subroutines have been fully marked. + do + { + repeat = false; + currentSubroutineStart = NO_SUBROUTINE; + recentCreationOffsetIndex = 0; - 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; + } - // 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); } + while (repeat); // The end of the code is a branch target sentinel. instructionMarks[codeLength] = BRANCH_TARGET; - // 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); - // Fill out any gaps in the subroutine starts and the subroutine ends - // and subroutine returning flags, working backward. - - // We're not starting in a subroutine. - int subroutineStart = NONE; - int subroutineEnd = codeLength; - boolean subroutineReturning = false; - - for (int index = codeLength - 1; index >= 0; index--) + if (containsSubroutines) { - if (isInstruction(index)) - { - // Are we inside a previously marked subroutine? - if (subroutineStarts[index] != NONE) - { - // Update the current subroutine start. - subroutineStart = subroutineStarts[index]; - } - else if (subroutineStart != NONE) - { - // Mark the subroutine start. - subroutineStarts[index] = subroutineStart; - } + // Set the subroutine returning flag and the subroutine end at each + // subroutine start. + int previousSubroutineStart = NO_SUBROUTINE; - // Did we reach the start of the subroutine. - if (isSubroutineStart(index)) - { - // Stop marking it. - subroutineStart = NONE; - } - - // Are we inside a subroutine? - if (isSubroutine(index)) + for (int offset = 0; offset < codeLength; offset++) + { + if (isInstruction(offset)) { - // Mark the subroutine end. - subroutineEnds[index] = subroutineEnd; + int subroutineStart = subroutineStarts[offset]; - // Update or mark the subroutine returning flag. - if (isSubroutineReturning(index)) + if (subroutineStart >= 0 && + isSubroutineReturning(offset)) { - subroutineReturning = true; + instructionMarks[subroutineStart] |= SUBROUTINE_RETURNING; } - else if (subroutineReturning) + + if (previousSubroutineStart >= 0) { - instructionMarks[index] |= SUBROUTINE_RETURNING; + subroutineEnds[previousSubroutineStart] = offset; } + + previousSubroutineStart = subroutineStart; } - else + } + + if (previousSubroutineStart >= 0) + { + subroutineEnds[previousSubroutineStart] = codeLength; + } + + // Set the subroutine returning flag and the subroutine end at each + // subroutine instruction, based on the marks at the subroutine + // start. + for (int offset = 0; offset < codeLength; offset++) + { + if (isSubroutine(offset)) { - // Update the subroutine end and returning flag. - subroutineEnd = index; - subroutineReturning = false; + int subroutineStart = subroutineStarts[offset]; + + if (isSubroutineReturning(subroutineStart)) + { + instructionMarks[offset] |= SUBROUTINE_RETURNING; + } + + subroutineEnds[offset] = subroutineEnds[subroutineStart]; } } } @@ -451,7 +459,7 @@ implements AttributeVisitor, // Mark the instruction. instructionMarks[offset] |= INSTRUCTION; - // Check if this is the first instruction of a subroutine. + // Check if this is an instruction of a subroutine. checkSubroutine(offset); byte opcode = simpleInstruction.opcode; @@ -476,7 +484,7 @@ implements AttributeVisitor, // Mark the instruction. instructionMarks[offset] |= INSTRUCTION; - // Check if this is the first instruction of a subroutine. + // Check if this is an instruction of a subroutine. checkSubroutine(offset); // Check if the instruction is a 'new' instruction. @@ -517,15 +525,18 @@ implements AttributeVisitor, // Mark the instruction. instructionMarks[offset] |= INSTRUCTION; - // Check if this is the first instruction of a subroutine. + // Check if this is an instruction of a subroutine. checkSubroutine(offset); if (variableInstruction.opcode == InstructionConstants.OP_RET) { + // Mark the method. + containsSubroutines = true; + // Mark the branch origin. markBranchOrigin(offset); - // Mark the regular subroutine return. + // Mark the subroutine return at its return instruction. instructionMarks[offset] |= SUBROUTINE_RETURNING; // Mark the next instruction. @@ -536,28 +547,39 @@ implements AttributeVisitor, public void visitBranchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, BranchInstruction branchInstruction) { + int branchOffset = branchInstruction.branchOffset; + int targetOffset = offset + branchOffset; + // Mark the branch origin. markBranchOrigin(offset); - // Check if this is the first instruction of a subroutine. + // Check if this is an instruction of a subroutine. checkSubroutine(offset); // Mark the branch target. - markBranchTarget(offset, branchInstruction.branchOffset); + markBranchTarget(offset, branchOffset); byte opcode = branchInstruction.opcode; if (opcode == InstructionConstants.OP_JSR || opcode == InstructionConstants.OP_JSR_W) { + // Mark the method. + containsSubroutines = true; + // Mark the subroutine invocation. instructionMarks[offset] |= SUBROUTINE_INVOCATION; - // Mark the subroutine start. - int targetOffset = offset + branchInstruction.branchOffset; - subroutineStarts[targetOffset] = targetOffset; + // Mark the new subroutine start. + markBranchSubroutineStart(offset, branchOffset, targetOffset); } - else if (opcode == InstructionConstants.OP_GOTO || - opcode == InstructionConstants.OP_GOTO_W) + else if (currentSubroutineStart != UNKNOWN) + { + // Mark the continued subroutine start. + markBranchSubroutineStart(offset, branchOffset, currentSubroutineStart); + } + + if (opcode == InstructionConstants.OP_GOTO || + opcode == InstructionConstants.OP_GOTO_W) { // Mark the next instruction. markAfterBranchOrigin(offset + branchInstruction.length(offset)); @@ -570,15 +592,14 @@ implements AttributeVisitor, // Mark the branch origin. markBranchOrigin(offset); - // Check if this is the first instruction of a subroutine. + // Check if this is an instruction of a subroutine. checkSubroutine(offset); // Mark the branch targets of the default jump offset. - markBranchTarget(offset, switchInstruction.defaultOffset); + markBranch(offset, switchInstruction.defaultOffset); // Mark the branch targets of the jump offsets. - markBranchTargets(offset, - switchInstruction.jumpOffsets); + markBranches(offset, switchInstruction.jumpOffsets); // Mark the next instruction. markAfterBranchOrigin(offset + switchInstruction.length(offset)); @@ -610,19 +631,32 @@ implements AttributeVisitor, // Small utility methods. /** - * Marks the branch targets of the given jump offsets for the instruction - * at the given offset. + * Marks the branch targets and their subroutine starts at the given + * offsets. */ - private void markBranchTargets(int offset, int[] jumpOffsets) + private void markBranches(int offset, int[] jumpOffsets) { for (int index = 0; index < jumpOffsets.length; index++) { - markBranchTarget(offset, jumpOffsets[index]); + markBranch(offset, jumpOffsets[index]); } } /** + * Marks the branch target and its subroutine start at the given offset. + */ + private void markBranch(int offset, int jumpOffset) + { + markBranchTarget(offset, jumpOffset); + + if (currentSubroutineStart != UNKNOWN) + { + markBranchSubroutineStart(offset, jumpOffset, currentSubroutineStart); + } + } + + /** * Marks the branch origin at the given offset. */ private void markBranchOrigin(int offset) @@ -639,18 +673,37 @@ implements AttributeVisitor, int targetOffset = offset + jumpOffset; instructionMarks[targetOffset] |= BRANCH_TARGET; + } - // Are we inside a previously marked subroutine? - if (isSubroutine(offset)) - { - // Mark the subroutine start of the target. - subroutineStarts[targetOffset] = currentSubroutineStart; - // Update the current subroutine end. - if (currentSubroutineEnd < targetOffset) + /** + * Marks the subroutine start at the given offset, if applicable. + */ + private void markBranchSubroutineStart(int offset, + int jumpOffset, + int subroutineStart) + { + int targetOffset = offset + jumpOffset; + + // Are we marking a subroutine and branching to an offset that hasn't + // been marked yet? + if (subroutineStarts[targetOffset] == UNKNOWN) + { + // Is it a backward branch? + if (jumpOffset < 0) { - currentSubroutineEnd = targetOffset; + // Remember the smallest subroutine start. + if (subroutineStart > targetOffset) + { + subroutineStart = targetOffset; + } + + // We'll have to go over all instructions again. + repeat = true; } + + // Mark the subroutine start of the target. + subroutineStarts[targetOffset] = subroutineStart; } } @@ -662,12 +715,8 @@ implements AttributeVisitor, { instructionMarks[nextOffset] |= AFTER_BRANCH; - // Are we at the end of the current subroutine? - if (currentSubroutineEnd <= nextOffset) - { - // Reset the subroutine start. - currentSubroutineStart = NONE; - } + // Stop marking a subroutine. + currentSubroutineStart = UNKNOWN; } @@ -677,15 +726,23 @@ implements AttributeVisitor, private void checkSubroutine(int offset) { // Are we inside a previously marked subroutine? - if (isSubroutine(offset)) + if (subroutineStarts[offset] != UNKNOWN) { - // Update the current subroutine start. + // Start marking a subroutine. currentSubroutineStart = subroutineStarts[offset]; } - else + + // Are we marking a subroutine? + else if (currentSubroutineStart != UNKNOWN) { - // Mark the subroutine start (or NONE). + // 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 b5e54f8..378f972 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-2009 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2013 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/ClassMerger.java b/src/proguard/optimize/peephole/ClassMerger.java index 1e1a950..aa40c75 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-2009 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2013 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,6 +21,7 @@ package proguard.optimize.peephole; import proguard.classfile.*; +import proguard.classfile.attribute.visitor.AttributeNameFilter; import proguard.classfile.constant.visitor.*; import proguard.classfile.editor.*; import proguard.classfile.util.*; @@ -50,7 +51,7 @@ implements ClassVisitor, //* private static final boolean DEBUG = false; /*/ - private static boolean DEBUG = true; + private static boolean DEBUG = System.getProperty("cm") != null; //*/ @@ -59,6 +60,8 @@ implements ClassVisitor, private final boolean mergeInterfacesAggressively; private final ClassVisitor extraClassVisitor; + private final MemberVisitor fieldOptimizationInfoCopier = new FieldOptimizationInfoCopier(); + /** * Creates a new ClassMerger that will merge classes into the given target @@ -151,7 +154,7 @@ implements ClassVisitor, // infinite recursion. (programClass.getAccessFlags() & ClassConstants.INTERNAL_ACC_ANNOTATTION) == 0 && - // Only merge classes if we can change the access permissioms, or + // Only merge classes if we can change the access permissions, or // if they are in the same package, or // if they are public and don't contain or invoke package visible // class members. @@ -196,12 +199,19 @@ implements ClassVisitor, !(DotClassMarker.isDotClassed(programClass) && DotClassMarker.isDotClassed(targetClass)) && + // The classes must not have clashing fields. + !haveAnyIdenticalFields(programClass, targetClass) && + // The two classes must not introduce any unwanted fields. !introducesUnwantedFields(programClass, targetClass) && !introducesUnwantedFields(targetClass, programClass) && - // The classes must not have clashing constructors. - !haveAnyIdenticalInitializers(programClass, targetClass) && + // The two classes must not shadow each others fields. + !shadowsAnyFields(programClass, targetClass) && + !shadowsAnyFields(targetClass, programClass) && + + // The classes must not have clashing methods. + !haveAnyIdenticalMethods(programClass, targetClass) && // The classes must not introduce abstract methods, unless // explicitly allowed. @@ -226,6 +236,10 @@ implements ClassVisitor, System.out.println(" Target subclasses ["+targetClass.subClasses+"]"); System.out.println(" Source superclass ["+programClass.getSuperClass().getName()+"]"); System.out.println(" Target superclass ["+targetClass.getSuperClass().getName()+"]"); + + //System.out.println("=== Before ==="); + //programClass.accept(new ClassPrinter()); + //targetClass.accept(new ClassPrinter()); } // Combine the access flags. @@ -235,11 +249,12 @@ implements ClassVisitor, targetClass.u2accessFlags = ((targetAccessFlags & sourceAccessFlags) & - (ClassConstants.INTERNAL_ACC_INTERFACE | + (ClassConstants.INTERNAL_ACC_INTERFACE | ClassConstants.INTERNAL_ACC_ABSTRACT)) | ((targetAccessFlags | sourceAccessFlags) & - (ClassConstants.INTERNAL_ACC_PUBLIC | + (ClassConstants.INTERNAL_ACC_PUBLIC | + ClassConstants.INTERNAL_ACC_SUPER | ClassConstants.INTERNAL_ACC_ANNOTATTION | ClassConstants.INTERNAL_ACC_ENUM)); @@ -260,14 +275,18 @@ implements ClassVisitor, // Copy over the class members. MemberAdder memberAdder = - new MemberAdder(targetClass); + new MemberAdder(targetClass, fieldOptimizationInfoCopier); programClass.fieldsAccept(memberAdder); programClass.methodsAccept(memberAdder); // Copy over the other attributes. programClass.attributesAccept( - new AttributeAdder(targetClass, true)); + new AttributeNameFilter(new NotMatcher(new OrMatcher(new OrMatcher( + new FixedStringMatcher(ClassConstants.ATTR_SourceFile), + new FixedStringMatcher(ClassConstants.ATTR_InnerClasses)), + new FixedStringMatcher(ClassConstants.ATTR_EnclosingMethod))), + new AttributeAdder(targetClass, true))); // Update the optimization information of the target class. ClassOptimizationInfo info = @@ -280,6 +299,12 @@ implements ClassVisitor, // Remember to replace the inlined class by the target class. setTargetClass(programClass, targetClass); + //if (DEBUG) + //{ + // System.out.println("=== After ===="); + // targetClass.accept(new ClassPrinter()); + //} + // Visit the merged class, if required. if (extraClassVisitor != null) { @@ -336,10 +361,8 @@ implements ClassVisitor, // Visit all superclasses and interfaces, collecting the ones that have // static initializers. clazz.hierarchyAccept(true, true, true, false, - new NamedMethodVisitor(ClassConstants.INTERNAL_METHOD_NAME_CLINIT, - ClassConstants.INTERNAL_METHOD_TYPE_INIT, - new MemberToClassVisitor( - new ClassCollector(set)))); + new StaticInitializerContainingClassFilter( + new ClassCollector(set))); return set; } @@ -368,9 +391,16 @@ 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)) + { + return Collections.EMPTY_SET; + } + + // Visit all superclasses, collecting the ones that are caught + // (plus java.lang.Object, in the current implementation). Set set = new HashSet(); - // Visit all superclasses, collecting the ones that are caught. clazz.hierarchyAccept(true, true, false, false, new CaughtClassFilter( new ClassCollector(set))); @@ -380,38 +410,82 @@ implements ClassVisitor, /** + * Returns whether the two given classes have class members with the same + * name and descriptor. + */ + private boolean haveAnyIdenticalFields(Clazz clazz, Clazz targetClass) + { + MemberCounter counter = new MemberCounter(); + + // Visit all fields, counting the with the same name and descriptor in + // the target class. + clazz.fieldsAccept(new SimilarMemberVisitor(targetClass, true, false, false, false, + counter)); + + return counter.getCount() > 0; + } + + + /** * Returns whether the given class would introduce any unwanted fields * in the target class. */ private boolean introducesUnwantedFields(ProgramClass programClass, ProgramClass targetClass) { - // The class must not have any fields, or it must not be instantiated, - // without any other subclasses. - return - programClass.u2fieldsCount != 0 && - (InstantiationClassMarker.isInstantiated(targetClass) || - (targetClass.subClasses != null && - !isOnlySubClass(programClass, targetClass))); + // It's ok if the target class is never instantiated, without any other + // subclasses except for maybe the source class. + if (!InstantiationClassMarker.isInstantiated(targetClass) && + (targetClass.subClasses == null || + isOnlySubClass(programClass, targetClass))) + { + return false; + } + + MemberCounter counter = new MemberCounter(); + + // Count all non-static fields in the the source class. + programClass.fieldsAccept(new MemberAccessFilter(0, ClassConstants.INTERNAL_ACC_STATIC, + counter)); + + return counter.getCount() > 0; + } + + + /** + * Returns whether the given class or its subclasses shadow any fields in + * the given target class. + */ + private boolean shadowsAnyFields(Clazz clazz, Clazz targetClass) + { + MemberCounter counter = new MemberCounter(); + + // Visit all fields, counting the ones that are shadowing non-private + // fields in the class hierarchy of the target class. + clazz.hierarchyAccept(true, false, false, true, + new AllFieldVisitor( + new SimilarMemberVisitor(targetClass, true, true, true, false, + new MemberAccessFilter(0, ClassConstants.INTERNAL_ACC_PRIVATE, + counter)))); + + return counter.getCount() > 0; } /** - * Returns whether the two given classes have initializers with the same - * descriptors. + * Returns whether the two given classes have class members with the same + * name and descriptor. */ - private boolean haveAnyIdenticalInitializers(Clazz clazz, Clazz targetClass) + private boolean haveAnyIdenticalMethods(Clazz clazz, Clazz targetClass) { MemberCounter counter = new MemberCounter(); - // TODO: Currently checking shared methods, not just initializers. - // TODO: Allow identical methods. - // Visit all methods, counting the ones that are also present in the - // target class. - clazz.methodsAccept(//new MemberNameFilter(new FixedStringMatcher(ClassConstants.INTERNAL_METHOD_NAME_INIT), + // 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, new SimilarMemberVisitor(targetClass, true, false, false, false, new MemberAccessFilter(0, ClassConstants.INTERNAL_ACC_ABSTRACT, - counter))); + counter)))); return counter.getCount() > 0; } @@ -538,4 +612,30 @@ implements ClassVisitor, targetClass = clazz; } } + + + /** + * This MemberVisitor copies field optimization info from copied fields. + */ + private static class FieldOptimizationInfoCopier + extends SimplifiedVisitor + implements MemberVisitor + { + public void visitProgramField(ProgramClass programClass, ProgramField programField) + { + // Copy the optimization info from the field that was just copied. + ProgramField copiedField = (ProgramField)programField.getVisitorInfo(); + Object info = copiedField.getVisitorInfo(); + + programField.setVisitorInfo(info instanceof FieldOptimizationInfo ? + new FieldOptimizationInfo((FieldOptimizationInfo)info) : + info); + } + + + public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) + { + // 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 4833275..3bfd98c 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-2009 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2013 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,7 +50,7 @@ implements AttributeVisitor, private final InstructionVisitor extraInstructionVisitor; private final BranchTargetFinder branchTargetFinder = new BranchTargetFinder(); - private final CodeAttributeEditor codeAttributeEditor = new CodeAttributeEditor(); + private final CodeAttributeEditor codeAttributeEditor = new CodeAttributeEditor(true, false); /** @@ -123,10 +123,7 @@ implements AttributeVisitor, int deleteOffset = offset - delta; if (branchTargetFinder.isInstruction(deleteOffset)) { - codeAttributeEditor.replaceInstruction( deleteOffset, (Instruction)null); - codeAttributeEditor.insertBeforeInstruction(deleteOffset, (Instruction)null); - codeAttributeEditor.insertAfterInstruction( deleteOffset, (Instruction)null); - + codeAttributeEditor.clearModifications(deleteOffset); codeAttributeEditor.deleteInstruction(deleteOffset); } } @@ -136,7 +133,7 @@ implements AttributeVisitor, if (newBranchOffset != branchInstruction.length(offset)) { Instruction newGotoInstruction = - new BranchInstruction(opcode, newBranchOffset); + new BranchInstruction(opcode, newBranchOffset).shrink(); codeAttributeEditor.replaceInstruction(offset, newGotoInstruction); } diff --git a/src/proguard/optimize/peephole/GotoGotoReplacer.java b/src/proguard/optimize/peephole/GotoGotoReplacer.java index 7d7e66c..4a490a1 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-2009 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2013 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 @@ -84,8 +84,9 @@ implements InstructionVisitor int branchOffset = branchInstruction.branchOffset; int targetOffset = offset + branchOffset; - if (branchOffset != branchInstruction.length(offset) && - !codeAttributeEditor.isModified(offset) && + if (branchOffset != 0 && + branchOffset != branchInstruction.length(offset) && + !codeAttributeEditor.isModified(offset) && !codeAttributeEditor.isModified(targetOffset)) { Instruction targetInstruction = diff --git a/src/proguard/optimize/peephole/GotoReturnReplacer.java b/src/proguard/optimize/peephole/GotoReturnReplacer.java index 5c3eb77..b6deec8 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-2009 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2013 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 a37b9a5..31d3d33 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-2009 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2013 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/InstructionSequenceConstants.java b/src/proguard/optimize/peephole/InstructionSequenceConstants.java index b33204b..4ab9056 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-2009 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2013 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,81 +20,207 @@ */ package proguard.optimize.peephole; +import proguard.classfile.*; import proguard.classfile.constant.*; import proguard.classfile.instruction.*; -import proguard.classfile.util.InstructionSequenceMatcher; +import proguard.classfile.visitor.ClassPrinter; /** * This class contains a set of instruction sequences and their suggested * replacements. * * @see InstructionSequencesReplacer + * @see InstructionSequenceReplacer * @author Eric Lafortune */ public class InstructionSequenceConstants { - public static final int X = InstructionSequenceMatcher.X; - public static final int Y = InstructionSequenceMatcher.Y; - public static final int Z = InstructionSequenceMatcher.Z; + private static final int X = InstructionSequenceReplacer.X; + private static final int Y = InstructionSequenceReplacer.Y; + private static final int Z = InstructionSequenceReplacer.Z; - public static final int A = InstructionSequenceMatcher.A; - public static final int B = InstructionSequenceMatcher.B; - public static final int C = InstructionSequenceMatcher.C; - public static final int D = InstructionSequenceMatcher.D; + private static final int A = InstructionSequenceReplacer.A; + private static final int B = InstructionSequenceReplacer.B; + private static final int C = InstructionSequenceReplacer.C; + private static final int D = InstructionSequenceReplacer.D; + private static final int STRING_A_LENGTH = InstructionSequenceReplacer.STRING_A_LENGTH; + private static final int BOOLEAN_A_STRING = InstructionSequenceReplacer.BOOLEAN_A_STRING; + private static final int CHAR_A_STRING = InstructionSequenceReplacer.CHAR_A_STRING; + private static final int INT_A_STRING = InstructionSequenceReplacer.INT_A_STRING; + private static final int LONG_A_STRING = InstructionSequenceReplacer.LONG_A_STRING; + private static final int FLOAT_A_STRING = InstructionSequenceReplacer.FLOAT_A_STRING; + private static final int DOUBLE_A_STRING = InstructionSequenceReplacer.DOUBLE_A_STRING; + private static final int STRING_A_STRING = InstructionSequenceReplacer.STRING_A_STRING; + private static final int BOOLEAN_B_STRING = InstructionSequenceReplacer.BOOLEAN_B_STRING; + private static final int CHAR_B_STRING = InstructionSequenceReplacer.CHAR_B_STRING; + private static final int INT_B_STRING = InstructionSequenceReplacer.INT_B_STRING; + private static final int LONG_B_STRING = InstructionSequenceReplacer.LONG_B_STRING; + private static final int FLOAT_B_STRING = InstructionSequenceReplacer.FLOAT_B_STRING; + private static final int DOUBLE_B_STRING = InstructionSequenceReplacer.DOUBLE_B_STRING; + private static final int STRING_B_STRING = InstructionSequenceReplacer.STRING_B_STRING; - private static final int I_32768 = 0; - private static final int I_65536 = 1; - private static final int I_16777216 = 2; + private static final int I_32768 = 0; + private static final int I_65536 = 1; + private static final int I_16777216 = 2; // private static final int I_0x000000ff - private static final int I_0x0000ff00 = 3; - private static final int I_0x00ff0000 = 4; - private static final int I_0xff000000 = 5; - private static final int I_0x0000ffff = 6; - private static final int I_0xffff0000 = 7; + private static final int I_0x0000ff00 = 3; + private static final int I_0x00ff0000 = 4; + private static final int I_0xff000000 = 5; + private static final int I_0x0000ffff = 6; + private static final int I_0xffff0000 = 7; - private static final int L_M1 = 8; - private static final int L_2 = 9; - private static final int L_4 = 10; - private static final int L_8 = 11; - private static final int L_16 = 12; - private static final int L_32 = 13; - private static final int L_64 = 14; - private static final int L_128 = 15; - private static final int L_256 = 16; - private static final int L_512 = 17; - private static final int L_1024 = 18; - private static final int L_2048 = 19; - private static final int L_4096 = 20; - private static final int L_8192 = 21; - private static final int L_16384 = 22; - private static final int L_32768 = 23; - private static final int L_65536 = 24; - private static final int L_16777216 = 25; - private static final int L_4294967296 = 26; + private static final int L_M1 = 8; + private static final int L_2 = 9; + private static final int L_4 = 10; + private static final int L_8 = 11; + private static final int L_16 = 12; + private static final int L_32 = 13; + private static final int L_64 = 14; + private static final int L_128 = 15; + private static final int L_256 = 16; + private static final int L_512 = 17; + private static final int L_1024 = 18; + private static final int L_2048 = 19; + private static final int L_4096 = 20; + private static final int L_8192 = 21; + private static final int L_16384 = 22; + private static final int L_32768 = 23; + private static final int L_65536 = 24; + private static final int L_16777216 = 25; + private static final int L_4294967296 = 26; - private static final int L_0x00000000ffffffff = 27; - private static final int L_0xffffffff00000000 = 28; + private static final int L_0x00000000ffffffff = 27; + private static final int L_0xffffffff00000000 = 28; - private static final int F_M1 = 29; + private static final int F_M1 = 29; - private static final int D_M1 = 30; + private static final int D_M1 = 30; - private static final int FIELD_I = 31; - private static final int FIELD_L = 32; - private static final int FIELD_F = 33; - private static final int FIELD_D = 34; + private static final int STRING_EMPTY = 31; - private static final int NAME_AND_TYPE_I = 35; - private static final int NAME_AND_TYPE_L = 36; - private static final int NAME_AND_TYPE_F = 37; - private static final int NAME_AND_TYPE_D = 38; + private static final int FIELD_I = 32; // Implicitly uses X and Y. + private static final int FIELD_L = 33; // Implicitly uses X and Y. + private static final int FIELD_F = 34; // Implicitly uses X and Y. + private static final int FIELD_D = 35; // Implicitly uses X and Y. - private static final int TYPE_I = 39; - private static final int TYPE_L = 40; - private static final int TYPE_F = 41; - private static final int TYPE_D = 42; + private static final int METHOD_STRING_EQUALS = 36; + private static final int METHOD_STRING_LENGTH = 37; + private static final int METHOD_STRING_VALUEOF_Z = 38; + private static final int METHOD_STRING_VALUEOF_C = 39; + private static final int METHOD_STRING_VALUEOF_I = 40; + private static final int METHOD_STRING_VALUEOF_J = 41; + private static final int METHOD_STRING_VALUEOF_F = 42; + private static final int METHOD_STRING_VALUEOF_D = 43; + private static final int METHOD_STRING_VALUEOF_OBJECT = 44; + private static final int METHOD_STRINGBUFFER_INIT = 45; + private static final int METHOD_STRINGBUFFER_INIT_STRING = 46; + private static final int METHOD_STRINGBUFFER_APPEND_Z = 47; + private static final int METHOD_STRINGBUFFER_APPEND_C = 48; + private static final int METHOD_STRINGBUFFER_APPEND_I = 49; + private static final int METHOD_STRINGBUFFER_APPEND_J = 50; + private static final int METHOD_STRINGBUFFER_APPEND_F = 51; + private static final int METHOD_STRINGBUFFER_APPEND_D = 52; + private static final int METHOD_STRINGBUFFER_APPEND_STRING = 53; + private static final int METHOD_STRINGBUFFER_APPEND_OBJECT = 54; + private static final int METHOD_STRINGBUFFER_LENGTH = 55; + private static final int METHOD_STRINGBUFFER_TOSTRING = 56; + private static final int METHOD_STRINGBUILDER_INIT = 57; + private static final int METHOD_STRINGBUILDER_INIT_STRING = 58; + private static final int METHOD_STRINGBUILDER_APPEND_Z = 59; + private static final int METHOD_STRINGBUILDER_APPEND_C = 60; + private static final int METHOD_STRINGBUILDER_APPEND_I = 61; + private static final int METHOD_STRINGBUILDER_APPEND_J = 62; + private static final int METHOD_STRINGBUILDER_APPEND_F = 63; + private static final int METHOD_STRINGBUILDER_APPEND_D = 64; + private static final int METHOD_STRINGBUILDER_APPEND_STRING = 65; + private static final int METHOD_STRINGBUILDER_APPEND_OBJECT = 66; + private static final int METHOD_STRINGBUILDER_LENGTH = 67; + private static final int METHOD_STRINGBUILDER_TOSTRING = 68; + + private static final int CLASS_STRING = 69; + private static final int CLASS_STRINGBUFFER = 70; + private static final int CLASS_STRINGBUILDER = 71; + + private static final int NAME_AND_TYPE_I = 72; // Implicitly uses Y. + private static final int NAME_AND_TYPE_L = 73; // Implicitly uses Y. + private static final int NAME_AND_TYPE_F = 74; // Implicitly uses Y. + private static final int NAME_AND_TYPE_D = 75; // Implicitly uses Y. + + private static final int NAME_AND_TYPE_EQUALS = 76; + private static final int NAME_AND_TYPE_LENGTH = 77; + private static final int NAME_AND_TYPE_VALUEOF_Z = 78; + private static final int NAME_AND_TYPE_VALUEOF_C = 79; + private static final int NAME_AND_TYPE_VALUEOF_I = 80; + private static final int NAME_AND_TYPE_VALUEOF_J = 81; + private static final int NAME_AND_TYPE_VALUEOF_F = 82; + private static final int NAME_AND_TYPE_VALUEOF_D = 83; + private static final int NAME_AND_TYPE_VALUEOF_OBJECT = 84; + private static final int NAME_AND_TYPE_INIT = 85; + private static final int NAME_AND_TYPE_INIT_STRING = 86; + private static final int NAME_AND_TYPE_APPEND_Z_STRINGBUFFER = 87; + private static final int NAME_AND_TYPE_APPEND_C_STRINGBUFFER = 88; + private static final int NAME_AND_TYPE_APPEND_I_STRINGBUFFER = 89; + private static final int NAME_AND_TYPE_APPEND_J_STRINGBUFFER = 90; + private static final int NAME_AND_TYPE_APPEND_F_STRINGBUFFER = 91; + private static final int NAME_AND_TYPE_APPEND_D_STRINGBUFFER = 92; + private static final int NAME_AND_TYPE_APPEND_STRING_STRINGBUFFER = 93; + private static final int NAME_AND_TYPE_APPEND_OBJECT_STRINGBUFFER = 94; + private static final int NAME_AND_TYPE_APPEND_Z_STRINGBUILDER = 95; + private static final int NAME_AND_TYPE_APPEND_C_STRINGBUILDER = 96; + private static final int NAME_AND_TYPE_APPEND_I_STRINGBUILDER = 97; + private static final int NAME_AND_TYPE_APPEND_J_STRINGBUILDER = 98; + private static final int NAME_AND_TYPE_APPEND_F_STRINGBUILDER = 99; + private static final int NAME_AND_TYPE_APPEND_D_STRINGBUILDER = 100; + private static final int NAME_AND_TYPE_APPEND_STRING_STRINGBUILDER = 101; + private static final int NAME_AND_TYPE_APPEND_OBJECT_STRINGBUILDER = 102; + private static final int NAME_AND_TYPE_TOSTRING = 103; + + private static final int UTF8_EMPTY = 104; + private static final int UTF8_I = 105; + private static final int UTF8_L = 106; + private static final int UTF8_F = 107; + private static final int UTF8_D = 108; + private static final int UTF8_STRING = 109; + private static final int UTF8_STRINGBUFFER = 110; + private static final int UTF8_STRINGBUILDER = 111; + private static final int UTF8_EQUALS = 112; + private static final int UTF8_OBJECT_Z = 113; + private static final int UTF8_LENGTH = 114; + private static final int UTF8__I = 115; + private static final int UTF8_VALUEOF = 116; + private static final int UTF8_Z_STRING = 117; + private static final int UTF8_C_STRING = 118; + private static final int UTF8_I_STRING = 119; + private static final int UTF8_J_STRING = 120; + private static final int UTF8_F_STRING = 121; + private static final int UTF8_D_STRING = 122; + private static final int UTF8_OBJECT_STRING = 123; + private static final int UTF8_INIT = 124; + private static final int UTF8__VOID = 125; + private static final int UTF8_STRING_VOID = 126; + private static final int UTF8_TOSTRING = 127; + private static final int UTF8__STRING = 128; + private static final int UTF8_APPEND = 129; + private static final int UTF8_Z_STRINGBUFFER = 130; + private static final int UTF8_C_STRINGBUFFER = 131; + private static final int UTF8_I_STRINGBUFFER = 132; + private static final int UTF8_J_STRINGBUFFER = 133; + private static final int UTF8_F_STRINGBUFFER = 134; + private static final int UTF8_D_STRINGBUFFER = 135; + private static final int UTF8_STRING_STRINGBUFFER = 136; + private static final int UTF8_OBJECT_STRINGBUFFER = 137; + private static final int UTF8_Z_STRINGBUILDER = 138; + private static final int UTF8_C_STRINGBUILDER = 139; + private static final int UTF8_I_STRINGBUILDER = 140; + private static final int UTF8_J_STRINGBUILDER = 141; + private static final int UTF8_F_STRINGBUILDER = 142; + private static final int UTF8_D_STRINGBUILDER = 143; + private static final int UTF8_STRING_STRINGBUILDER = 144; + private static final int UTF8_OBJECT_STRINGBUILDER = 145; + + private static final int SENTINEL = 146; public static final Constant[] CONSTANTS = new Constant[] @@ -136,23 +262,128 @@ public class InstructionSequenceConstants new DoubleConstant(-1d), + new StringConstant(UTF8_EMPTY, null, null), + new FieldrefConstant(X, NAME_AND_TYPE_I, null, null), new FieldrefConstant(X, NAME_AND_TYPE_L, null, null), new FieldrefConstant(X, NAME_AND_TYPE_F, null, null), new FieldrefConstant(X, NAME_AND_TYPE_D, null, null), - new NameAndTypeConstant(Y, TYPE_I), - new NameAndTypeConstant(Y, TYPE_L), - new NameAndTypeConstant(Y, TYPE_F), - new NameAndTypeConstant(Y, TYPE_D), + new MethodrefConstant(CLASS_STRING, NAME_AND_TYPE_EQUALS, null, null), + new MethodrefConstant(CLASS_STRING, NAME_AND_TYPE_LENGTH, null, null), + new MethodrefConstant(CLASS_STRING, NAME_AND_TYPE_VALUEOF_Z, null, null), + new MethodrefConstant(CLASS_STRING, NAME_AND_TYPE_VALUEOF_C, null, null), + new MethodrefConstant(CLASS_STRING, NAME_AND_TYPE_VALUEOF_I, null, null), + new MethodrefConstant(CLASS_STRING, NAME_AND_TYPE_VALUEOF_J, null, null), + new MethodrefConstant(CLASS_STRING, NAME_AND_TYPE_VALUEOF_F, null, null), + new MethodrefConstant(CLASS_STRING, NAME_AND_TYPE_VALUEOF_D, null, null), + new MethodrefConstant(CLASS_STRING, NAME_AND_TYPE_VALUEOF_OBJECT, null, null), + new MethodrefConstant(CLASS_STRINGBUFFER, NAME_AND_TYPE_INIT, null, null), + new MethodrefConstant(CLASS_STRINGBUFFER, NAME_AND_TYPE_INIT_STRING, null, null), + new MethodrefConstant(CLASS_STRINGBUFFER, NAME_AND_TYPE_APPEND_Z_STRINGBUFFER, null, null), + new MethodrefConstant(CLASS_STRINGBUFFER, NAME_AND_TYPE_APPEND_C_STRINGBUFFER, null, null), + new MethodrefConstant(CLASS_STRINGBUFFER, NAME_AND_TYPE_APPEND_I_STRINGBUFFER, null, null), + new MethodrefConstant(CLASS_STRINGBUFFER, NAME_AND_TYPE_APPEND_J_STRINGBUFFER, null, null), + new MethodrefConstant(CLASS_STRINGBUFFER, NAME_AND_TYPE_APPEND_F_STRINGBUFFER, null, null), + new MethodrefConstant(CLASS_STRINGBUFFER, NAME_AND_TYPE_APPEND_D_STRINGBUFFER, null, null), + new MethodrefConstant(CLASS_STRINGBUFFER, NAME_AND_TYPE_APPEND_STRING_STRINGBUFFER, null, null), + new MethodrefConstant(CLASS_STRINGBUFFER, NAME_AND_TYPE_APPEND_OBJECT_STRINGBUFFER, null, null), + new MethodrefConstant(CLASS_STRINGBUFFER, NAME_AND_TYPE_LENGTH, null, null), + new MethodrefConstant(CLASS_STRINGBUFFER, NAME_AND_TYPE_TOSTRING, null, null), + new MethodrefConstant(CLASS_STRINGBUILDER, NAME_AND_TYPE_INIT, null, null), + new MethodrefConstant(CLASS_STRINGBUILDER, NAME_AND_TYPE_INIT_STRING, null, null), + new MethodrefConstant(CLASS_STRINGBUILDER, NAME_AND_TYPE_APPEND_Z_STRINGBUILDER, null, null), + new MethodrefConstant(CLASS_STRINGBUILDER, NAME_AND_TYPE_APPEND_C_STRINGBUILDER, null, null), + new MethodrefConstant(CLASS_STRINGBUILDER, NAME_AND_TYPE_APPEND_I_STRINGBUILDER, null, null), + new MethodrefConstant(CLASS_STRINGBUILDER, NAME_AND_TYPE_APPEND_J_STRINGBUILDER, null, null), + new MethodrefConstant(CLASS_STRINGBUILDER, NAME_AND_TYPE_APPEND_F_STRINGBUILDER, null, null), + new MethodrefConstant(CLASS_STRINGBUILDER, NAME_AND_TYPE_APPEND_D_STRINGBUILDER, null, null), + new MethodrefConstant(CLASS_STRINGBUILDER, NAME_AND_TYPE_APPEND_STRING_STRINGBUILDER, null, null), + new MethodrefConstant(CLASS_STRINGBUILDER, NAME_AND_TYPE_APPEND_OBJECT_STRINGBUILDER, null, null), + new MethodrefConstant(CLASS_STRINGBUILDER, NAME_AND_TYPE_LENGTH, null, null), + new MethodrefConstant(CLASS_STRINGBUILDER, NAME_AND_TYPE_TOSTRING, null, null), + + new ClassConstant(UTF8_STRING, null), + new ClassConstant(UTF8_STRINGBUFFER, null), + new ClassConstant(UTF8_STRINGBUILDER, null), + new NameAndTypeConstant(Y, UTF8_I), + new NameAndTypeConstant(Y, UTF8_L), + new NameAndTypeConstant(Y, UTF8_F), + new NameAndTypeConstant(Y, UTF8_D), + new NameAndTypeConstant(UTF8_EQUALS, UTF8_OBJECT_Z), + new NameAndTypeConstant(UTF8_LENGTH, UTF8__I), + new NameAndTypeConstant(UTF8_VALUEOF, UTF8_Z_STRING), + new NameAndTypeConstant(UTF8_VALUEOF, UTF8_C_STRING), + new NameAndTypeConstant(UTF8_VALUEOF, UTF8_I_STRING), + new NameAndTypeConstant(UTF8_VALUEOF, UTF8_J_STRING), + new NameAndTypeConstant(UTF8_VALUEOF, UTF8_F_STRING), + new NameAndTypeConstant(UTF8_VALUEOF, UTF8_D_STRING), + new NameAndTypeConstant(UTF8_VALUEOF, UTF8_OBJECT_STRING), + new NameAndTypeConstant(UTF8_INIT, UTF8__VOID), + new NameAndTypeConstant(UTF8_INIT, UTF8_STRING_VOID), + new NameAndTypeConstant(UTF8_APPEND, UTF8_Z_STRINGBUFFER), + new NameAndTypeConstant(UTF8_APPEND, UTF8_C_STRINGBUFFER), + new NameAndTypeConstant(UTF8_APPEND, UTF8_I_STRINGBUFFER), + new NameAndTypeConstant(UTF8_APPEND, UTF8_J_STRINGBUFFER), + new NameAndTypeConstant(UTF8_APPEND, UTF8_F_STRINGBUFFER), + new NameAndTypeConstant(UTF8_APPEND, UTF8_D_STRINGBUFFER), + new NameAndTypeConstant(UTF8_APPEND, UTF8_STRING_STRINGBUFFER), + new NameAndTypeConstant(UTF8_APPEND, UTF8_OBJECT_STRINGBUFFER), + new NameAndTypeConstant(UTF8_APPEND, UTF8_Z_STRINGBUILDER), + new NameAndTypeConstant(UTF8_APPEND, UTF8_C_STRINGBUILDER), + new NameAndTypeConstant(UTF8_APPEND, UTF8_I_STRINGBUILDER), + new NameAndTypeConstant(UTF8_APPEND, UTF8_J_STRINGBUILDER), + new NameAndTypeConstant(UTF8_APPEND, UTF8_F_STRINGBUILDER), + new NameAndTypeConstant(UTF8_APPEND, UTF8_D_STRINGBUILDER), + new NameAndTypeConstant(UTF8_APPEND, UTF8_STRING_STRINGBUILDER), + new NameAndTypeConstant(UTF8_APPEND, UTF8_OBJECT_STRINGBUILDER), + new NameAndTypeConstant(UTF8_TOSTRING, UTF8__STRING), + + new Utf8Constant(""), new Utf8Constant("I"), 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), }; - public static final Instruction[][][] VARIABLE = new Instruction[][][] { { // nop = nothing @@ -237,7 +468,7 @@ public class InstructionSequenceConstants { // a = a = nothing { new VariableInstruction(InstructionConstants.OP_ALOAD, X), - new SimpleInstruction(InstructionConstants.OP_POP), + new VariableInstruction(InstructionConstants.OP_ASTORE, X), },{ // Nothing. }, @@ -382,12 +613,12 @@ public class InstructionSequenceConstants }, { // c * i = i * c { - new SimpleInstruction(InstructionConstants.OP_ICONST_0, A), + new SimpleInstruction(InstructionConstants.OP_SIPUSH, A), new VariableInstruction(InstructionConstants.OP_ILOAD, X), new SimpleInstruction(InstructionConstants.OP_IMUL), },{ new VariableInstruction(InstructionConstants.OP_ILOAD, X), - new SimpleInstruction(InstructionConstants.OP_ICONST_0, A), + new SimpleInstruction(InstructionConstants.OP_SIPUSH, A), new SimpleInstruction(InstructionConstants.OP_IMUL), }, }, @@ -548,7 +779,7 @@ public class InstructionSequenceConstants { // i = i + c = i += c { new VariableInstruction(InstructionConstants.OP_ILOAD, X), - new SimpleInstruction(InstructionConstants.OP_ICONST_0, A), + new SimpleInstruction(InstructionConstants.OP_SIPUSH, A), new SimpleInstruction(InstructionConstants.OP_IADD), new VariableInstruction(InstructionConstants.OP_ISTORE, X), },{ @@ -651,22 +882,23 @@ public class InstructionSequenceConstants // Nothing. }, }, - { // ... + 0f = ... - { - new SimpleInstruction(InstructionConstants.OP_FCONST_0), - new SimpleInstruction(InstructionConstants.OP_FADD), - },{ - // Nothing. - }, - }, - { // ... + 0d = ... - { - new SimpleInstruction(InstructionConstants.OP_DCONST_0), - new SimpleInstruction(InstructionConstants.OP_DADD), - },{ - // Nothing. - }, - }, + // Not valid for -0.0. +// { // ... + 0f = ... +// { +// new SimpleInstruction(InstructionConstants.OP_FCONST_0), +// new SimpleInstruction(InstructionConstants.OP_FADD), +// },{ +// // Nothing. +// }, +// }, +// { // ... + 0d = ... +// { +// new SimpleInstruction(InstructionConstants.OP_DCONST_0), +// new SimpleInstruction(InstructionConstants.OP_DADD), +// },{ +// // Nothing. +// }, +// }, { // ... - 0 = ... { new SimpleInstruction(InstructionConstants.OP_ICONST_0), @@ -1072,7 +1304,8 @@ public class InstructionSequenceConstants new SimpleInstruction(InstructionConstants.OP_FNEG), }, }, -// { // ... * 0f = 0f (or NaN) + // Not valid for -0.0 and for NaN. +// { // ... * 0f = 0f // { // new SimpleInstruction(InstructionConstants.OP_FCONST_0), // new SimpleInstruction(InstructionConstants.OP_FMUL), @@ -1097,7 +1330,8 @@ public class InstructionSequenceConstants new SimpleInstruction(InstructionConstants.OP_DNEG), }, }, -// { // ... * 0d = 0d (or NaN) + // Not valid for -0.0 and for NaN. +// { // ... * 0d = 0d // { // new SimpleInstruction(InstructionConstants.OP_DCONST_0), // new SimpleInstruction(InstructionConstants.OP_DMUL), @@ -1504,6 +1738,7 @@ public class InstructionSequenceConstants new SimpleInstruction(InstructionConstants.OP_ICONST_0), }, }, + // Not valid for negative values. // { // ... % 2 = ... & 0x1 // { // new SimpleInstruction(InstructionConstants.OP_ICONST_2), @@ -3348,4 +3583,1508 @@ public class InstructionSequenceConstants // }, // } }; -}
\ No newline at end of file + + public static final Instruction[][][] STRING = new Instruction[][][] + { + { // "...".equals("...") = true + { + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRING_EQUALS), + },{ + new SimpleInstruction(InstructionConstants.OP_ICONST_1), + }, + }, + { // "...".length() = ... + { + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRING_LENGTH), + },{ + new SimpleInstruction(InstructionConstants.OP_SIPUSH, STRING_A_LENGTH), + }, + }, + { // String.valueOf(Z) = ".... + { + new SimpleInstruction(InstructionConstants.OP_ICONST_0, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESTATIC, METHOD_STRING_VALUEOF_Z), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, BOOLEAN_A_STRING), + }, + }, + { // String.valueOf(C) = "...." + { + new SimpleInstruction(InstructionConstants.OP_ICONST_0, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESTATIC, METHOD_STRING_VALUEOF_C), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, CHAR_A_STRING), + }, + }, + { // String.valueOf(Cc) = "...." + { + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESTATIC, METHOD_STRING_VALUEOF_C), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, CHAR_A_STRING), + }, + }, + { // String.valueOf(I) = "...." + { + new SimpleInstruction(InstructionConstants.OP_ICONST_0, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESTATIC, METHOD_STRING_VALUEOF_I), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, INT_A_STRING), + }, + }, + { // String.valueOf(Ic) = "...." + { + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESTATIC, METHOD_STRING_VALUEOF_I), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, INT_A_STRING), + }, + }, + { // String.valueOf(J) = "...." + { + new SimpleInstruction(InstructionConstants.OP_LCONST_0, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESTATIC, METHOD_STRING_VALUEOF_J), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, LONG_A_STRING), + }, + }, + { // String.valueOf(Jc) = "...." + { + new ConstantInstruction(InstructionConstants.OP_LDC2_W, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESTATIC, METHOD_STRING_VALUEOF_J), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, LONG_A_STRING), + }, + }, + { // String.valueOf(F) = "...." + { + new SimpleInstruction(InstructionConstants.OP_FCONST_0, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESTATIC, METHOD_STRING_VALUEOF_F), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, FLOAT_A_STRING), + }, + }, + { // String.valueOf(Fc) = "...." + { + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESTATIC, METHOD_STRING_VALUEOF_F), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, FLOAT_A_STRING), + }, + }, + { // String.valueOf(D) = "...." + { + new SimpleInstruction(InstructionConstants.OP_DCONST_0, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESTATIC, METHOD_STRING_VALUEOF_D), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, DOUBLE_A_STRING), + }, + }, + { // String.valueOf(Dc) = "...." + { + new ConstantInstruction(InstructionConstants.OP_LDC2_W, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESTATIC, METHOD_STRING_VALUEOF_D), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, DOUBLE_A_STRING), + }, + }, + + { // new StringBuffer("...").toString() = "..." (ignoring identity) + { + new ConstantInstruction(InstructionConstants.OP_NEW, CLASS_STRINGBUFFER), + new SimpleInstruction(InstructionConstants.OP_DUP), + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_TOSTRING), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, A), + }, + }, + { // new StringBuffer(string).toString() = string (ignoring identity) + { + new ConstantInstruction(InstructionConstants.OP_NEW, CLASS_STRINGBUFFER), + new SimpleInstruction(InstructionConstants.OP_DUP), + new VariableInstruction(InstructionConstants.OP_ALOAD, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_TOSTRING), + },{ + new VariableInstruction(InstructionConstants.OP_ALOAD, A), + }, + }, + { // new StringBuffer("...").length() = length + { + new ConstantInstruction(InstructionConstants.OP_NEW, CLASS_STRINGBUFFER), + new SimpleInstruction(InstructionConstants.OP_DUP), + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_LENGTH), + },{ + new SimpleInstruction(InstructionConstants.OP_SIPUSH, STRING_A_LENGTH), + }, + }, + { // new StringBuffer() (without dup) = nothing + { + new ConstantInstruction(InstructionConstants.OP_NEW, CLASS_STRINGBUFFER), + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT), + },{ + // Nothing. + }, + }, + { // new StringBuffer("...") (without dup) = nothing + { + new ConstantInstruction(InstructionConstants.OP_NEW, CLASS_STRINGBUFFER), + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT_STRING), + },{ + // Nothing. + }, + }, + { // new StringBuffer()/pop = nothing + { + new ConstantInstruction(InstructionConstants.OP_NEW, CLASS_STRINGBUFFER), + new SimpleInstruction(InstructionConstants.OP_DUP), + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT), + new SimpleInstruction(InstructionConstants.OP_POP), + },{ + // Nothing. + }, + }, + { // new StringBuffer("...")/pop = nothing + { + new ConstantInstruction(InstructionConstants.OP_NEW, CLASS_STRINGBUFFER), + new SimpleInstruction(InstructionConstants.OP_DUP), + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT_STRING), + new SimpleInstruction(InstructionConstants.OP_POP), + },{ + // Nothing. + }, + }, + { // new StringBuffer("...").append(z)/pop = nothing + { + new ConstantInstruction(InstructionConstants.OP_NEW, CLASS_STRINGBUFFER), + new SimpleInstruction(InstructionConstants.OP_DUP), + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT_STRING), + new VariableInstruction(InstructionConstants.OP_ILOAD, B), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_Z), + new SimpleInstruction(InstructionConstants.OP_POP), + },{ + // Nothing. + }, + }, + { // new StringBuffer("...").append(c)/pop = nothing + { + new ConstantInstruction(InstructionConstants.OP_NEW, CLASS_STRINGBUFFER), + new SimpleInstruction(InstructionConstants.OP_DUP), + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT_STRING), + new VariableInstruction(InstructionConstants.OP_ILOAD, B), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_C), + new SimpleInstruction(InstructionConstants.OP_POP), + },{ + // Nothing. + }, + }, + { // new StringBuffer("...").append(i)/pop = nothing + { + new ConstantInstruction(InstructionConstants.OP_NEW, CLASS_STRINGBUFFER), + new SimpleInstruction(InstructionConstants.OP_DUP), + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT_STRING), + new VariableInstruction(InstructionConstants.OP_ILOAD, B), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_I), + new SimpleInstruction(InstructionConstants.OP_POP), + },{ + // Nothing. + }, + }, + { // new StringBuffer("...").append(l)/pop = nothing + { + new ConstantInstruction(InstructionConstants.OP_NEW, CLASS_STRINGBUFFER), + new SimpleInstruction(InstructionConstants.OP_DUP), + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT_STRING), + new VariableInstruction(InstructionConstants.OP_LLOAD, B), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_J), + new SimpleInstruction(InstructionConstants.OP_POP), + },{ + // Nothing. + }, + }, + { // new StringBuffer("...").append(f)/pop = nothing + { + new ConstantInstruction(InstructionConstants.OP_NEW, CLASS_STRINGBUFFER), + new SimpleInstruction(InstructionConstants.OP_DUP), + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT_STRING), + new VariableInstruction(InstructionConstants.OP_FLOAD, B), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_F), + new SimpleInstruction(InstructionConstants.OP_POP), + },{ + // Nothing. + }, + }, + { // new StringBuffer("...").append(d)/pop = nothing + { + new ConstantInstruction(InstructionConstants.OP_NEW, CLASS_STRINGBUFFER), + new SimpleInstruction(InstructionConstants.OP_DUP), + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT_STRING), + new VariableInstruction(InstructionConstants.OP_DLOAD, B), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_D), + new SimpleInstruction(InstructionConstants.OP_POP), + },{ + // Nothing. + }, + }, + { // new StringBuffer("...").append(s)/pop = nothing + { + new ConstantInstruction(InstructionConstants.OP_NEW, CLASS_STRINGBUFFER), + new SimpleInstruction(InstructionConstants.OP_DUP), + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT_STRING), + new VariableInstruction(InstructionConstants.OP_ALOAD, B), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_STRING), + new SimpleInstruction(InstructionConstants.OP_POP), + },{ + // Nothing. + }, + }, + { // StringBuffer#toString()/pop = pop + { + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_TOSTRING), + new SimpleInstruction(InstructionConstants.OP_POP), + },{ + new SimpleInstruction(InstructionConstants.OP_POP), + }, + }, + { // StringBuffer#append("") = nothing + { + new ConstantInstruction(InstructionConstants.OP_LDC, STRING_EMPTY), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_STRING), + },{ + // Nothing. + }, + }, + { // new StringBuffer().append(Z) = new StringBuffer("....") + { + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT), + new SimpleInstruction(InstructionConstants.OP_ICONST_0, A), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_Z), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, BOOLEAN_A_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT_STRING), + }, + }, + { // new StringBuffer().append(C) = new StringBuffer("....") + { + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT), + new SimpleInstruction(InstructionConstants.OP_ICONST_0, A), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_C), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, CHAR_A_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT_STRING), + }, + }, + { // new StringBuffer().append(Cc) = new StringBuffer("....") + { + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT), + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_C), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, CHAR_A_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT_STRING), + }, + }, + { // new StringBuffer().append(I) = new StringBuffer("....") + { + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT), + new SimpleInstruction(InstructionConstants.OP_ICONST_0, A), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_I), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, INT_A_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT_STRING), + }, + }, + { // new StringBuffer().append(Ic) = new StringBuffer("....") + { + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT), + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_I), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, INT_A_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT_STRING), + }, + }, + { // new StringBuffer().append(J) = new StringBuffer("....") + { + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT), + new SimpleInstruction(InstructionConstants.OP_LCONST_0, A), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_J), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, LONG_A_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT_STRING), + }, + }, + { // new StringBuffer().append(Jc) = new StringBuffer("....") + { + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT), + new ConstantInstruction(InstructionConstants.OP_LDC2_W, A), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_J), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, LONG_A_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT_STRING), + }, + }, + { // new StringBuffer().append(F) = new StringBuffer("....") + { + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT), + new SimpleInstruction(InstructionConstants.OP_FCONST_0, A), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_F), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, FLOAT_A_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT_STRING), + }, + }, + { // new StringBuffer().append(Fc) = new StringBuffer("....") + { + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT), + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_F), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, FLOAT_A_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT_STRING), + }, + }, + { // new StringBuffer().append(D) = new StringBuffer("....") + { + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT), + new SimpleInstruction(InstructionConstants.OP_DCONST_0, A), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_D), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, DOUBLE_A_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT_STRING), + }, + }, + { // new StringBuffer().append(Dc) = new StringBuffer("....") + { + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT), + new ConstantInstruction(InstructionConstants.OP_LDC2_W, A), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_D), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, DOUBLE_A_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT_STRING), + }, + }, + { // new StringBuffer().append("...") = new StringBuffer("...") + { + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT), + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_STRING), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT_STRING), + }, + }, + { // new StringBuffer("...").append(Z) = new StringBuffer("....") + { + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT_STRING), + new SimpleInstruction(InstructionConstants.OP_ICONST_0, B), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_Z), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, STRING_A_STRING | BOOLEAN_B_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT_STRING), + }, + }, + { // new StringBuffer("...").append(C) = new StringBuffer("....") + { + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT_STRING), + new SimpleInstruction(InstructionConstants.OP_ICONST_0, B), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_C), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, STRING_A_STRING | CHAR_B_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT_STRING), + }, + }, + { // new StringBuffer("...").append(Cc) = new StringBuffer("....") + { + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT_STRING), + new ConstantInstruction(InstructionConstants.OP_LDC, B), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_C), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, STRING_A_STRING | CHAR_B_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT_STRING), + }, + }, + { // new StringBuffer("...").append(I) = new StringBuffer("....") + { + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT_STRING), + new SimpleInstruction(InstructionConstants.OP_ICONST_0, B), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_I), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, STRING_A_STRING | INT_B_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT_STRING), + }, + }, + { // new StringBuffer("...").append(Ic) = new StringBuffer("....") + { + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT_STRING), + new ConstantInstruction(InstructionConstants.OP_LDC, B), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_I), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, STRING_A_STRING | INT_B_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT_STRING), + }, + }, + { // new StringBuffer("...").append(J) = new StringBuffer("....") + { + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT_STRING), + new SimpleInstruction(InstructionConstants.OP_LCONST_0, B), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_J), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, STRING_A_STRING | LONG_B_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT_STRING), + }, + }, + { // new StringBuffer("...").append(Jc) = new StringBuffer("....") + { + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT_STRING), + new ConstantInstruction(InstructionConstants.OP_LDC2_W, B), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_J), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, STRING_A_STRING | LONG_B_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT_STRING), + }, + }, + { // new StringBuffer("...").append(F) = new StringBuffer("....") + { + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT_STRING), + new SimpleInstruction(InstructionConstants.OP_FCONST_0, B), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_F), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, STRING_A_STRING | FLOAT_B_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT_STRING), + }, + }, + { // new StringBuffer("...").append(Fc) = new StringBuffer("....") + { + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT_STRING), + new ConstantInstruction(InstructionConstants.OP_LDC, B), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_F), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, STRING_A_STRING | FLOAT_B_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT_STRING), + }, + }, + { // new StringBuffer("...").append(D) = new StringBuffer("....") + { + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT_STRING), + new SimpleInstruction(InstructionConstants.OP_DCONST_0, B), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_D), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, STRING_A_STRING | DOUBLE_B_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT_STRING), + }, + }, + { // new StringBuffer("...").append(Dc) = new StringBuffer("....") + { + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT_STRING), + new ConstantInstruction(InstructionConstants.OP_LDC2_W, B), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_D), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, STRING_A_STRING | DOUBLE_B_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT_STRING), + }, + }, + { // new StringBuffer("...").append("...") = new StringBuffer("......") + { + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT_STRING), + new ConstantInstruction(InstructionConstants.OP_LDC, B), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_STRING), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, STRING_A_STRING | STRING_B_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT_STRING), + }, + }, + { // StringBuffer#append("...").append(Z) = StringBuffer#append("....") + { + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_STRING), + new SimpleInstruction(InstructionConstants.OP_ICONST_0, B), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_Z), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, STRING_A_STRING | BOOLEAN_B_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_STRING), + }, + }, + { // StringBuffer#append("...").append(C) = StringBuffer#append("....") + { + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_STRING), + new SimpleInstruction(InstructionConstants.OP_ICONST_0, B), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_C), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, STRING_A_STRING | CHAR_B_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_STRING), + }, + }, + { // StringBuffer#append("...").append(Cc) = StringBuffer#append("....") + { + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_STRING), + new ConstantInstruction(InstructionConstants.OP_LDC, B), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_C), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, STRING_A_STRING | CHAR_B_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_STRING), + }, + }, + { // StringBuffer#append("...").append(I) = StringBuffer#append("....") + { + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_STRING), + new SimpleInstruction(InstructionConstants.OP_ICONST_0, B), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_I), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, STRING_A_STRING | INT_B_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_STRING), + }, + }, + { // StringBuffer#append("...").append(Ic) = StringBuffer#append("....") + { + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_STRING), + new ConstantInstruction(InstructionConstants.OP_LDC, B), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_I), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, STRING_A_STRING | INT_B_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_STRING), + }, + }, + { // StringBuffer#append("...").append(J) = StringBuffer#append("....") + { + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_STRING), + new SimpleInstruction(InstructionConstants.OP_LCONST_0, B), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_J), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, STRING_A_STRING | LONG_B_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_STRING), + }, + }, + { // StringBuffer#append("...").append(Jc) = StringBuffer#append("....") + { + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_STRING), + new ConstantInstruction(InstructionConstants.OP_LDC2_W, B), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_J), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, STRING_A_STRING | LONG_B_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_STRING), + }, + }, + { // StringBuffer#append("...").append(F) = StringBuffer#append("....") + { + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_STRING), + new SimpleInstruction(InstructionConstants.OP_FCONST_0, B), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_F), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, STRING_A_STRING | FLOAT_B_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_STRING), + }, + }, + { // StringBuffer#append("...").append(Fc) = StringBuffer#append("....") + { + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_STRING), + new ConstantInstruction(InstructionConstants.OP_LDC, B), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_F), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, STRING_A_STRING | FLOAT_B_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_STRING), + }, + }, + { // StringBuffer#append("...").append(D) = StringBuffer#append("....") + { + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_STRING), + new SimpleInstruction(InstructionConstants.OP_DCONST_0, B), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_D), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, STRING_A_STRING | DOUBLE_B_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_STRING), + }, + }, + { // StringBuffer#append("...").append(Dc) = StringBuffer#append("....") + { + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_STRING), + new ConstantInstruction(InstructionConstants.OP_LDC2_W, B), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_D), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, STRING_A_STRING | DOUBLE_B_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_STRING), + }, + }, + { // StringBuffer#append("...").append("...") = StringBuffer#append("......") + { + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_STRING), + new ConstantInstruction(InstructionConstants.OP_LDC, B), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_STRING), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, STRING_A_STRING | STRING_B_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_STRING), + }, + }, + { // new StringBuffer().append(z).toString() = String.valueOf(z) + { + new ConstantInstruction(InstructionConstants.OP_NEW, CLASS_STRINGBUFFER), + new SimpleInstruction(InstructionConstants.OP_DUP), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT), + new VariableInstruction(InstructionConstants.OP_ILOAD, A), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_Z), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_TOSTRING), + },{ + new VariableInstruction(InstructionConstants.OP_ILOAD, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESTATIC, METHOD_STRING_VALUEOF_Z), + }, + }, + { // new StringBuffer().append(c).toString() = String.valueOf(c) + { + new ConstantInstruction(InstructionConstants.OP_NEW, CLASS_STRINGBUFFER), + new SimpleInstruction(InstructionConstants.OP_DUP), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT), + new VariableInstruction(InstructionConstants.OP_ILOAD, A), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_C), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_TOSTRING), + },{ + new VariableInstruction(InstructionConstants.OP_ILOAD, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESTATIC, METHOD_STRING_VALUEOF_C), + }, + }, + { // new StringBuffer().append(i).toString() = String.valueOf(i) + { + new ConstantInstruction(InstructionConstants.OP_NEW, CLASS_STRINGBUFFER), + new SimpleInstruction(InstructionConstants.OP_DUP), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT), + new VariableInstruction(InstructionConstants.OP_ILOAD, A), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_I), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_TOSTRING), + },{ + new VariableInstruction(InstructionConstants.OP_ILOAD, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESTATIC, METHOD_STRING_VALUEOF_I), + }, + }, + { // new StringBuffer().append(j).toString() = String.valueOf(j) + { + new ConstantInstruction(InstructionConstants.OP_NEW, CLASS_STRINGBUFFER), + new SimpleInstruction(InstructionConstants.OP_DUP), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT), + new VariableInstruction(InstructionConstants.OP_LLOAD, A), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_J), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_TOSTRING), + },{ + new VariableInstruction(InstructionConstants.OP_LLOAD, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESTATIC, METHOD_STRING_VALUEOF_J), + }, + }, + { // new StringBuffer().append(f).toString() = String.valueOf(f) + { + new ConstantInstruction(InstructionConstants.OP_NEW, CLASS_STRINGBUFFER), + new SimpleInstruction(InstructionConstants.OP_DUP), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT), + new VariableInstruction(InstructionConstants.OP_FLOAD, A), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_F), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_TOSTRING), + },{ + new VariableInstruction(InstructionConstants.OP_FLOAD, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESTATIC, METHOD_STRING_VALUEOF_F), + }, + }, + { // new StringBuffer().append(d).toString() = String.valueOf(d) + { + new ConstantInstruction(InstructionConstants.OP_NEW, CLASS_STRINGBUFFER), + new SimpleInstruction(InstructionConstants.OP_DUP), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT), + new VariableInstruction(InstructionConstants.OP_DLOAD, A), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_D), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_TOSTRING), + },{ + new VariableInstruction(InstructionConstants.OP_DLOAD, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESTATIC, METHOD_STRING_VALUEOF_D), + }, + }, + { // new StringBuffer().append(string).toString() = string + { + new ConstantInstruction(InstructionConstants.OP_NEW, CLASS_STRINGBUFFER), + new SimpleInstruction(InstructionConstants.OP_DUP), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT), + new VariableInstruction(InstructionConstants.OP_ALOAD, A), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_TOSTRING), + },{ + new VariableInstruction(InstructionConstants.OP_ALOAD, A), + }, + }, + { // new StringBuffer().append(object).toString() = String.valueOf(object) + { + new ConstantInstruction(InstructionConstants.OP_NEW, CLASS_STRINGBUFFER), + new SimpleInstruction(InstructionConstants.OP_DUP), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUFFER_INIT), + new VariableInstruction(InstructionConstants.OP_ALOAD, A), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_APPEND_OBJECT), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUFFER_TOSTRING), + },{ + new VariableInstruction(InstructionConstants.OP_ALOAD, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESTATIC, METHOD_STRING_VALUEOF_OBJECT), + }, + }, + + { // new StringBuilder("...").toString() = "..." (ignoring identity) + { + new ConstantInstruction(InstructionConstants.OP_NEW, CLASS_STRINGBUILDER), + new SimpleInstruction(InstructionConstants.OP_DUP), + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_TOSTRING), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, A), + }, + }, + { // new StringBuilder(string).toString() = string (ignoring identity) + { + new ConstantInstruction(InstructionConstants.OP_NEW, CLASS_STRINGBUILDER), + new SimpleInstruction(InstructionConstants.OP_DUP), + new VariableInstruction(InstructionConstants.OP_ALOAD, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_TOSTRING), + },{ + new VariableInstruction(InstructionConstants.OP_ALOAD, A), + }, + }, + { // new StringBuilder("...").length() = length + { + new ConstantInstruction(InstructionConstants.OP_NEW, CLASS_STRINGBUILDER), + new SimpleInstruction(InstructionConstants.OP_DUP), + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_LENGTH), + },{ + new SimpleInstruction(InstructionConstants.OP_SIPUSH, STRING_A_LENGTH), + }, + }, + { // new StringBuilder() (without dup) = nothing + { + new ConstantInstruction(InstructionConstants.OP_NEW, CLASS_STRINGBUILDER), + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT), + },{ + // Nothing. + }, + }, + { // new StringBuilder("...") (without dup) = nothing + { + new ConstantInstruction(InstructionConstants.OP_NEW, CLASS_STRINGBUILDER), + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT_STRING), + },{ + // Nothing. + }, + }, + { // new StringBuilder()/pop = nothing + { + new ConstantInstruction(InstructionConstants.OP_NEW, CLASS_STRINGBUILDER), + new SimpleInstruction(InstructionConstants.OP_DUP), + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT), + new SimpleInstruction(InstructionConstants.OP_POP), + },{ + // Nothing. + }, + }, + { // new StringBuilder("...")/pop = nothing + { + new ConstantInstruction(InstructionConstants.OP_NEW, CLASS_STRINGBUILDER), + new SimpleInstruction(InstructionConstants.OP_DUP), + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT_STRING), + new SimpleInstruction(InstructionConstants.OP_POP), + },{ + // Nothing. + }, + }, + { // new StringBuilder("...").append(z)/pop = nothing + { + new ConstantInstruction(InstructionConstants.OP_NEW, CLASS_STRINGBUILDER), + new SimpleInstruction(InstructionConstants.OP_DUP), + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT_STRING), + new VariableInstruction(InstructionConstants.OP_ILOAD, B), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_Z), + new SimpleInstruction(InstructionConstants.OP_POP), + },{ + // Nothing. + }, + }, + { // new StringBuilder("...").append(c)/pop = nothing + { + new ConstantInstruction(InstructionConstants.OP_NEW, CLASS_STRINGBUILDER), + new SimpleInstruction(InstructionConstants.OP_DUP), + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT_STRING), + new VariableInstruction(InstructionConstants.OP_ILOAD, B), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_C), + new SimpleInstruction(InstructionConstants.OP_POP), + },{ + // Nothing. + }, + }, + { // new StringBuilder("...").append(i)/pop = nothing + { + new ConstantInstruction(InstructionConstants.OP_NEW, CLASS_STRINGBUILDER), + new SimpleInstruction(InstructionConstants.OP_DUP), + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT_STRING), + new VariableInstruction(InstructionConstants.OP_ILOAD, B), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_I), + new SimpleInstruction(InstructionConstants.OP_POP), + },{ + // Nothing. + }, + }, + { // new StringBuilder("...").append(l)/pop = nothing + { + new ConstantInstruction(InstructionConstants.OP_NEW, CLASS_STRINGBUILDER), + new SimpleInstruction(InstructionConstants.OP_DUP), + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT_STRING), + new VariableInstruction(InstructionConstants.OP_LLOAD, B), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_J), + new SimpleInstruction(InstructionConstants.OP_POP), + },{ + // Nothing. + }, + }, + { // new StringBuilder("...").append(f)/pop = nothing + { + new ConstantInstruction(InstructionConstants.OP_NEW, CLASS_STRINGBUILDER), + new SimpleInstruction(InstructionConstants.OP_DUP), + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT_STRING), + new VariableInstruction(InstructionConstants.OP_FLOAD, B), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_F), + new SimpleInstruction(InstructionConstants.OP_POP), + },{ + // Nothing. + }, + }, + { // new StringBuilder("...").append(d)/pop = nothing + { + new ConstantInstruction(InstructionConstants.OP_NEW, CLASS_STRINGBUILDER), + new SimpleInstruction(InstructionConstants.OP_DUP), + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT_STRING), + new VariableInstruction(InstructionConstants.OP_DLOAD, B), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_D), + new SimpleInstruction(InstructionConstants.OP_POP), + },{ + // Nothing. + }, + }, + { // new StringBuilder("...").append(s)/pop = nothing + { + new ConstantInstruction(InstructionConstants.OP_NEW, CLASS_STRINGBUILDER), + new SimpleInstruction(InstructionConstants.OP_DUP), + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT_STRING), + new VariableInstruction(InstructionConstants.OP_ALOAD, B), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_STRING), + new SimpleInstruction(InstructionConstants.OP_POP), + },{ + // Nothing. + }, + }, + { // StringBuilder#toString()/pop = pop + { + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_TOSTRING), + new SimpleInstruction(InstructionConstants.OP_POP), + },{ + new SimpleInstruction(InstructionConstants.OP_POP), + }, + }, + { // StringBuilder#append("") = nothing + { + new ConstantInstruction(InstructionConstants.OP_LDC, STRING_EMPTY), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_STRING), + },{ + // Nothing. + }, + }, + { // new StringBuilder().append(Z) = new StringBuilder("....") + { + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT), + new SimpleInstruction(InstructionConstants.OP_ICONST_0, A), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_Z), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, BOOLEAN_A_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT_STRING), + }, + }, + { // new StringBuilder().append(C) = new StringBuilder("....") + { + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT), + new SimpleInstruction(InstructionConstants.OP_ICONST_0, A), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_C), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, CHAR_A_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT_STRING), + }, + }, + { // new StringBuilder().append(Cc) = new StringBuilder("....") + { + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT), + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_C), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, CHAR_A_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT_STRING), + }, + }, + { // new StringBuilder().append(I) = new StringBuilder("....") + { + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT), + new SimpleInstruction(InstructionConstants.OP_ICONST_0, A), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_I), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, INT_A_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT_STRING), + }, + }, + { // new StringBuilder().append(Ic) = new StringBuilder("....") + { + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT), + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_I), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, INT_A_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT_STRING), + }, + }, + { // new StringBuilder().append(J) = new StringBuilder("....") + { + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT), + new SimpleInstruction(InstructionConstants.OP_LCONST_0, A), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_J), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, LONG_A_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT_STRING), + }, + }, + { // new StringBuilder().append(Jc) = new StringBuilder("....") + { + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT), + new ConstantInstruction(InstructionConstants.OP_LDC2_W, A), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_J), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, LONG_A_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT_STRING), + }, + }, + { // new StringBuilder().append(F) = new StringBuilder("....") + { + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT), + new SimpleInstruction(InstructionConstants.OP_FCONST_0, A), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_F), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, FLOAT_A_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT_STRING), + }, + }, + { // new StringBuilder().append(Fc) = new StringBuilder("....") + { + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT), + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_F), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, FLOAT_A_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT_STRING), + }, + }, + { // new StringBuilder().append(D) = new StringBuilder("....") + { + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT), + new SimpleInstruction(InstructionConstants.OP_DCONST_0, A), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_D), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, DOUBLE_A_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT_STRING), + }, + }, + { // new StringBuilder().append(Dc) = new StringBuilder("....") + { + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT), + new ConstantInstruction(InstructionConstants.OP_LDC2_W, A), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_D), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, DOUBLE_A_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT_STRING), + }, + }, + { // new StringBuilder().append("...") = new StringBuilder("...") + { + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT), + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_STRING), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT_STRING), + }, + }, + { // new StringBuilder("...").append(Z) = new StringBuilder("....") + { + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT_STRING), + new SimpleInstruction(InstructionConstants.OP_ICONST_0, B), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_Z), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, STRING_A_STRING | BOOLEAN_B_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT_STRING), + }, + }, + { // new StringBuilder("...").append(C) = new StringBuilder("....") + { + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT_STRING), + new SimpleInstruction(InstructionConstants.OP_ICONST_0, B), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_C), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, STRING_A_STRING | CHAR_B_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT_STRING), + }, + }, + { // new StringBuilder("...").append(Cc) = new StringBuilder("....") + { + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT_STRING), + new ConstantInstruction(InstructionConstants.OP_LDC, B), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_C), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, STRING_A_STRING | CHAR_B_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT_STRING), + }, + }, + { // new StringBuilder("...").append(I) = new StringBuilder("....") + { + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT_STRING), + new SimpleInstruction(InstructionConstants.OP_ICONST_0, B), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_I), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, STRING_A_STRING | INT_B_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT_STRING), + }, + }, + { // new StringBuilder("...").append(Ic) = new StringBuilder("....") + { + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT_STRING), + new ConstantInstruction(InstructionConstants.OP_LDC, B), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_I), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, STRING_A_STRING | INT_B_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT_STRING), + }, + }, + { // new StringBuilder("...").append(J) = new StringBuilder("....") + { + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT_STRING), + new SimpleInstruction(InstructionConstants.OP_LCONST_0, B), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_J), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, STRING_A_STRING | LONG_B_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT_STRING), + }, + }, + { // new StringBuilder("...").append(Jc) = new StringBuilder("....") + { + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT_STRING), + new ConstantInstruction(InstructionConstants.OP_LDC2_W, B), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_J), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, STRING_A_STRING | LONG_B_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT_STRING), + }, + }, + { // new StringBuilder("...").append(F) = new StringBuilder("....") + { + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT_STRING), + new SimpleInstruction(InstructionConstants.OP_FCONST_0, B), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_F), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, STRING_A_STRING | FLOAT_B_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT_STRING), + }, + }, + { // new StringBuilder("...").append(Fc) = new StringBuilder("....") + { + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT_STRING), + new ConstantInstruction(InstructionConstants.OP_LDC, B), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_F), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, STRING_A_STRING | FLOAT_B_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT_STRING), + }, + }, + { // new StringBuilder("...").append(D) = new StringBuilder("....") + { + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT_STRING), + new SimpleInstruction(InstructionConstants.OP_DCONST_0, B), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_D), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, STRING_A_STRING | DOUBLE_B_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT_STRING), + }, + }, + { // new StringBuilder("...").append(Dc) = new StringBuilder("....") + { + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT_STRING), + new ConstantInstruction(InstructionConstants.OP_LDC2_W, B), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_D), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, STRING_A_STRING | DOUBLE_B_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT_STRING), + }, + }, + { // new StringBuilder("...").append("...") = new StringBuilder("......") + { + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT_STRING), + new ConstantInstruction(InstructionConstants.OP_LDC, B), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_STRING), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, STRING_A_STRING | STRING_B_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT_STRING), + }, + }, + { // StringBuilder#append("...").append(Z) = StringBuilder#append("....") + { + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_STRING), + new SimpleInstruction(InstructionConstants.OP_ICONST_0, B), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_Z), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, STRING_A_STRING | BOOLEAN_B_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_STRING), + }, + }, + { // StringBuilder#append("...").append(C) = StringBuilder#append("....") + { + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_STRING), + new SimpleInstruction(InstructionConstants.OP_ICONST_0, B), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_C), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, STRING_A_STRING | CHAR_B_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_STRING), + }, + }, + { // StringBuilder#append("...").append(Cc) = StringBuilder#append("....") + { + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_STRING), + new ConstantInstruction(InstructionConstants.OP_LDC, B), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_C), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, STRING_A_STRING | CHAR_B_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_STRING), + }, + }, + { // StringBuilder#append("...").append(I) = StringBuilder#append("....") + { + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_STRING), + new SimpleInstruction(InstructionConstants.OP_ICONST_0, B), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_I), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, STRING_A_STRING | INT_B_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_STRING), + }, + }, + { // StringBuilder#append("...").append(Ic) = StringBuilder#append("....") + { + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_STRING), + new ConstantInstruction(InstructionConstants.OP_LDC, B), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_I), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, STRING_A_STRING | INT_B_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_STRING), + }, + }, + { // StringBuilder#append("...").append(J) = StringBuilder#append("....") + { + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_STRING), + new SimpleInstruction(InstructionConstants.OP_LCONST_0, B), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_J), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, STRING_A_STRING | LONG_B_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_STRING), + }, + }, + { // StringBuilder#append("...").append(Jc) = StringBuilder#append("....") + { + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_STRING), + new ConstantInstruction(InstructionConstants.OP_LDC2_W, B), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_J), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, STRING_A_STRING | LONG_B_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_STRING), + }, + }, + { // StringBuilder#append("...").append(F) = StringBuilder#append("....") + { + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_STRING), + new SimpleInstruction(InstructionConstants.OP_FCONST_0, B), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_F), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, STRING_A_STRING | FLOAT_B_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_STRING), + }, + }, + { // StringBuilder#append("...").append(Fc) = StringBuilder#append("....") + { + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_STRING), + new ConstantInstruction(InstructionConstants.OP_LDC, B), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_F), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, STRING_A_STRING | FLOAT_B_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_STRING), + }, + }, + { // StringBuilder#append("...").append(D) = StringBuilder#append("....") + { + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_STRING), + new SimpleInstruction(InstructionConstants.OP_DCONST_0, B), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_D), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, STRING_A_STRING | DOUBLE_B_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_STRING), + }, + }, + { // StringBuilder#append("...").append(Dc) = StringBuilder#append("....") + { + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_STRING), + new ConstantInstruction(InstructionConstants.OP_LDC2_W, B), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_D), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, STRING_A_STRING | DOUBLE_B_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_STRING), + }, + }, + { // StringBuilder#append("...").append("...") = StringBuilder#append("......") + { + new ConstantInstruction(InstructionConstants.OP_LDC, A), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_STRING), + new ConstantInstruction(InstructionConstants.OP_LDC, B), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_STRING), + },{ + new ConstantInstruction(InstructionConstants.OP_LDC, STRING_A_STRING | STRING_B_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_STRING), + }, + }, + { // new StringBuilder().append(z).toString() = String.valueOf(z) + { + new ConstantInstruction(InstructionConstants.OP_NEW, CLASS_STRINGBUILDER), + new SimpleInstruction(InstructionConstants.OP_DUP), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT), + new VariableInstruction(InstructionConstants.OP_ILOAD, A), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_Z), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_TOSTRING), + },{ + new VariableInstruction(InstructionConstants.OP_ILOAD, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESTATIC, METHOD_STRING_VALUEOF_Z), + }, + }, + { // new StringBuilder().append(c).toString() = String.valueOf(c) + { + new ConstantInstruction(InstructionConstants.OP_NEW, CLASS_STRINGBUILDER), + new SimpleInstruction(InstructionConstants.OP_DUP), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT), + new VariableInstruction(InstructionConstants.OP_ILOAD, A), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_C), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_TOSTRING), + },{ + new VariableInstruction(InstructionConstants.OP_ILOAD, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESTATIC, METHOD_STRING_VALUEOF_C), + }, + }, + { // new StringBuilder().append(i).toString() = String.valueOf(i) + { + new ConstantInstruction(InstructionConstants.OP_NEW, CLASS_STRINGBUILDER), + new SimpleInstruction(InstructionConstants.OP_DUP), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT), + new VariableInstruction(InstructionConstants.OP_ILOAD, A), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_I), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_TOSTRING), + },{ + new VariableInstruction(InstructionConstants.OP_ILOAD, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESTATIC, METHOD_STRING_VALUEOF_I), + }, + }, + { // new StringBuilder().append(j).toString() = String.valueOf(j) + { + new ConstantInstruction(InstructionConstants.OP_NEW, CLASS_STRINGBUILDER), + new SimpleInstruction(InstructionConstants.OP_DUP), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT), + new VariableInstruction(InstructionConstants.OP_LLOAD, A), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_J), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_TOSTRING), + },{ + new VariableInstruction(InstructionConstants.OP_LLOAD, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESTATIC, METHOD_STRING_VALUEOF_J), + }, + }, + { // new StringBuilder().append(f).toString() = String.valueOf(f) + { + new ConstantInstruction(InstructionConstants.OP_NEW, CLASS_STRINGBUILDER), + new SimpleInstruction(InstructionConstants.OP_DUP), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT), + new VariableInstruction(InstructionConstants.OP_FLOAD, A), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_F), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_TOSTRING), + },{ + new VariableInstruction(InstructionConstants.OP_FLOAD, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESTATIC, METHOD_STRING_VALUEOF_F), + }, + }, + { // new StringBuilder().append(d).toString() = String.valueOf(d) + { + new ConstantInstruction(InstructionConstants.OP_NEW, CLASS_STRINGBUILDER), + new SimpleInstruction(InstructionConstants.OP_DUP), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT), + new VariableInstruction(InstructionConstants.OP_DLOAD, A), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_D), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_TOSTRING), + },{ + new VariableInstruction(InstructionConstants.OP_DLOAD, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESTATIC, METHOD_STRING_VALUEOF_D), + }, + }, + { // new StringBuilder().append(string).toString() = string + { + new ConstantInstruction(InstructionConstants.OP_NEW, CLASS_STRINGBUILDER), + new SimpleInstruction(InstructionConstants.OP_DUP), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT), + new VariableInstruction(InstructionConstants.OP_ALOAD, A), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_STRING), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_TOSTRING), + },{ + new VariableInstruction(InstructionConstants.OP_ALOAD, A), + }, + }, + { // new StringBuilder().append(object).toString() = String.valueOf(object) + { + new ConstantInstruction(InstructionConstants.OP_NEW, CLASS_STRINGBUILDER), + new SimpleInstruction(InstructionConstants.OP_DUP), + new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_STRINGBUILDER_INIT), + new VariableInstruction(InstructionConstants.OP_ALOAD, A), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_APPEND_OBJECT), + new ConstantInstruction(InstructionConstants.OP_INVOKEVIRTUAL, METHOD_STRINGBUILDER_TOSTRING), + },{ + new VariableInstruction(InstructionConstants.OP_ALOAD, A), + new ConstantInstruction(InstructionConstants.OP_INVOKESTATIC, METHOD_STRING_VALUEOF_OBJECT), + }, + }, + }; + + + /** + * Prints out the constants and the instruction sequences. + */ + public static void main(String[] args) + { + ProgramClass clazz = new ProgramClass(); + clazz.constantPool = CONSTANTS; + + ClassPrinter printer = new ClassPrinter(); + + for (int index = 0; index < CONSTANTS.length; index++) + { + System.out.print("["+index+"] "); + try + { + CONSTANTS[index].accept(clazz, printer); + } + catch (Exception e) + { + System.out.println("("+e.getClass().getName()+")"); + } + } + + if (CONSTANTS.length != SENTINEL) + { + throw new IllegalStateException("Constants length ["+CONSTANTS.length+"] different from number of constant names ["+SENTINEL+"]"); + } + + Instruction[][][] sequences = STRING; + + for (int sequence = 0; sequence < sequences.length; sequence++) + { + System.out.println(); + Instruction[] instructions = sequences[sequence][0]; + for (int index = 0; index < instructions.length; index++) + { + Instruction instruction = instructions[index]; + try + { + instruction.accept(clazz, null, null, index, new ClassPrinter()); + } + catch (Exception e) {} + } + + System.out.println("=>"); + instructions = sequences[sequence][1]; + for (int index = 0; index < instructions.length; index++) + { + Instruction instruction = instructions[index]; + try + { + instruction.accept(clazz, null, null, index, new ClassPrinter()); + } + catch (Exception e) {} + } + } + } +} diff --git a/src/proguard/optimize/peephole/InstructionSequenceReplacer.java b/src/proguard/optimize/peephole/InstructionSequenceReplacer.java index bce06e2..7ec1a95 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-2009 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2013 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,9 +22,9 @@ package proguard.optimize.peephole; import proguard.classfile.*; import proguard.classfile.attribute.CodeAttribute; -import proguard.classfile.constant.Constant; +import proguard.classfile.constant.*; import proguard.classfile.constant.visitor.ConstantVisitor; -import proguard.classfile.editor.CodeAttributeEditor; +import proguard.classfile.editor.*; import proguard.classfile.instruction.*; import proguard.classfile.instruction.visitor.InstructionVisitor; import proguard.classfile.util.*; @@ -42,10 +42,48 @@ extends SimplifiedVisitor implements InstructionVisitor, ConstantVisitor { + //* private static final boolean DEBUG = false; + /*/ + public static boolean DEBUG = true; + //*/ + + public static final int X = InstructionSequenceMatcher.X; + public static final int Y = InstructionSequenceMatcher.Y; + public static final int Z = InstructionSequenceMatcher.Z; + + public static final int A = InstructionSequenceMatcher.A; + public static final int B = InstructionSequenceMatcher.B; + public static final int C = InstructionSequenceMatcher.C; + public static final int D = InstructionSequenceMatcher.D; + + private static final int BOOLEAN_STRING = 0x1; + private static final int CHAR_STRING = 0x2; + private static final int INT_STRING = 0x3; + private static final int LONG_STRING = 0x4; + private static final int FLOAT_STRING = 0x5; + private static final int DOUBLE_STRING = 0x6; + private static final int STRING_STRING = 0x7; + + public static final int STRING_A_LENGTH = 0x20000000; + public static final int BOOLEAN_A_STRING = 0x20000001; + public static final int CHAR_A_STRING = 0x20000002; + public static final int INT_A_STRING = 0x20000003; + public static final int LONG_A_STRING = 0x20000004; + public static final int FLOAT_A_STRING = 0x20000005; + public static final int DOUBLE_A_STRING = 0x20000006; + public static final int STRING_A_STRING = 0x20000007; + public static final int BOOLEAN_B_STRING = 0x20000010; + public static final int CHAR_B_STRING = 0x20000020; + public static final int INT_B_STRING = 0x20000030; + public static final int LONG_B_STRING = 0x20000040; + public static final int FLOAT_B_STRING = 0x20000050; + public static final int DOUBLE_B_STRING = 0x20000060; + public static final int STRING_B_STRING = 0x20000070; private final InstructionSequenceMatcher instructionSequenceMatcher; + private final Constant[] patternConstants; private final Instruction[] replacementInstructions; private final BranchTargetFinder branchTargetFinder; private final CodeAttributeEditor codeAttributeEditor; @@ -101,6 +139,7 @@ implements InstructionVisitor, InstructionVisitor extraInstructionVisitor) { this.instructionSequenceMatcher = new InstructionSequenceMatcher(patternConstants, patternInstructions); + this.patternConstants = patternConstants; this.replacementInstructions = replacementInstructions; this.branchTargetFinder = branchTargetFinder; this.codeAttributeEditor = codeAttributeEditor; @@ -140,7 +179,7 @@ implements InstructionVisitor, for (int index = 0; index < replacementInstructions.length; index++) { int matchedOffset = instructionSequenceMatcher.matchedInstructionOffset(index); - System.out.println(" "+replacementInstructionFactory.create(index).shrink().toString(matchedOffset)); + System.out.println(" "+replacementInstructionFactory.create(clazz, index).shrink().toString(matchedOffset)); } } @@ -148,7 +187,7 @@ implements InstructionVisitor, for (int index = 0; index < replacementInstructions.length; index++) { codeAttributeEditor.replaceInstruction(instructionSequenceMatcher.matchedInstructionOffset(index), - replacementInstructionFactory.create(index).shrink()); + replacementInstructionFactory.create(clazz, index)); } // Delete any remaining instructions in the from sequence. @@ -204,17 +243,17 @@ implements InstructionVisitor, * Creates the replacement instruction for the given index in the * instruction sequence. */ - public Instruction create(int index) + public Instruction create(Clazz clazz, int index) { // Create the instruction. - replacementInstructions[index].accept(null, + replacementInstructions[index].accept(clazz, null, null, instructionSequenceMatcher.matchedInstructionOffset(index), this); // Return it. - return replacementInstruction.shrink(); + return replacementInstruction; } @@ -224,7 +263,7 @@ implements InstructionVisitor, { replacementInstruction = new SimpleInstruction(simpleInstruction.opcode, - instructionSequenceMatcher.matchedArgument(simpleInstruction.constant)); + matchedArgument(clazz, simpleInstruction.constant)); } @@ -241,7 +280,8 @@ implements InstructionVisitor, { replacementInstruction = new ConstantInstruction(constantInstruction.opcode, - instructionSequenceMatcher.matchedConstantIndex(constantInstruction.constantIndex), + matchedConstantIndex((ProgramClass)clazz, + constantInstruction.constantIndex), instructionSequenceMatcher.matchedArgument(constantInstruction.constant)); } @@ -250,7 +290,8 @@ implements InstructionVisitor, { replacementInstruction = new BranchInstruction(branchInstruction.opcode, - instructionSequenceMatcher.matchedBranchOffset(offset, branchInstruction.branchOffset)); + instructionSequenceMatcher.matchedBranchOffset(offset, + branchInstruction.branchOffset)); } @@ -261,7 +302,8 @@ implements InstructionVisitor, instructionSequenceMatcher.matchedBranchOffset(offset, tableSwitchInstruction.defaultOffset), instructionSequenceMatcher.matchedArgument(tableSwitchInstruction.lowCase), instructionSequenceMatcher.matchedArgument(tableSwitchInstruction.highCase), - instructionSequenceMatcher.matchedJumpOffsets(offset, tableSwitchInstruction.jumpOffsets)); + instructionSequenceMatcher.matchedJumpOffsets(offset, + tableSwitchInstruction.jumpOffsets)); } @@ -274,5 +316,105 @@ implements InstructionVisitor, instructionSequenceMatcher.matchedArguments(lookUpSwitchInstruction.cases), instructionSequenceMatcher.matchedJumpOffsets(offset, lookUpSwitchInstruction.jumpOffsets)); } + + + /** + * Returns the matched argument for the given pattern argument. + */ + private int matchedArgument(Clazz clazz, int argument) + { + // Special case: do we have to compute the string length? + if (argument == STRING_A_LENGTH) + { + // Return the string length. + return clazz.getStringString(instructionSequenceMatcher.matchedArgument(A)).length(); + } + + // Otherwise, just return the matched argument. + return instructionSequenceMatcher.matchedArgument(argument); + } + + + /** + * Returns the matched or newly created constant index for the given + * pattern constant index. + */ + private int matchedConstantIndex(ProgramClass programClass, int constantIndex) + { + // Special case: do we have to create a concatenated string? + if (constantIndex >= BOOLEAN_A_STRING && + constantIndex <= (STRING_A_STRING | STRING_B_STRING)) + { + // Create a new string constant and return its index. + return new ConstantPoolEditor(programClass).addStringConstant( + argumentAsString(programClass, constantIndex & 0xf, A) + + argumentAsString(programClass, (constantIndex >>> 4) & 0xf, B), + null, + null); + } + + int matchedConstantIndex = + instructionSequenceMatcher.matchedConstantIndex(constantIndex); + + // Do we have a matched constant index? + if (matchedConstantIndex > 0) + { + // Return its index. + return matchedConstantIndex; + } + + // Otherwise, we still have to create a new constant. + // This currently only works for constants without any wildcards. + ProgramClass dummyClass = new ProgramClass(); + dummyClass.constantPool = patternConstants; + + return new ConstantAdder(programClass).addConstant(dummyClass, constantIndex); + } + + + private String argumentAsString(ProgramClass programClass, + int valueType, + int argument) + { + switch (valueType) + { + case BOOLEAN_STRING: + return Boolean.toString((instructionSequenceMatcher.wasConstant(argument) ? + ((IntegerConstant)(programClass.getConstant(instructionSequenceMatcher.matchedConstantIndex(argument)))).getValue() : + instructionSequenceMatcher.matchedArgument(argument)) != 0); + + case CHAR_STRING: + return Character.toString((char)(instructionSequenceMatcher.wasConstant(argument) ? + ((IntegerConstant)(programClass.getConstant(instructionSequenceMatcher.matchedConstantIndex(argument)))).getValue() : + instructionSequenceMatcher.matchedArgument(argument))); + + case INT_STRING: + return Integer.toString(instructionSequenceMatcher.wasConstant(argument) ? + ((IntegerConstant)(programClass.getConstant(instructionSequenceMatcher.matchedConstantIndex(argument)))).getValue() : + instructionSequenceMatcher.matchedArgument(argument)); + + case LONG_STRING: + return Long.toString(instructionSequenceMatcher.wasConstant(argument) ? + ((LongConstant)(programClass.getConstant(instructionSequenceMatcher.matchedConstantIndex(argument)))).getValue() : + instructionSequenceMatcher.matchedArgument(argument)); + + case FLOAT_STRING: + return Float.toString(instructionSequenceMatcher.wasConstant(argument) ? + ((FloatConstant)(programClass.getConstant(instructionSequenceMatcher.matchedConstantIndex(argument)))).getValue() : + instructionSequenceMatcher.matchedArgument(argument)); + + case DOUBLE_STRING: + return Double.toString(instructionSequenceMatcher.wasConstant(argument) ? + ((DoubleConstant)(programClass.getConstant(instructionSequenceMatcher.matchedConstantIndex(argument)))).getValue() : + instructionSequenceMatcher.matchedArgument(argument)); + + case STRING_STRING: + return + programClass.getStringString(instructionSequenceMatcher.matchedConstantIndex(argument)); + + default: + return ""; + } + } } } diff --git a/src/proguard/optimize/peephole/InstructionSequencesReplacer.java b/src/proguard/optimize/peephole/InstructionSequencesReplacer.java index f12b51a..22fb6cd 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-2009 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2013 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 55b2f31..f57281c 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-2009 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2013 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/MethodFinalizer.java b/src/proguard/optimize/peephole/MethodFinalizer.java index af1811b..89174ac 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-2009 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2013 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/MethodInliner.java b/src/proguard/optimize/peephole/MethodInliner.java index 55f9ccb..947cd43 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-2009 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2013 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,10 +29,11 @@ import proguard.classfile.editor.*; import proguard.classfile.instruction.*; import proguard.classfile.instruction.visitor.InstructionVisitor; import proguard.classfile.util.*; -import proguard.classfile.visitor.MemberVisitor; +import proguard.classfile.visitor.*; +import proguard.optimize.*; import proguard.optimize.info.*; -import java.util.Stack; +import java.util.*; /** * This AttributeVisitor inlines short methods or methods that are only invoked @@ -48,10 +49,8 @@ implements AttributeVisitor, MemberVisitor { private static final int MAXIMUM_INLINED_CODE_LENGTH = Integer.parseInt(System.getProperty("maximum.inlined.code.length", "8")); - private static final int MAXIMUM_RESULTING_CODE_LENGTH_JSE = Integer.parseInt(System.getProperty("maximum.resulting.code.length", "8000")); + private static final int MAXIMUM_RESULTING_CODE_LENGTH_JSE = Integer.parseInt(System.getProperty("maximum.resulting.code.length", "7000")); private static final int MAXIMUM_RESULTING_CODE_LENGTH_JME = Integer.parseInt(System.getProperty("maximum.resulting.code.length", "2000")); - private static final int MAXIMUM_CODE_EXPANSION = 2; - private static final int MAXIMUM_EXTRA_CODE_LENGTH = 128; //* private static final boolean DEBUG = false; @@ -138,6 +137,42 @@ implements AttributeVisitor, public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) { + // TODO: Remove this when the method inliner has stabilized. + // Catch any unexpected exceptions from the actual visiting method. + try + { + // Process the code. + visitCodeAttribute0(clazz, method, codeAttribute); + } + catch (RuntimeException ex) + { + System.err.println("Unexpected error while inlining method:"); + System.err.println(" Target class = ["+targetClass.getName()+"]"); + System.err.println(" Target method = ["+targetMethod.getName(targetClass)+targetMethod.getDescriptor(targetClass)+"]"); + if (inlining) + { + System.err.println(" Inlined class = ["+clazz.getName()+"]"); + System.err.println(" Inlined method = ["+method.getName(clazz)+method.getDescriptor(clazz)+"]"); + } + System.err.println(" Exception = ["+ex.getClass().getName()+"] ("+ex.getMessage()+")"); + System.err.println("Not inlining this method"); + + if (DEBUG) + { + targetMethod.accept(targetClass, new ClassPrinter()); + if (inlining) + { + method.accept(clazz, new ClassPrinter()); + } + + throw ex; + } + } + } + + + public void visitCodeAttribute0(Clazz clazz, Method method, CodeAttribute codeAttribute) + { if (!inlining) { // codeAttributeComposer.DEBUG = DEBUG = @@ -278,7 +313,7 @@ implements AttributeVisitor, } codeAttributeComposer.appendInstruction(parameterSize-parameterIndex-1, - new VariableInstruction(opcode, variableOffset + parameterOffset + parameterIndex).shrink()); + new VariableInstruction(opcode, variableOffset + parameterOffset + parameterIndex)); } } @@ -286,7 +321,7 @@ implements AttributeVisitor, if (!isStatic) { codeAttributeComposer.appendInstruction(parameterSize, - new VariableInstruction(InstructionConstants.OP_ASTORE, variableOffset).shrink()); + new VariableInstruction(InstructionConstants.OP_ASTORE, variableOffset)); } codeAttributeComposer.endCodeFragment(); @@ -305,12 +340,12 @@ implements AttributeVisitor, // Copy the instructions. codeAttribute.instructionsAccept(clazz, method, this); - // Copy the exceptions. - codeAttribute.exceptionsAccept(clazz, method, exceptionInfoAdder); - // Append a label just after the code. codeAttributeComposer.appendLabel(codeAttribute.u4codeLength); + // Copy the exceptions. + codeAttribute.exceptionsAccept(clazz, method, exceptionInfoAdder); + codeAttributeComposer.endCodeFragment(); } @@ -319,7 +354,7 @@ implements AttributeVisitor, public void visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction) { - codeAttributeComposer.appendInstruction(offset, instruction.shrink()); + codeAttributeComposer.appendInstruction(offset, instruction); } @@ -346,7 +381,7 @@ implements AttributeVisitor, codeAttribute.u4codeLength - offset); codeAttributeComposer.appendInstruction(offset, - branchInstruction.shrink()); + branchInstruction); } else { @@ -359,7 +394,7 @@ implements AttributeVisitor, } } - codeAttributeComposer.appendInstruction(offset, simpleInstruction.shrink()); + codeAttributeComposer.appendInstruction(offset, simpleInstruction); } @@ -372,7 +407,7 @@ implements AttributeVisitor, variableInstruction.variableIndex += variableOffset; } - codeAttributeComposer.appendInstruction(offset, variableInstruction.shrink()); + codeAttributeComposer.appendInstruction(offset, variableInstruction); } @@ -430,7 +465,7 @@ implements AttributeVisitor, constantAdder.addConstant(clazz, constantInstruction.constantIndex); } - codeAttributeComposer.appendInstruction(offset, constantInstruction.shrink()); + codeAttributeComposer.appendInstruction(offset, constantInstruction); } @@ -454,45 +489,48 @@ implements AttributeVisitor, { int accessFlags = programMethod.getAccessFlags(); - if (// Only inline the method if it is private, static, or final. + if (// Don't inline methods that must be preserved. + !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 && + ClassConstants.INTERNAL_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 && + ClassConstants.INTERNAL_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) || // (programClass.equals(targetClass) && // targetMethod.getName(targetClass).equals(ClassConstants.INTERNAL_METHOD_NAME_INIT))) && - !programMethod.getName(programClass).equals(ClassConstants.INTERNAL_METHOD_NAME_INIT) && + !programMethod.getName(programClass).equals(ClassConstants.INTERNAL_METHOD_NAME_INIT) && // Don't inline a method into itself. (!programMethod.equals(targetMethod) || - !programClass.equals(targetClass)) && + !programClass.equals(targetClass)) && // Only inline the method if it isn't recursing. - !inliningMethods.contains(programMethod) && + !inliningMethods.contains(programMethod) && // Only inline the method if its target class has at least the // same version number as the source class, in order to avoid // introducing incompatible constructs. - targetClass.u4version >= programClass.u4version && + 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) || - programClass.equals(targetClass)) && + programClass.equals(targetClass)) && // Only inline the method if it doesn't branch backward while there // are uninitialized objects. (!BackwardBranchMarker.branchesBackward(programMethod) || - uninitializedObjectCount == 0) && + uninitializedObjectCount == 0) && // Only inline if the code access of the inlined method allows it. (allowAccessModification || @@ -501,47 +539,24 @@ implements AttributeVisitor, (!AccessMethodMarker.accessesPackageCode(programMethod) || ClassUtil.internalPackageName(programClass.getName()).equals( - ClassUtil.internalPackageName(targetClass.getName()))))) && + ClassUtil.internalPackageName(targetClass.getName()))))) && // (!AccessMethodMarker.accessesProtectedCode(programMethod) || // targetClass.extends_(programClass) || // targetClass.implements_(programClass)) || (!AccessMethodMarker.accessesProtectedCode(programMethod) || - programClass.equals(targetClass)) && + programClass.equals(targetClass)) && // Only inline the method if it doesn't catch exceptions, or if it // is invoked with an empty stack. (!CatchExceptionMarker.catchesExceptions(programMethod) || - emptyInvokingStack) && + emptyInvokingStack) && - // Only inline the method if it comes from the same class or from - // a class with a static initializer. + // Only inline the method if it comes from the a class with at most + // a subset of the initialized superclasses. (programClass.equals(targetClass) || - programClass.findMethod(ClassConstants.INTERNAL_METHOD_NAME_CLINIT, - ClassConstants.INTERNAL_METHOD_TYPE_CLINIT) == null)) - { -// System.out.print("MethodInliner: inlining "); -// programMethod.accept(programClass, new SimpleClassPrinter(true)); -// System.out.print(" in "); -// targetMethod.accept(targetClass, new SimpleClassPrinter(true)); -// -// System.out.println(" Private: "+ -// (!AccessMethodMarker.accessesPrivateCode(programMethod) || -// programClass.equals(targetClass))); -// -// System.out.println(" Package: "+ -// (!AccessMethodMarker.accessesPackageCode(programMethod) || -// ClassUtil.internalPackageName(programClass.getName()).equals( -// ClassUtil.internalPackageName(targetClass.getName())))); -// -// System.out.println(" Protected: "+ -// ((!AccessMethodMarker.accessesProtectedCode(programMethod) || -// targetClass.extends_(programClass) || -// targetClass.implements_(programClass)) || -// ClassUtil.internalPackageName(programClass.getName()).equals( -// ClassUtil.internalPackageName(targetClass.getName())))); - - boolean oldInlining = inlining; + initializedSuperClasses(targetClass).containsAll(initializedSuperClasses(programClass)))) + { boolean oldInlining = inlining; inlining = true; inliningMethods.push(programMethod); @@ -564,4 +579,21 @@ implements AttributeVisitor, uninitializedObjectCount--; } } + + + /** + * Returns the set of superclasses and interfaces that are initialized. + */ + private Set initializedSuperClasses(Clazz clazz) + { + Set set = new HashSet(); + + // Visit all superclasses and interfaces, collecting the ones that have + // static initializers. + clazz.hierarchyAccept(true, true, true, false, + new StaticInitializerContainingClassFilter( + new ClassCollector(set))); + + return set; + } } diff --git a/src/proguard/optimize/peephole/NopRemover.java b/src/proguard/optimize/peephole/NopRemover.java index 69adb30..9396c40 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-2009 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2013 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 98f8e8d..2a602ee 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-2009 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2013 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 d9dbf2d..b6fcf18 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-2009 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2013 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,6 +27,8 @@ import proguard.classfile.instruction.*; import proguard.classfile.instruction.visitor.InstructionVisitor; import proguard.classfile.util.SimplifiedVisitor; +import java.util.Arrays; + /** * This AttributeVisitor finds all instruction offsets, branch targets, and * exception targets in the CodeAttribute objects that it visits. @@ -91,10 +93,7 @@ implements AttributeVisitor, else { // Reset the array. - for (int index = 0; index < codeLength; index++) - { - isReachable[index] = false; - } + Arrays.fill(isReachable, 0, codeLength, false); } // Mark the code, starting at the entry point. diff --git a/src/proguard/optimize/peephole/RetargetedInnerClassAttributeRemover.java b/src/proguard/optimize/peephole/RetargetedInnerClassAttributeRemover.java index 6707a12..a67c6ff 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-2009 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2013 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 @@ -28,6 +28,8 @@ import proguard.classfile.constant.visitor.ConstantVisitor; import proguard.classfile.util.SimplifiedVisitor; import proguard.classfile.visitor.ClassVisitor; +import java.util.Arrays; + /** * This ClassVisitor removes InnerClasses and EnclosingMethod attributes in * classes that are retargeted or that refer to classes that are retargeted. @@ -70,12 +72,9 @@ implements ClassVisitor, } // Clean up any remaining array elements. - for (int index = newAtributesCount; index < attributesCount; index++) - { - attributes[index] = null; - } + Arrays.fill(attributes, newAtributesCount, attributesCount, null); - // Update the number of attribuets. + // Update the number of attributes. programClass.u2attributesCount = newAtributesCount; } @@ -90,8 +89,39 @@ implements ClassVisitor, // Check whether the class itself is retargeted. checkTarget(clazz); - // Check whether the referenced classes are retargeted. - innerClassesAttribute.innerClassEntriesAccept(clazz, this); + if (!retargeted) + { + // Check whether the referenced classes are retargeted. + innerClassesAttribute.innerClassEntriesAccept(clazz, this); + int classesCount = innerClassesAttribute.u2classesCount; + InnerClassesInfo[] classes = innerClassesAttribute.classes; + + int newClassesCount = 0; + + // Copy over all non-retargeted attributes. + for (int index = 0; index < classesCount; index++) + { + InnerClassesInfo classInfo = classes[index]; + + // Check if the outer class or inner class is a retargeted class. + retargeted = false; + classInfo.outerClassConstantAccept(clazz, this); + classInfo.innerClassConstantAccept(clazz, this); + if (!retargeted) + { + classes[newClassesCount++] = classInfo; + } + } + + // Clean up any remaining array elements. + Arrays.fill(classes, newClassesCount, classesCount, null); + + // Update the number of classes. + innerClassesAttribute.u2classesCount = newClassesCount; + + // Remove the attribute altogether if it's empty. + retargeted = newClassesCount == 0; + } } diff --git a/src/proguard/optimize/peephole/TargetClassChanger.java b/src/proguard/optimize/peephole/TargetClassChanger.java index 22fd83d..f997e03 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-2009 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2013 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 @@ -51,13 +51,13 @@ implements ClassVisitor, AnnotationVisitor, ElementValueVisitor { + private static final boolean DEBUG = false; + + // Implementations for ClassVisitor. public void visitProgramClass(ProgramClass programClass) { - Clazz superClass = null; - Clazz[] interfaceClasses = null; - // Change the references of the constant pool. programClass.constantPoolEntriesAccept(this); @@ -80,34 +80,39 @@ implements ClassVisitor, programClass.getName(), programClass); + // This class will loose all its interfaces. + programClass.u2interfacesCount = 0; + // This class will loose all its subclasses. programClass.subClasses = null; } - - // Remove interface classes that are pointing to this class. - int newInterfacesCount = 0; - for (int index = 0; index < programClass.u2interfacesCount; index++) + else { - Clazz interfaceClass = programClass.getInterface(index); - if (!programClass.equals(interfaceClass)) + // Remove interface classes that are pointing to this class. + int newInterfacesCount = 0; + for (int index = 0; index < programClass.u2interfacesCount; index++) { - programClass.u2interfaces[newInterfacesCount++] = - programClass.u2interfaces[index]; + Clazz interfaceClass = programClass.getInterface(index); + if (!programClass.equals(interfaceClass)) + { + programClass.u2interfaces[newInterfacesCount++] = + programClass.u2interfaces[index]; + } } - } - programClass.u2interfacesCount = newInterfacesCount; + programClass.u2interfacesCount = newInterfacesCount; - // Update the subclasses of the superclass and interfaces of the - // target class. - ConstantVisitor subclassAdder = - new ReferencedClassVisitor( - new SubclassFilter(programClass, - new SubclassAdder(programClass))); + // Update the subclasses of the superclass and interfaces of the + // target class. + ConstantVisitor subclassAdder = + new ReferencedClassVisitor( + new SubclassFilter(programClass, + new SubclassAdder(programClass))); - programClass.superClassConstantAccept(subclassAdder); - programClass.interfaceConstantsAccept(subclassAdder); + programClass.superClassConstantAccept(subclassAdder); + programClass.interfaceConstantsAccept(subclassAdder); - // TODO: Maybe restore private method references. + // TODO: Maybe restore private method references. + } } @@ -188,6 +193,12 @@ implements ClassVisitor, Clazz newReferencedClass = updateReferencedClass(referencedClass); if (referencedClass != newReferencedClass) { + if (DEBUG) + { + System.out.println("TargetClassChanger:"); + System.out.println(" ["+clazz.getName()+"] changing reference from ["+refConstant.referencedClass+"."+refConstant.referencedMember.getName(refConstant.referencedClass)+refConstant.referencedMember.getDescriptor(refConstant.referencedClass)+"]"); + } + // Change the referenced class. refConstant.referencedClass = newReferencedClass; @@ -197,6 +208,11 @@ implements ClassVisitor, refConstant.getName(clazz), refConstant.getType(clazz), newReferencedClass); + + if (DEBUG) + { + System.out.println(" ["+clazz.getName()+"] to ["+refConstant.referencedClass+"."+refConstant.referencedMember.getName(refConstant.referencedClass)+refConstant.referencedMember.getDescriptor(refConstant.referencedClass)+"]"); + } } } diff --git a/src/proguard/optimize/peephole/UnreachableCodeRemover.java b/src/proguard/optimize/peephole/UnreachableCodeRemover.java index e8a99ab..570b3ca 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-2009 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2013 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 048f5e3..8e77716 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-2009 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2013 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/VariableShrinker.java b/src/proguard/optimize/peephole/VariableShrinker.java index 45b694f..6c05944 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-2009 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2013 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/VerticalClassMerger.java b/src/proguard/optimize/peephole/VerticalClassMerger.java index 29ed6ff..825de94 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-2009 Eric Lafortune (eric@graphics.cornell.edu) + * Copyright (c) 2002-2013 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 |