summaryrefslogtreecommitdiff
path: root/src/proguard/optimize/evaluation/SimpleEnumUseSimplifier.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/proguard/optimize/evaluation/SimpleEnumUseSimplifier.java')
-rw-r--r--src/proguard/optimize/evaluation/SimpleEnumUseSimplifier.java820
1 files changed, 820 insertions, 0 deletions
diff --git a/src/proguard/optimize/evaluation/SimpleEnumUseSimplifier.java b/src/proguard/optimize/evaluation/SimpleEnumUseSimplifier.java
new file mode 100644
index 0000000..b5a2396
--- /dev/null
+++ b/src/proguard/optimize/evaluation/SimpleEnumUseSimplifier.java
@@ -0,0 +1,820 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ * of Java bytecode.
+ *
+ * Copyright (c) 2002-2014 Eric Lafortune (eric@graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.optimize.evaluation;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.*;
+import proguard.classfile.attribute.visitor.AttributeVisitor;
+import proguard.classfile.constant.*;
+import proguard.classfile.constant.visitor.ConstantVisitor;
+import proguard.classfile.editor.*;
+import proguard.classfile.instruction.*;
+import proguard.classfile.instruction.visitor.InstructionVisitor;
+import proguard.classfile.util.*;
+import proguard.classfile.visitor.*;
+import proguard.evaluation.value.*;
+import proguard.optimize.info.SimpleEnumMarker;
+
+/**
+ * This AttributeVisitor simplifies the use of enums in the code attributes that
+ * it visits.
+ *
+ * @see SimpleEnumMarker
+ * @see MemberReferenceFixer
+ * @author Eric Lafortune
+ */
+public class SimpleEnumUseSimplifier
+extends SimplifiedVisitor
+implements AttributeVisitor,
+ InstructionVisitor,
+ ConstantVisitor,
+ ParameterVisitor
+{
+ //*
+ private static final boolean DEBUG = false;
+ /*/
+ private static boolean DEBUG = System.getProperty("enum") != null;
+ //*/
+
+ private final InstructionVisitor extraInstructionVisitor;
+
+ private final PartialEvaluator partialEvaluator;
+ private final CodeAttributeEditor codeAttributeEditor = new CodeAttributeEditor(true, true);
+ private final ConstantVisitor nullParameterFixer = new ReferencedMemberVisitor(new AllParameterVisitor(this));
+
+ // Fields acting as parameters and return values for the visitor methods.
+ private Clazz invocationClazz;
+ private Method invocationMethod;
+ private CodeAttribute invocationCodeAttribute;
+ private int invocationOffset;
+ private boolean isSimpleEnum;
+
+
+ /**
+ * Creates a new SimpleEnumUseSimplifier.
+ */
+ public SimpleEnumUseSimplifier()
+ {
+ this(new PartialEvaluator(), null);
+ }
+
+
+ /**
+ * Creates a new SimpleEnumDescriptorSimplifier.
+ * @param partialEvaluator the partial evaluator that will
+ * execute the code and provide
+ * information about the results.
+ * @param extraInstructionVisitor an optional extra visitor for all
+ * simplified instructions.
+ */
+ public SimpleEnumUseSimplifier(PartialEvaluator partialEvaluator,
+ InstructionVisitor extraInstructionVisitor)
+ {
+ this.partialEvaluator = partialEvaluator;
+ this.extraInstructionVisitor = extraInstructionVisitor;
+ }
+
+
+ // Implementations for AttributeVisitor.
+
+ public void visitAnyAttribute(Clazz clazz, Attribute attribute) {}
+
+
+ public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute)
+ {
+ if (DEBUG)
+ {
+ System.out.println("SimpleEnumUseSimplifier: "+clazz.getName()+"."+method.getName(clazz)+method.getDescriptor(clazz));
+ }
+
+ // Skip the non-static methods of simple enum classes.
+ if (SimpleEnumMarker.isSimpleEnum(clazz) &&
+ (method.getAccessFlags() & ClassConstants.ACC_STATIC) == 0)
+ {
+ return;
+ }
+
+ // Evaluate the method.
+ partialEvaluator.visitCodeAttribute(clazz, method, codeAttribute);
+
+ int codeLength = codeAttribute.u4codeLength;
+
+ // Reset the code changes.
+ codeAttributeEditor.reset(codeLength);
+
+ // Replace any instructions that can be simplified.
+ for (int offset = 0; offset < codeLength; offset++)
+ {
+ if (partialEvaluator.isTraced(offset))
+ {
+ Instruction instruction = InstructionFactory.create(codeAttribute.code,
+ offset);
+
+ instruction.accept(clazz, method, codeAttribute, offset, this);
+ }
+ }
+
+ // Apply all accumulated changes to the code.
+ codeAttributeEditor.visitCodeAttribute(clazz, method, codeAttribute);
+ }
+
+
+ // Implementations for InstructionVisitor.
+
+ public void visitSimpleInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SimpleInstruction simpleInstruction)
+ {
+ switch (simpleInstruction.opcode)
+ {
+ case InstructionConstants.OP_AALOAD:
+ {
+ if (isPushingSimpleEnum(offset))
+ {
+ // Load a simple enum integer from an integer array.
+ replaceInstruction(clazz,
+ offset,
+ simpleInstruction,
+ new SimpleInstruction(
+ InstructionConstants.OP_IALOAD));
+ }
+ break;
+ }
+ case InstructionConstants.OP_AASTORE:
+ {
+ if (isPoppingSimpleEnumArray(offset, 2))
+ {
+ // Store a simple enum integer in an integer array.
+ replaceInstruction(clazz,
+ offset,
+ simpleInstruction,
+ new SimpleInstruction(InstructionConstants.OP_IASTORE));
+
+ // Replace any producers of null constants.
+ replaceNullStackEntryProducers(clazz, method, codeAttribute, offset);
+ }
+ break;
+ }
+ case InstructionConstants.OP_ARETURN:
+ {
+ if (isReturningSimpleEnum(clazz, method))
+ {
+ // Return a simple enum integer instead of an enum.
+ replaceInstruction(clazz,
+ offset,
+ simpleInstruction,
+ new SimpleInstruction(InstructionConstants.OP_IRETURN));
+
+ // Replace any producers of null constants.
+ replaceNullStackEntryProducers(clazz, method, codeAttribute, offset);
+ }
+ break;
+ }
+ }
+ }
+
+
+ public void visitVariableInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VariableInstruction variableInstruction)
+ {
+ int variableIndex = variableInstruction.variableIndex;
+
+ switch (variableInstruction.opcode)
+ {
+ case InstructionConstants.OP_ALOAD:
+ case InstructionConstants.OP_ALOAD_0:
+ case InstructionConstants.OP_ALOAD_1:
+ case InstructionConstants.OP_ALOAD_2:
+ case InstructionConstants.OP_ALOAD_3:
+ {
+ if (isPushingSimpleEnum(offset))
+ {
+ // Load a simple enum integer instead of an enum.
+ replaceInstruction(clazz,
+ offset,
+ variableInstruction,
+ new VariableInstruction(InstructionConstants.OP_ILOAD,
+ variableIndex));
+
+ // Replace any producers of null constants.
+ replaceNullVariableProducers(clazz,
+ method,
+ codeAttribute,
+ offset,
+ variableIndex);
+ }
+ break;
+ }
+ case InstructionConstants.OP_ASTORE:
+ case InstructionConstants.OP_ASTORE_0:
+ case InstructionConstants.OP_ASTORE_1:
+ case InstructionConstants.OP_ASTORE_2:
+ case InstructionConstants.OP_ASTORE_3:
+ {
+ if (!partialEvaluator.isSubroutineStart(offset) &&
+ isPoppingSimpleEnum(offset))
+ {
+ // Store a simple enum integer instead of an enum.
+ replaceInstruction(clazz,
+ offset,
+ variableInstruction,
+ new VariableInstruction(InstructionConstants.OP_ISTORE,
+ variableIndex));
+
+ // Replace any producers of null constants.
+ replaceNullStackEntryProducers(clazz, method, codeAttribute, offset);
+ }
+ break;
+ }
+ }
+ }
+
+
+ public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction)
+ {
+ switch (constantInstruction.opcode)
+ {
+ case InstructionConstants.OP_PUTSTATIC:
+ case InstructionConstants.OP_PUTFIELD:
+ {
+ // Replace any producers of null constants.
+ invocationClazz = clazz;
+ invocationMethod = method;
+ invocationCodeAttribute = codeAttribute;
+ invocationOffset = offset;
+ clazz.constantPoolEntryAccept(constantInstruction.constantIndex,
+ nullParameterFixer);
+ break;
+ }
+ case InstructionConstants.OP_INVOKEVIRTUAL:
+ {
+ // Check if the instruction is calling a simple enum.
+ String invokedMethodName =
+ clazz.getRefName(constantInstruction.constantIndex);
+ String invokedMethodType =
+ clazz.getRefType(constantInstruction.constantIndex);
+ int stackEntryIndex =
+ ClassUtil.internalMethodParameterSize(invokedMethodType);
+ if (isPoppingSimpleEnum(offset, stackEntryIndex))
+ {
+ replaceSupportedMethod(clazz,
+ offset,
+ constantInstruction,
+ invokedMethodName,
+ invokedMethodType);
+ }
+
+ // Fall through to check the parameters.
+ }
+ case InstructionConstants.OP_INVOKESPECIAL:
+ case InstructionConstants.OP_INVOKESTATIC:
+ case InstructionConstants.OP_INVOKEINTERFACE:
+ {
+ // Replace any producers of null constants.
+ invocationClazz = clazz;
+ invocationMethod = method;
+ invocationCodeAttribute = codeAttribute;
+ invocationOffset = offset;
+ clazz.constantPoolEntryAccept(constantInstruction.constantIndex,
+ nullParameterFixer);
+ break;
+ }
+ case InstructionConstants.OP_ANEWARRAY:
+ {
+ int constantIndex = constantInstruction.constantIndex;
+
+ if (isReferencingSimpleEnum(clazz, constantIndex) &&
+ !ClassUtil.isInternalArrayType(clazz.getClassName(constantIndex)))
+ {
+ // Create an integer array instead of an enum array.
+ replaceInstruction(clazz,
+ offset,
+ constantInstruction,
+ new SimpleInstruction(InstructionConstants.OP_NEWARRAY,
+ InstructionConstants.ARRAY_T_INT));
+ }
+ break;
+ }
+ case InstructionConstants.OP_CHECKCAST:
+ {
+ if (isPoppingSimpleEnum(offset))
+ {
+ // Enum classes can only be simple if the checkcast
+ // succeeds, so we can delete it.
+ deleteInstruction(clazz,
+ offset,
+ constantInstruction);
+
+ // Replace any producers of null constants.
+ replaceNullStackEntryProducers(clazz, method, codeAttribute, offset);
+ }
+ break;
+ }
+ case InstructionConstants.OP_INSTANCEOF:
+ {
+ if (isPoppingSimpleEnum(offset))
+ {
+ // Enum classes can only be simple if the instanceof
+ // succeeds, so we can push a constant result.
+ replaceInstruction(clazz,
+ offset,
+ constantInstruction,
+ new SimpleInstruction(InstructionConstants.OP_ICONST_1));
+
+ // Replace any producers of null constants.
+ replaceNullStackEntryProducers(clazz, method, codeAttribute, offset);
+ }
+ break;
+ }
+ }
+ }
+
+
+ public void visitBranchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, BranchInstruction branchInstruction)
+ {
+ switch (branchInstruction.opcode)
+ {
+ case InstructionConstants.OP_IFACMPEQ:
+ {
+ if (isPoppingSimpleEnum(offset))
+ {
+ // Compare simple enum integers instead of enums.
+ replaceInstruction(clazz,
+ offset,
+ branchInstruction,
+ new BranchInstruction(InstructionConstants.OP_IFICMPEQ,
+ branchInstruction.branchOffset));
+ }
+ break;
+ }
+ case InstructionConstants.OP_IFACMPNE:
+ {
+ if (isPoppingSimpleEnum(offset))
+ {
+ // Compare simple enum integers instead of enums.
+ replaceInstruction(clazz,
+ offset,
+ branchInstruction,
+ new BranchInstruction(InstructionConstants.OP_IFICMPNE,
+ branchInstruction.branchOffset));
+ }
+ break;
+ }
+ case InstructionConstants.OP_IFNULL:
+ {
+ if (isPoppingSimpleEnum(offset))
+ {
+ // Compare with 0 instead of null.
+ replaceInstruction(clazz,
+ offset,
+ branchInstruction,
+ new BranchInstruction(
+ InstructionConstants.OP_IFEQ,
+ branchInstruction.branchOffset));
+ }
+ break;
+ }
+ case InstructionConstants.OP_IFNONNULL:
+ {
+ if (isPoppingSimpleEnum(offset))
+ {
+ // Compare with 0 instead of null.
+ replaceInstruction(clazz,
+ offset,
+ branchInstruction,
+ new BranchInstruction(InstructionConstants.OP_IFNE,
+ branchInstruction.branchOffset));
+ }
+ break;
+ }
+ }
+ }
+
+
+ public void visitAnySwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SwitchInstruction switchInstruction)
+ {
+ }
+
+
+ // Implementations for ConstantVisitor.
+
+ public void visitAnyConstant(Clazz clazz, Constant constant) {}
+
+
+ public void visitStringConstant(Clazz clazz, StringConstant stringConstant)
+ {
+ // Does the constant refer to a simple enum type?
+ isSimpleEnum = isSimpleEnum(stringConstant.referencedClass);
+ }
+
+
+ public void visitClassConstant(Clazz clazz, ClassConstant classConstant)
+ {
+ // Does the constant refer to a simple enum type?
+ isSimpleEnum = isSimpleEnum(classConstant.referencedClass);
+ }
+
+
+ // Implementations for ParameterVisitor.
+
+ public void visitParameter(Clazz clazz, Member member, int parameterIndex, int parameterCount, int parameterOffset, int parameterSize, String parameterType, Clazz referencedClass)
+ {
+ // Check if the parameter is passing a simple enum as a more general
+ // type.
+ if (!ClassUtil.isInternalPrimitiveType(parameterType.charAt(0)) &&
+ isSimpleEnum(referencedClass))
+ {
+ // Replace any producers of null constants for this parameter.
+ int stackEntryIndex = parameterSize - parameterOffset - 1;
+
+ replaceNullStackEntryProducers(invocationClazz,
+ invocationMethod,
+ invocationCodeAttribute,
+ invocationOffset,
+ stackEntryIndex);
+ }
+ }
+
+
+ // Small utility methods.
+
+ /**
+ * Returns whether the constant at the given offset is referencing a
+ * simple enum class.
+ */
+ private boolean isReferencingSimpleEnum(Clazz clazz, int constantIndex)
+ {
+ isSimpleEnum = false;
+
+ clazz.constantPoolEntryAccept(constantIndex, this);
+
+ return isSimpleEnum;
+ }
+
+
+ /**
+ * Returns whether the given method is returning a simple enum class.
+ */
+ private boolean isReturningSimpleEnum(Clazz clazz, Method method)
+ {
+ String descriptor = method.getDescriptor(clazz);
+ String returnType = ClassUtil.internalMethodReturnType(descriptor);
+
+ if (ClassUtil.isInternalClassType(returnType) &&
+ !ClassUtil.isInternalArrayType(returnType))
+ {
+ Clazz[] referencedClasses =
+ ((ProgramMethod)method).referencedClasses;
+
+ if (referencedClasses != null)
+ {
+ int returnedClassIndex =
+ new DescriptorClassEnumeration(descriptor).classCount() - 1;
+
+ Clazz returnedClass = referencedClasses[returnedClassIndex];
+
+ return isSimpleEnum(returnedClass);
+ }
+ }
+
+ return false;
+ }
+
+
+ /**
+ * Returns whether the instruction at the given offset is pushing a simple
+ * enum class.
+ */
+ private boolean isPushingSimpleEnum(int offset)
+ {
+ ReferenceValue referenceValue =
+ partialEvaluator.getStackAfter(offset).getTop(0).referenceValue();
+
+ Clazz referencedClass = referenceValue.getReferencedClass();
+
+ return isSimpleEnum(referencedClass) &&
+ !ClassUtil.isInternalArrayType(referenceValue.getType());
+ }
+
+
+ /**
+ * Returns whether the instruction at the given offset is popping a simple
+ * enum class.
+ */
+ private boolean isPoppingSimpleEnum(int offset)
+ {
+ return isPoppingSimpleEnum(offset, 0);
+ }
+
+
+ /**
+ * Returns whether the instruction at the given offset is popping a simple
+ * enum class.
+ */
+ private boolean isPoppingSimpleEnum(int offset, int stackEntryIndex)
+ {
+ ReferenceValue referenceValue =
+ partialEvaluator.getStackBefore(offset).getTop(stackEntryIndex).referenceValue();
+
+ return isSimpleEnum(referenceValue.getReferencedClass()) &&
+ !ClassUtil.isInternalArrayType(referenceValue.getType());
+ }
+
+
+ /**
+ * Returns whether the instruction at the given offset is popping a simple
+ * enum type. This includes simple enum arrays.
+ */
+ private boolean isPoppingSimpleEnumType(int offset, int stackEntryIndex)
+ {
+ ReferenceValue referenceValue =
+ partialEvaluator.getStackBefore(offset).getTop(stackEntryIndex).referenceValue();
+
+ return isSimpleEnum(referenceValue.getReferencedClass());
+ }
+
+
+ /**
+ * Returns whether the instruction at the given offset is popping a
+ * one-dimensional simple enum array.
+ */
+ private boolean isPoppingSimpleEnumArray(int offset, int stackEntryIndex)
+ {
+ ReferenceValue referenceValue =
+ partialEvaluator.getStackBefore(offset).getTop(stackEntryIndex).referenceValue();
+
+ return isSimpleEnum(referenceValue.getReferencedClass()) &&
+ ClassUtil.internalArrayTypeDimensionCount(referenceValue.getType()) == 1;
+ }
+
+
+ /**
+ * Returns whether the given class is not null and a simple enum class.
+ */
+ private boolean isSimpleEnum(Clazz clazz)
+ {
+ return clazz != null &&
+ SimpleEnumMarker.isSimpleEnum(clazz);
+ }
+
+
+ /**
+ * Returns whether the specified enum method is supported for simple enums.
+ */
+ private void replaceSupportedMethod(Clazz clazz,
+ int offset,
+ Instruction instruction,
+ String name,
+ String type)
+ {
+ if (name.equals(ClassConstants.METHOD_NAME_ORDINAL) &&
+ type.equals(ClassConstants.METHOD_TYPE_ORDINAL))
+ {
+ Instruction[] replacementInstructions = new Instruction[]
+ {
+ new SimpleInstruction(InstructionConstants.OP_ICONST_1),
+ new SimpleInstruction(InstructionConstants.OP_ISUB),
+ };
+
+ replaceInstructions(clazz,
+ offset,
+ instruction,
+ replacementInstructions);
+ }
+ }
+
+
+ /**
+ * Replaces the instruction at the given offset by the given instructions.
+ */
+ private void replaceInstructions(Clazz clazz,
+ int offset,
+ Instruction instruction,
+ Instruction[] replacementInstructions)
+ {
+ if (DEBUG) System.out.println(" Replacing instruction "+instruction.toString(offset)+" -> "+replacementInstructions.length+" instructions");
+
+ codeAttributeEditor.replaceInstruction(offset, replacementInstructions);
+
+ // Visit the instruction, if required.
+ if (extraInstructionVisitor != null)
+ {
+ // Note: we're not passing the right arguments for now, knowing that
+ // they aren't used anyway.
+ instruction.accept(clazz, null, null, offset, extraInstructionVisitor);
+ }
+ }
+
+
+ /**
+ * Replaces the instruction at the given offset by the given instruction,
+ * popping any now unused stack entries.
+ */
+ private void replaceInstruction(Clazz clazz,
+ int offset,
+ Instruction instruction,
+ Instruction replacementInstruction)
+ {
+ // Pop unneeded stack entries if necessary.
+ int popCount =
+ instruction.stackPopCount(clazz) -
+ replacementInstruction.stackPopCount(clazz);
+
+ insertPopInstructions(offset, popCount);
+
+ if (DEBUG) System.out.println(" Replacing instruction "+instruction.toString(offset)+" -> "+replacementInstruction.toString()+(popCount == 0 ? "" : " ("+popCount+" pops)"));
+
+ codeAttributeEditor.replaceInstruction(offset, replacementInstruction);
+
+ // Visit the instruction, if required.
+ if (extraInstructionVisitor != null)
+ {
+ // Note: we're not passing the right arguments for now, knowing that
+ // they aren't used anyway.
+ instruction.accept(clazz, null, null, offset, extraInstructionVisitor);
+ }
+ }
+
+
+ /**
+ * Deletes the instruction at the given offset, popping any now unused
+ * stack entries.
+ */
+ private void deleteInstruction(Clazz clazz,
+ int offset,
+ Instruction instruction)
+ {
+ // Pop unneeded stack entries if necessary.
+ //int popCount = instruction.stackPopCount(clazz);
+ //
+ //insertPopInstructions(offset, popCount);
+ //
+ //if (DEBUG) System.out.println(" Deleting instruction "+instruction.toString(offset)+(popCount == 0 ? "" : " ("+popCount+" pops)"));
+
+ if (DEBUG) System.out.println(" Deleting instruction "+instruction.toString(offset));
+
+ codeAttributeEditor.deleteInstruction(offset);
+
+ // Visit the instruction, if required.
+ if (extraInstructionVisitor != null)
+ {
+ // Note: we're not passing the right arguments for now, knowing that
+ // they aren't used anyway.
+ instruction.accept(clazz, null, null, offset, extraInstructionVisitor);
+ }
+ }
+
+
+ /**
+ * Pops the given number of stack entries before the instruction at the
+ * given offset.
+ */
+ private void insertPopInstructions(int offset, int popCount)
+ {
+ switch (popCount)
+ {
+ case 0:
+ {
+ break;
+ }
+ case 1:
+ {
+ // Insert a single pop instruction.
+ Instruction popInstruction =
+ new SimpleInstruction(InstructionConstants.OP_POP);
+
+ codeAttributeEditor.insertBeforeInstruction(offset,
+ popInstruction);
+ break;
+ }
+ case 2:
+ {
+ // Insert a single pop2 instruction.
+ Instruction popInstruction =
+ new SimpleInstruction(InstructionConstants.OP_POP2);
+
+ codeAttributeEditor.insertBeforeInstruction(offset,
+ popInstruction);
+ break;
+ }
+ default:
+ {
+ // Insert the specified number of pop instructions.
+ Instruction[] popInstructions =
+ new Instruction[popCount / 2 + popCount % 2];
+
+ Instruction popInstruction =
+ new SimpleInstruction(InstructionConstants.OP_POP2);
+
+ for (int index = 0; index < popCount / 2; index++)
+ {
+ popInstructions[index] = popInstruction;
+ }
+
+ if (popCount % 2 == 1)
+ {
+ popInstruction =
+ new SimpleInstruction(InstructionConstants.OP_POP);
+
+ popInstructions[popCount / 2] = popInstruction;
+ }
+
+ codeAttributeEditor.insertBeforeInstruction(offset,
+ popInstructions);
+ break;
+ }
+ }
+ }
+
+
+ /**
+ * Replaces aconst_null producers of the consumer of the top stack entry
+ * at the given offset by iconst_0.
+ */
+ private void replaceNullStackEntryProducers(Clazz clazz,
+ Method method,
+ CodeAttribute codeAttribute,
+ int consumerOffset)
+ {
+ replaceNullStackEntryProducers(clazz, method, codeAttribute, consumerOffset, 0);
+ }
+
+
+ /**
+ * Replaces aconst_null producers of the specified stack entry by
+ * iconst_0.
+ */
+ private void replaceNullStackEntryProducers(Clazz clazz,
+ Method method,
+ CodeAttribute codeAttribute,
+ int consumerOffset,
+ int stackEntryIndex)
+ {
+ InstructionOffsetValue producerOffsets =
+ partialEvaluator.getStackBefore(consumerOffset).getTopActualProducerValue(stackEntryIndex).instructionOffsetValue();
+
+ for (int index = 0; index < producerOffsets.instructionOffsetCount(); index++)
+ {
+ int producerOffset = producerOffsets.instructionOffset(index);
+
+ // TODO: A method might be pushing the null constant.
+ if (producerOffset >= 0 &&
+ codeAttribute.code[producerOffset] == InstructionConstants.OP_ACONST_NULL)
+ {
+ // Replace pushing null by pushing 0.
+ replaceInstruction(clazz,
+ producerOffset,
+ new SimpleInstruction(InstructionConstants.OP_ACONST_NULL),
+ new SimpleInstruction(InstructionConstants.OP_ICONST_0));
+ }
+ }
+ }
+
+
+ /**
+ * Replaces aconst_null/astore producers of the specified reference variable by
+ * iconst_0/istore.
+ */
+ private void replaceNullVariableProducers(Clazz clazz,
+ Method method,
+ CodeAttribute codeAttribute,
+ int consumerOffset,
+ int variableIndex)
+ {
+ InstructionOffsetValue producerOffsets =
+ partialEvaluator.getVariablesBefore(consumerOffset).getProducerValue(variableIndex).instructionOffsetValue();
+
+ for (int index = 0; index < producerOffsets.instructionOffsetCount(); index++)
+ {
+ int producerOffset = producerOffsets.instructionOffset(index);
+
+ if (producerOffset >= 0 &&
+ partialEvaluator.getVariablesAfter(producerOffset).getValue(variableIndex).referenceValue().isNull() == Value.ALWAYS)
+ {
+ // Replace loading null by loading 0.
+ replaceInstruction(clazz,
+ producerOffset,
+ new VariableInstruction(InstructionConstants.OP_ASTORE, variableIndex),
+ new VariableInstruction(InstructionConstants.OP_ISTORE, variableIndex));
+
+ // Replace pushing null by pushing 0.
+ replaceNullStackEntryProducers(clazz, method, codeAttribute, producerOffset);
+ }
+ }
+ }
+}