public void visitLibraryClass(LibraryClass libraryClass) { if (shouldBeMarkedAsUsed(libraryClass)) { markAsUsed(libraryClass); // We're not going to analyze all library code. We're assuming that // if this class is being used, all of its methods will be used as // well. We'll mark them as such (here and in all subclasses). // Mark the superclass. Clazz superClass = libraryClass.superClass; if (superClass != null) { superClass.accept(this); } // Mark the interfaces. Clazz[] interfaceClasses = libraryClass.interfaceClasses; if (interfaceClasses != null) { for (int index = 0; index < interfaceClasses.length; index++) { if (interfaceClasses[index] != null) { interfaceClasses[index].accept(this); } } } // Mark all methods. libraryClass.methodsAccept(this); } }
/** * Writes the given word to the given writer, after having adapted it, based on the renamed class * names. */ private void writeUpdatedWord(Writer writer, String word) throws IOException { if (word.length() > 0) { String newWord = word; boolean containsDots = word.indexOf('.') >= 0; // Replace dots by forward slashes. String className = containsDots ? word.replace('.', ClassConstants.INTERNAL_PACKAGE_SEPARATOR) : word; // Find the class corrsponding to the word. Clazz clazz = classPool.getClass(className); if (clazz != null) { // Update the word if necessary. String newClassName = clazz.getName(); if (!className.equals(newClassName)) { // Replace forward slashes by dots. newWord = containsDots ? newClassName.replace(ClassConstants.INTERNAL_PACKAGE_SEPARATOR, '.') : newClassName; } } writer.write(newWord); } }
public int stackPopCount(Clazz clazz) { int stackPopCount = super.stackPopCount(clazz); // Some special cases. switch (opcode) { case InstructionConstants.OP_MULTIANEWARRAY: // For each dimension, an integer size is popped from the stack. stackPopCount += constant; break; case InstructionConstants.OP_PUTSTATIC: case InstructionConstants.OP_PUTFIELD: // The field value is be popped from the stack. clazz.constantPoolEntryAccept(constantIndex, this); stackPopCount += typeStackDelta; break; case InstructionConstants.OP_INVOKEVIRTUAL: case InstructionConstants.OP_INVOKESPECIAL: case InstructionConstants.OP_INVOKESTATIC: case InstructionConstants.OP_INVOKEINTERFACE: // The some parameters may be popped from the stack. clazz.constantPoolEntryAccept(constantIndex, this); stackPopCount += parameterStackDelta; break; } return stackPopCount; }
public void visitConstantInstruction( Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction) { byte opcode = constantInstruction.opcode; // Check for instructions that involve fields. switch (opcode) { case InstructionConstants.OP_LDC: case InstructionConstants.OP_LDC_W: // Mark the field, if any, as being read from and written to. reading = true; writing = true; clazz.constantPoolEntryAccept(constantInstruction.constantIndex, this); break; case InstructionConstants.OP_GETSTATIC: case InstructionConstants.OP_GETFIELD: // Mark the field as being read from. reading = true; writing = false; clazz.constantPoolEntryAccept(constantInstruction.constantIndex, this); break; case InstructionConstants.OP_PUTSTATIC: case InstructionConstants.OP_PUTFIELD: // Mark the field as being written to. reading = false; writing = true; clazz.constantPoolEntryAccept(constantInstruction.constantIndex, this); break; } }
/** Applies the given visitor to all referenced classes. */ public void referencedClassesAccept(ClassVisitor classVisitor) { if (referencedClasses != null) { for (int index = 0; index < referencedClasses.length; index++) { Clazz referencedClass = referencedClasses[index]; if (referencedClass != null) { referencedClass.accept(classVisitor); } } } }
public void visitInnerClassesInfo(Clazz clazz, InnerClassesInfo innerClassesInfo) { // At this point, we only mark outer classes of this class. // Inner class can be marked later, by InnerUsageMarker. if (innerClassesInfo.u2innerClassIndex != 0 && clazz.getName().equals(clazz.getClassName(innerClassesInfo.u2innerClassIndex))) { markAsUsed(innerClassesInfo); innerClassesInfo.innerClassConstantAccept(clazz, this); innerClassesInfo.outerClassConstantAccept(clazz, this); innerClassesInfo.innerNameConstantAccept(clazz, this); } }
public void visitAnyRefConstant(Clazz clazz, RefConstant refConstant) { Clazz referencedClass = refConstant.referencedClass; if (referencedClass != null && (referencedClass.getAccessFlags() & ClassConstants.INTERNAL_ACC_PUBLIC) == 0) { setInvokesPackageVisibleMembers(clazz); } Member referencedMember = refConstant.referencedMember; if (referencedMember != null && (referencedMember.getAccessFlags() & (ClassConstants.INTERNAL_ACC_PUBLIC | ClassConstants.INTERNAL_ACC_PRIVATE)) == 0) { setInvokesPackageVisibleMembers(clazz); } }
public void read(DataEntry dataEntry) throws IOException { try { // Get the input stream. InputStream inputStream = dataEntry.getInputStream(); // Wrap it into a data input stream. DataInputStream dataInputStream = new DataInputStream(inputStream); // Create a Clazz representation. Clazz clazz; if (isLibrary) { clazz = new LibraryClass(); clazz.accept( new LibraryClassReader( dataInputStream, skipNonPublicLibraryClasses, skipNonPublicLibraryClassMembers)); } else { clazz = new ProgramClass(); clazz.accept(new ProgramClassReader(dataInputStream)); } // Apply the visitor, if we have a real class. String className = clazz.getName(); if (className != null) { if (!dataEntry .getName() .replace(File.pathSeparatorChar, ClassConstants.PACKAGE_SEPARATOR) .equals(className + ClassConstants.CLASS_FILE_EXTENSION) && warningPrinter != null) { warningPrinter.print( className, "Warning: class [" + dataEntry.getName() + "] unexpectedly contains class [" + ClassUtil.externalClassName(className) + "]"); } clazz.accept(classVisitor); } dataEntry.closeInputStream(); } catch (Exception ex) { throw (IOException) new IOException( "Can't process class [" + dataEntry.getName() + "] (" + ex.getMessage() + ")") .initCause(ex); } }
public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) { // DEBUG = // clazz.getName().equals("abc/Def") && // method.getName(clazz).equals("abc"); // TODO: Remove this when the code 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 computing stack sizes:"); System.err.println(" Class = [" + clazz.getName() + "]"); System.err.println( " Method = [" + method.getName(clazz) + method.getDescriptor(clazz) + "]"); System.err.println( " Exception = [" + ex.getClass().getName() + "] (" + ex.getMessage() + ")"); if (DEBUG) { method.accept(clazz, new ClassPrinter()); } throw ex; } }
public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) { // DEBUG = // clazz.getName().equals("abc/Def") && // method.getName(clazz).equals("abc"); // The minimum variable size is determined by the arguments. codeAttribute.u2maxLocals = ClassUtil.internalMethodParameterSize(method.getDescriptor(clazz), method.getAccessFlags()); if (DEBUG) { System.out.println( "VariableSizeUpdater: " + clazz.getName() + "." + method.getName(clazz) + method.getDescriptor(clazz)); System.out.println(" Max locals: " + codeAttribute.u2maxLocals + " <- parameters"); } // Go over all instructions. codeAttribute.instructionsAccept(clazz, method, this); // Remove the unused variables of the attributes. codeAttribute.attributesAccept(clazz, method, variableCleaner); }
/** * Marks the hierarchy of implementing or overriding methods corresponding to the given method, if * any. */ protected void markMethodHierarchy(Clazz clazz, Method method) { int accessFlags = method.getAccessFlags(); if ((accessFlags & (ClassConstants.ACC_PRIVATE | ClassConstants.ACC_STATIC)) == 0 && !ClassUtil.isInitializer(method.getName(clazz))) { // We can skip private and static methods in the hierarchy, and // also abstract methods, unless they might widen a current // non-public access. int requiredUnsetAccessFlags = ClassConstants.ACC_PRIVATE | ClassConstants.ACC_STATIC | ((accessFlags & ClassConstants.ACC_PUBLIC) == 0 ? 0 : ClassConstants.ACC_ABSTRACT); clazz.accept( new ConcreteClassDownTraveler( new ClassHierarchyTraveler( true, true, false, true, new NamedMethodVisitor( method.getName(clazz), method.getDescriptor(clazz), new MemberAccessFilter(0, requiredUnsetAccessFlags, this))))); } }
public void visitCodeAttribute0(Clazz clazz, Method method, CodeAttribute codeAttribute) { if (DEBUG) { System.out.println( "StackSizeComputer: " + clazz.getName() + "." + method.getName(clazz) + method.getDescriptor(clazz)); } // Try to reuse the previous array. int codeLength = codeAttribute.u4codeLength; if (evaluated.length < codeLength) { evaluated = new boolean[codeLength]; stackSizes = new int[codeLength]; } else { Arrays.fill(evaluated, 0, codeLength, false); } // The initial stack is always empty. stackSize = 0; maxStackSize = 0; // Evaluate the instruction block starting at the entry point of the method. evaluateInstructionBlock(clazz, method, codeAttribute, 0); // Evaluate the exception handlers. codeAttribute.exceptionsAccept(clazz, method, this); }
/** * Marks the hierarchy of implementing or overriding methods corresponding to the given method, if * any. */ protected void markMethodHierarchy(Clazz clazz, Method method) { int accessFlags = method.getAccessFlags(); if ((accessFlags & (ClassConstants.ACC_PRIVATE | ClassConstants.ACC_STATIC)) == 0 && !ClassUtil.isInitializer(method.getName(clazz))) { // We can skip private and static methods in the hierarchy, and // also abstract methods, unless they might widen a current // non-public access. int requiredUnsetAccessFlags = ClassConstants.ACC_PRIVATE | ClassConstants.ACC_STATIC | ((accessFlags & ClassConstants.ACC_PUBLIC) == 0 ? 0 : ClassConstants.ACC_ABSTRACT); // Mark default implementations in interfaces down the hierarchy. // TODO: This may be premature if there aren't any concrete implementing classes. clazz.accept( new ClassAccessFilter( ClassConstants.ACC_ABSTRACT, 0, new ClassHierarchyTraveler( false, false, false, true, new ProgramClassFilter( new ClassAccessFilter( ClassConstants.ACC_ABSTRACT, 0, new NamedMethodVisitor( method.getName(clazz), method.getDescriptor(clazz), new MemberAccessFilter( 0, requiredUnsetAccessFlags, defaultMethodUsageMarker))))))); // Mark other implementations. clazz.accept( new ConcreteClassDownTraveler( new ClassHierarchyTraveler( true, true, false, true, new NamedMethodVisitor( method.getName(clazz), method.getDescriptor(clazz), new MemberAccessFilter(0, requiredUnsetAccessFlags, this))))); } }
public void visitSignatureAttribute(Clazz clazz, SignatureAttribute signatureAttribute) { // Process the generic definitions, superclass, and implemented // interfaces. String signature = clazz.getString(signatureAttribute.u2signatureIndex); // Count the signature types. InternalTypeEnumeration internalTypeEnumeration = new InternalTypeEnumeration(signature); int count = 0; int interfacesCount = -1; while (internalTypeEnumeration.hasMoreTypes()) { String internalType = internalTypeEnumeration.nextType(); count++; if (ClassUtil.isInternalClassType(internalType)) { interfacesCount++; } } // Put the signature types in an array. internalTypeEnumeration = new InternalTypeEnumeration(signature); String[] internalTypes = new String[count]; for (int index = 0; index < count; index++) { String internalType = internalTypeEnumeration.nextType(); internalTypes[index] = internalType; } // Sort the interface types in the array. Arrays.sort(internalTypes, count - interfacesCount, count); // Recompose the signature types in a string. StringBuffer newSignatureBuffer = new StringBuffer(); for (int index = 0; index < count; index++) { // Is this not an interface type, or an interface type that isn't // a duplicate of the previous interface type? if (index < count - interfacesCount || !internalTypes[index].equals(internalTypes[index - 1])) { newSignatureBuffer.append(internalTypes[index]); } } String newSignature = newSignatureBuffer.toString(); // Did the signature change? if (!newSignature.equals(signature)) { // Update the signature. ((Utf8Constant) ((ProgramClass) clazz).constantPool[signatureAttribute.u2signatureIndex]) .setString(newSignatureBuffer.toString()); // Clear the referenced classes. // TODO: Properly update the referenced classes. signatureAttribute.referencedClasses = null; } }
public String toString() { return "certain=" + certain + ", depth=" + depth + ": " + reason + (clazz != null ? clazz.getName() : "(none)") + ": " + (member != null ? member.getName(clazz) : "(none)"); }
public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) { targetClass.hierarchyAccept( visitThisMember, visitSuperMembers, visitInterfaceMembers, visitOverridingMembers, new NamedMethodVisitor( programMethod.getName(programClass), programMethod.getDescriptor(programClass), memberVisitor)); }
public void visitLibraryField(LibraryClass libraryClass, LibraryField libraryField) { targetClass.hierarchyAccept( visitThisMember, visitSuperMembers, visitInterfaceMembers, visitOverridingMembers, new NamedFieldVisitor( libraryField.getName(libraryClass), libraryField.getDescriptor(libraryClass), memberVisitor)); }
public void visitInvokeDynamicConstant(Clazz clazz, InvokeDynamicConstant invokeDynamicConstant) { if (shouldBeMarkedAsUsed(invokeDynamicConstant)) { markAsUsed(invokeDynamicConstant); markConstant(clazz, invokeDynamicConstant.u2nameAndTypeIndex); // Mark the bootstrap methods attribute. clazz.attributesAccept( new MyBootStrapMethodUsageMarker(invokeDynamicConstant.u2bootstrapMethodAttributeIndex)); } }
public void visitConstantInstruction( Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction) { markConstant(clazz, constantInstruction.constantIndex); // Also mark the parameterless constructor of the class, in case the // string constant or class constant is being used in a Class.forName // or a .class construct. clazz.constantPoolEntryAccept( constantInstruction.constantIndex, parameterlessConstructorMarker); }
public void visitSignatureAttribute( Clazz clazz, Method method, SignatureAttribute signatureAttribute) { // Compute the new signature. String signature = clazz.getString(signatureAttribute.u2signatureIndex); String newSignature = shrinkDescriptor(method, signature); // Update the signature. signatureAttribute.u2signatureIndex = new ConstantPoolEditor((ProgramClass) clazz).addUtf8Constant(newSignature); // Update the referenced classes. signatureAttribute.referencedClasses = shrinkReferencedClasses(method, signature, signatureAttribute.referencedClasses); }
/** Returns if the number of superclasses of the given class in the given set of classes. */ private int superClassCount(Clazz subClass, Set classes) { int count = 0; Iterator iterator = classes.iterator(); while (iterator.hasNext()) { Clazz clazz = (Clazz) iterator.next(); if (subClass.extendsOrImplements(clazz)) { count++; } } return count; }
public int stackPushCount(Clazz clazz) { int stackPushCount = super.stackPushCount(clazz); // Some special cases. switch (opcode) { case InstructionConstants.OP_GETSTATIC: case InstructionConstants.OP_GETFIELD: case InstructionConstants.OP_INVOKEVIRTUAL: case InstructionConstants.OP_INVOKESPECIAL: case InstructionConstants.OP_INVOKESTATIC: case InstructionConstants.OP_INVOKEINTERFACE: // The field value or a return value may be pushed onto the stack. clazz.constantPoolEntryAccept(constantIndex, this); stackPushCount += typeStackDelta; break; } return stackPushCount; }
/** * Marks the hierarchy of implementing or overriding methods corresponding to the given method, if * any. */ protected void markMethodHierarchy(Clazz clazz, Method method) { if ((method.getAccessFlags() & (ClassConstants.INTERNAL_ACC_PRIVATE | ClassConstants.INTERNAL_ACC_STATIC)) == 0) { clazz.accept( new ConcreteClassDownTraveler( new ClassHierarchyTraveler( true, true, false, true, new NamedMethodVisitor( method.getName(clazz), method.getDescriptor(clazz), new MemberAccessFilter( 0, ClassConstants.INTERNAL_ACC_PRIVATE | ClassConstants.INTERNAL_ACC_STATIC | ClassConstants.INTERNAL_ACC_ABSTRACT, this))))); } }
public void visitMethodHandleConstant(Clazz clazz, MethodHandleConstant methodHandleConstant) { clazz.constantPoolEntryAccept(methodHandleConstant.u2referenceIndex, methodrefConstantVisitor); }
/** * Returns the most specific common superclass or interface of the given classes. * * @param class1 the first class. * @param class2 the second class. * @param interfaces specifies whether to look for a superclass or for an interface. * @return the common class. */ private Clazz findCommonClass(Clazz class1, Clazz class2, boolean interfaces) { // Collect the superclasses or the interfaces of this class. Set superClasses1 = new HashSet(); class1.hierarchyAccept( !interfaces, !interfaces, interfaces, false, new ClassCollector(superClasses1)); int superClasses1Count = superClasses1.size(); if (superClasses1Count == 0) { if (interfaces) { return null; } else if (class1.getSuperName() != null) { throw new IllegalArgumentException( "Can't find any super classes of [" + class1.getName() + "] (not even immediate super class [" + class1.getSuperName() + "])"); } } // Collect the superclasses or the interfaces of the other class. Set superClasses2 = new HashSet(); class2.hierarchyAccept( !interfaces, !interfaces, interfaces, false, new ClassCollector(superClasses2)); int superClasses2Count = superClasses2.size(); if (superClasses2Count == 0) { if (interfaces) { return null; } else if (class2.getSuperName() != null) { throw new IllegalArgumentException( "Can't find any super classes of [" + class2.getName() + "] (not even immediate super class [" + class2.getSuperName() + "])"); } } if (DEBUG) { System.out.println( "ReferenceValue.generalize this [" + class1.getName() + "] with other [" + class2.getName() + "] (interfaces = " + interfaces + ")"); System.out.println(" This super classes: " + superClasses1); System.out.println(" Other super classes: " + superClasses2); } // Find the common superclasses. superClasses1.retainAll(superClasses2); if (DEBUG) { System.out.println(" Common super classes: " + superClasses1); } if (interfaces && superClasses1.isEmpty()) { return null; } // Find a class that is a subclass of all common superclasses, // or that at least has the maximum number of common superclasses. Clazz commonClass = null; int maximumSuperClassCount = -1; // Go over all common superclasses to find it. In case of // multiple subclasses, keep the lowest one alphabetically, // in order to ensure that the choice is deterministic. Iterator commonSuperClasses = superClasses1.iterator(); while (commonSuperClasses.hasNext()) { Clazz commonSuperClass = (Clazz) commonSuperClasses.next(); int superClassCount = superClassCount(commonSuperClass, superClasses1); if (maximumSuperClassCount < superClassCount || (maximumSuperClassCount == superClassCount && commonClass != null && commonClass.getName().compareTo(commonSuperClass.getName()) > 0)) { commonClass = commonSuperClass; maximumSuperClassCount = superClassCount; } } if (commonClass == null) { throw new IllegalArgumentException( "Can't find common super class of [" + class1.getName() + "] (with " + superClasses1Count + " known super classes) and [" + class2.getName() + "] (with " + superClasses2Count + " known super classes)"); } if (DEBUG) { System.out.println(" Best common class: [" + commonClass.getName() + "]"); } return commonClass; }
/** * Applies the given class visitor to this mark's class, if any, and if this mark doesn't have a * member. */ public void acceptClassVisitor(ClassVisitor classVisitor) { if (clazz != null && member == null) { clazz.accept(classVisitor); } }
/** Returns whether this is mark is caused by the given class. */ public boolean isCausedBy(Clazz clazz) { return clazz.equals(this.clazz); }
public int instanceOf(String otherType, Clazz otherReferencedClass) { String thisType = this.type; // If this type is null, it is never an instance of any class. if (thisType == null) { return NEVER; } // Start taking into account the type dimensions. int thisDimensionCount = ClassUtil.internalArrayTypeDimensionCount(thisType); int otherDimensionCount = ClassUtil.internalArrayTypeDimensionCount(otherType); int commonDimensionCount = Math.min(thisDimensionCount, otherDimensionCount); // Strip any common array prefixes. thisType = thisType.substring(commonDimensionCount); otherType = otherType.substring(commonDimensionCount); // If either stripped type is a primitive type, we can tell right away. if (commonDimensionCount > 0 && (ClassUtil.isInternalPrimitiveType(thisType.charAt(0)) || ClassUtil.isInternalPrimitiveType(otherType.charAt(0)))) { return !thisType.equals(otherType) ? NEVER : mayBeNull ? MAYBE : ALWAYS; } // Strip the class type prefix and suffix of this type, if any. if (thisDimensionCount == commonDimensionCount) { thisType = ClassUtil.internalClassNameFromClassType(thisType); } // Strip the class type prefix and suffix of the other type, if any. if (otherDimensionCount == commonDimensionCount) { otherType = ClassUtil.internalClassNameFromClassType(otherType); } // If this type is an array type, and the other type is not // java.lang.Object, java.lang.Cloneable, or java.io.Serializable, // this type can never be an instance. if (thisDimensionCount > otherDimensionCount && !ClassUtil.isInternalArrayInterfaceName(otherType)) { return NEVER; } // If the other type is an array type, and this type is not // java.lang.Object, java.lang.Cloneable, or java.io.Serializable, // this type can never be an instance. if (thisDimensionCount < otherDimensionCount && !ClassUtil.isInternalArrayInterfaceName(thisType)) { return NEVER; } // If this type may be null, it might not be an instance of any class. if (mayBeNull) { return MAYBE; } // If this type is equal to the other type, or if the other type is // java.lang.Object, this type is always an instance. if (thisType.equals(otherType) || ClassConstants.NAME_JAVA_LANG_OBJECT.equals(otherType)) { return ALWAYS; } // If this type is an array type, it's ok. if (thisDimensionCount > otherDimensionCount) { return ALWAYS; } // If the other type is an array type, it might be ok. if (thisDimensionCount < otherDimensionCount) { return MAYBE; } // If the value extends the type, we're sure. return referencedClass != null && otherReferencedClass != null && referencedClass.extendsOrImplements(otherReferencedClass) ? ALWAYS : MAYBE; }
/** * Evaluates a block of instructions that hasn't been handled before, starting at the given offset * and ending at a branch instruction, a return instruction, or a throw instruction. Branch * instructions are handled recursively. */ private void evaluateInstructionBlock( Clazz clazz, Method method, CodeAttribute codeAttribute, int instructionOffset) { if (DEBUG) { if (evaluated[instructionOffset]) { System.out.println("-- (instruction block at " + instructionOffset + " already evaluated)"); } else { System.out.println("-- instruction block:"); } } // Remember the initial stack size. int initialStackSize = stackSize; // Remember the maximum stack size. if (maxStackSize < stackSize) { maxStackSize = stackSize; } // Evaluate any instructions that haven't been evaluated before. while (!evaluated[instructionOffset]) { // Mark the instruction as evaluated. evaluated[instructionOffset] = true; Instruction instruction = InstructionFactory.create(codeAttribute.code, instructionOffset); if (DEBUG) { int stackPushCount = instruction.stackPushCount(clazz); int stackPopCount = instruction.stackPopCount(clazz); System.out.println( "[" + instructionOffset + "]: " + stackSize + " - " + stackPopCount + " + " + stackPushCount + " = " + (stackSize + stackPushCount - stackPopCount) + ": " + instruction.toString(instructionOffset)); } // Compute the instruction's effect on the stack size. stackSize -= instruction.stackPopCount(clazz); if (stackSize < 0) { throw new IllegalArgumentException( "Stack size becomes negative after instruction " + instruction.toString(instructionOffset) + " in [" + clazz.getName() + "." + method.getName(clazz) + method.getDescriptor(clazz) + "]"); } stackSizes[instructionOffset] = stackSize += instruction.stackPushCount(clazz); // Remember the maximum stack size. if (maxStackSize < stackSize) { maxStackSize = stackSize; } // Remember the next instruction offset. int nextInstructionOffset = instructionOffset + instruction.length(instructionOffset); // Visit the instruction, in order to handle branches. instruction.accept(clazz, method, codeAttribute, instructionOffset, this); // Stop evaluating after a branch. if (exitInstructionBlock) { break; } // Continue with the next instruction. instructionOffset = nextInstructionOffset; if (DEBUG) { if (evaluated[instructionOffset]) { System.out.println("-- (instruction at " + instructionOffset + " already evaluated)"); } } } // Restore the stack size for possible subsequent instruction blocks. this.stackSize = initialStackSize; }
public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) { // DEBUG = // clazz.getName().equals("abc/Def") && // method.getName(clazz).equals("abc"); if (DEBUG) { method.accept(clazz, new ClassPrinter()); } branchTargetFinder.visitCodeAttribute(clazz, method, codeAttribute); // Don't bother if there aren't any subroutines anyway. if (!containsSubroutines(codeAttribute)) { return; } if (DEBUG) { System.out.println( "SubroutineInliner: processing [" + clazz.getName() + "." + method.getName(clazz) + method.getDescriptor(clazz) + "]"); } // Append the body of the code. codeAttributeComposer.reset(); codeAttributeComposer.beginCodeFragment(codeAttribute.u4codeLength); // Copy the non-subroutine instructions. int offset = 0; while (offset < codeAttribute.u4codeLength) { Instruction instruction = InstructionFactory.create(codeAttribute.code, offset); int instructionLength = instruction.length(offset); // Is this returning subroutine? if (branchTargetFinder.isSubroutine(offset) && branchTargetFinder.isSubroutineReturning(offset)) { // Skip the subroutine. if (DEBUG) { System.out.println( " Skipping original subroutine instruction " + instruction.toString(offset)); } // Append a label at this offset instead. codeAttributeComposer.appendLabel(offset); } else { // Copy the instruction, inlining any subroutine call recursively. instruction.accept(clazz, method, codeAttribute, offset, this); } offset += instructionLength; } // Copy the exceptions. Note that exceptions with empty try blocks // are automatically removed. codeAttribute.exceptionsAccept(clazz, method, subroutineExceptionInliner); if (DEBUG) { System.out.println(" Appending label after code at [" + offset + "]"); } // Append a label just after the code. codeAttributeComposer.appendLabel(codeAttribute.u4codeLength); // End and update the code attribute. codeAttributeComposer.endCodeFragment(); codeAttributeComposer.visitCodeAttribute(clazz, method, codeAttribute); if (DEBUG) { method.accept(clazz, new ClassPrinter()); } }