/** * 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 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 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); }
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); }
public String toString() { return "certain=" + certain + ", depth=" + depth + ": " + reason + (clazz != null ? clazz.getName() : "(none)") + ": " + (member != null ? member.getName(clazz) : "(none)"); }
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 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); } }
/** * 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()); } }
/** * 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; }
public ReferenceValue generalize(TypedReferenceValue other) { // If both types are identical, the generalization is the same too. if (this.equals(other)) { return this; } String thisType = this.type; String otherType = other.type; // If both types are nul, the generalization is null too. if (thisType == null && otherType == null) { return ValueFactory.REFERENCE_VALUE_NULL; } // If this type is null, the generalization is the other type, maybe null. if (thisType == null) { return other.generalizeMayBeNull(true); } // If the other type is null, the generalization is this type, maybe null. if (otherType == null) { return this.generalizeMayBeNull(true); } boolean mayBeNull = this.mayBeNull || other.mayBeNull; // If the two types are equal, the generalization remains the same, maybe null. if (thisType.equals(otherType)) { return typedReferenceValue(this, mayBeNull); } // Start taking into account the type dimensions. int thisDimensionCount = ClassUtil.internalArrayTypeDimensionCount(thisType); int otherDimensionCount = ClassUtil.internalArrayTypeDimensionCount(otherType); int commonDimensionCount = Math.min(thisDimensionCount, otherDimensionCount); if (thisDimensionCount == otherDimensionCount) { // See if we can take into account the referenced classes. Clazz thisReferencedClass = this.referencedClass; Clazz otherReferencedClass = other.referencedClass; if (thisReferencedClass != null && otherReferencedClass != null) { // Is one class simply an extension of the other one? if (thisReferencedClass.extendsOrImplements(otherReferencedClass)) { return typedReferenceValue(other, mayBeNull); } if (otherReferencedClass.extendsOrImplements(thisReferencedClass)) { return typedReferenceValue(this, mayBeNull); } // Do the classes have a non-trivial common superclass? Clazz commonClass = findCommonClass(thisReferencedClass, otherReferencedClass, false); if (commonClass.getName().equals(ClassConstants.NAME_JAVA_LANG_OBJECT)) { // Otherwise, do the classes have a common interface? Clazz commonInterface = findCommonClass(thisReferencedClass, otherReferencedClass, true); if (commonInterface != null) { commonClass = commonInterface; } } return new TypedReferenceValue( commonDimensionCount == 0 ? commonClass.getName() : ClassUtil.internalArrayTypeFromClassName( commonClass.getName(), commonDimensionCount), commonClass, mayBeNull); } } else if (thisDimensionCount > otherDimensionCount) { // See if the other type is an interface type of arrays. if (ClassUtil.isInternalArrayInterfaceName( ClassUtil.internalClassNameFromClassType(otherType))) { return typedReferenceValue(other, mayBeNull); } } else if (thisDimensionCount < otherDimensionCount) { // See if this type is an interface type of arrays. if (ClassUtil.isInternalArrayInterfaceName( ClassUtil.internalClassNameFromClassType(thisType))) { return typedReferenceValue(this, mayBeNull); } } // Reduce the common dimension count if either type is an array of // primitives type of this dimension. if (commonDimensionCount > 0 && (ClassUtil.isInternalPrimitiveType(otherType.charAt(commonDimensionCount))) || ClassUtil.isInternalPrimitiveType(thisType.charAt(commonDimensionCount))) { commonDimensionCount--; } // Fall back on a basic Object or array of Objects type. return commonDimensionCount != 0 ? new TypedReferenceValue( ClassUtil.internalArrayTypeFromClassName( ClassConstants.NAME_JAVA_LANG_OBJECT, commonDimensionCount), null, mayBeNull) : mayBeNull ? ValueFactory.REFERENCE_VALUE_JAVA_LANG_OBJECT_MAYBE_NULL : ValueFactory.REFERENCE_VALUE_JAVA_LANG_OBJECT_NOT_NULL; }
public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) { // Get the original parameter size that was saved. int oldParameterSize = ParameterUsageMarker.getParameterSize(method); // Compute the new parameter size from the shrunk descriptor. int newParameterSize = ClassUtil.internalMethodParameterSize(method.getDescriptor(clazz), method.getAccessFlags()); if (oldParameterSize > newParameterSize) { // Get the total size of the local variable frame. int maxLocals = codeAttribute.u2maxLocals; if (DEBUG) { System.out.println( "ParameterShrinker: " + clazz.getName() + "." + method.getName(clazz) + method.getDescriptor(clazz)); System.out.println(" Old parameter size = " + oldParameterSize); System.out.println(" New parameter size = " + newParameterSize); System.out.println(" Max locals = " + maxLocals); } // Create a variable map. int[] variableMap = new int[maxLocals]; // Move unused parameters right after the parameter block. int usedParameterIndex = 0; int unusedParameterIndex = newParameterSize; for (int parameterIndex = 0; parameterIndex < oldParameterSize; parameterIndex++) { // Is the variable required as a parameter? if (ParameterUsageMarker.isParameterUsed(method, parameterIndex)) { // Keep the variable as a parameter. variableMap[parameterIndex] = usedParameterIndex++; } else { if (DEBUG) { System.out.println(" Deleting parameter #" + parameterIndex); } // Shift the variable to the unused parameter block, // in case it is still used as a variable. variableMap[parameterIndex] = unusedParameterIndex++; // Visit the method, if required. if (extraVariableMemberVisitor != null) { method.accept(clazz, extraVariableMemberVisitor); } } } // Fill out the remainder of the map. for (int variableIndex = oldParameterSize; variableIndex < maxLocals; variableIndex++) { variableMap[variableIndex] = variableIndex; } // Set the map. variableRemapper.setVariableMap(variableMap); // Remap the variables. variableRemapper.visitCodeAttribute(clazz, method, codeAttribute); } }
/** Returns the generalization of this ReferenceValue and the given other ReferenceValue. */ public ReferenceValue generalize(ReferenceValue other) { // If both types are identical, the generalization is the same too. if (this.equals(other)) { return this; } String thisType = this.type; String otherType = other.type; // If both types are nul, the generalization is null too. if (thisType == null && otherType == null) { return ValueFactory.REFERENCE_VALUE_NULL; } // If this type is null, the generalization is the other type, maybe null. if (thisType == null) { return other.generalizeMayBeNull(true); } // If the other type is null, the generalization is this type, maybe null. if (otherType == null) { return this.generalizeMayBeNull(true); } boolean mayBeNull = this.mayBeNull || other.mayBeNull; // If the two types are equal, the generalization remains the same, maybe null. if (thisType.equals(otherType)) { return this.generalizeMayBeNull(mayBeNull); } // Start taking into account the type dimensions. int thisDimensionCount = ClassUtil.internalArrayTypeDimensionCount(thisType); int otherDimensionCount = ClassUtil.internalArrayTypeDimensionCount(otherType); int commonDimensionCount = Math.min(thisDimensionCount, otherDimensionCount); if (thisDimensionCount == otherDimensionCount) { // See if we can take into account the referenced classes. Clazz thisReferencedClass = this.referencedClass; Clazz otherReferencedClass = other.referencedClass; if (thisReferencedClass != null && otherReferencedClass != null) { if (thisReferencedClass.extendsOrImplements(otherReferencedClass)) { return other.generalizeMayBeNull(mayBeNull); } if (otherReferencedClass.extendsOrImplements(thisReferencedClass)) { return this.generalizeMayBeNull(mayBeNull); } // Collect the superclasses and interfaces of this class. Set thisSuperClasses = new HashSet(); thisReferencedClass.hierarchyAccept( false, true, true, false, new ClassCollector(thisSuperClasses)); int thisSuperClassesCount = thisSuperClasses.size(); if (thisSuperClassesCount == 0 && thisReferencedClass.getSuperName() != null) { throw new IllegalArgumentException( "Can't find any super classes of [" + thisType + "] (not even immediate super class [" + thisReferencedClass.getSuperName() + "])"); } // Collect the superclasses and interfaces of the other class. Set otherSuperClasses = new HashSet(); otherReferencedClass.hierarchyAccept( false, true, true, false, new ClassCollector(otherSuperClasses)); int otherSuperClassesCount = otherSuperClasses.size(); if (otherSuperClassesCount == 0 && otherReferencedClass.getSuperName() != null) { throw new IllegalArgumentException( "Can't find any super classes of [" + otherType + "] (not even immediate super class [" + otherReferencedClass.getSuperName() + "])"); } if (DEBUG) { System.out.println( "ReferenceValue.generalize this [" + thisReferencedClass.getName() + "] with other [" + otherReferencedClass.getName() + "]"); System.out.println(" This super classes: " + thisSuperClasses); System.out.println(" Other super classes: " + otherSuperClasses); } // Find the common superclasses. thisSuperClasses.retainAll(otherSuperClasses); if (DEBUG) { System.out.println(" Common super classes: " + thisSuperClasses); } // 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 = thisSuperClasses.iterator(); while (commonSuperClasses.hasNext()) { Clazz commonSuperClass = (Clazz) commonSuperClasses.next(); int superClassCount = superClassCount(commonSuperClass, thisSuperClasses); 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 [" + thisType + "] (with " + thisSuperClassesCount + " known super classes) and [" + otherType + "] (with " + otherSuperClassesCount + " known super classes)"); } if (DEBUG) { System.out.println(" Best common class: [" + commonClass.getName() + "]"); } // TODO: Handle more difficult cases, with multiple global subclasses. return new ReferenceValue( commonDimensionCount == 0 ? commonClass.getName() : ClassUtil.internalArrayTypeFromClassName( commonClass.getName(), commonDimensionCount), commonClass, mayBeNull); } } else if (thisDimensionCount > otherDimensionCount) { // See if the other type is an interface type of arrays. if (ClassUtil.isInternalArrayInterfaceName( ClassUtil.internalClassNameFromClassType(otherType))) { return other.generalizeMayBeNull(mayBeNull); } } else if (thisDimensionCount < otherDimensionCount) { // See if this type is an interface type of arrays. if (ClassUtil.isInternalArrayInterfaceName( ClassUtil.internalClassNameFromClassType(thisType))) { return this.generalizeMayBeNull(mayBeNull); } } // Reduce the common dimension count if either type is an array of // primitives type of this dimension. if (commonDimensionCount > 0 && (ClassUtil.isInternalPrimitiveType(otherType.charAt(commonDimensionCount))) || ClassUtil.isInternalPrimitiveType(thisType.charAt(commonDimensionCount))) { commonDimensionCount--; } // Fall back on a basic Object or array of Objects type. return commonDimensionCount == 0 ? mayBeNull ? ValueFactory.REFERENCE_VALUE_JAVA_LANG_OBJECT_MAYBE_NULL : ValueFactory.REFERENCE_VALUE_JAVA_LANG_OBJECT_NOT_NULL : new ReferenceValue( ClassUtil.internalArrayTypeFromClassName( ClassConstants.INTERNAL_NAME_JAVA_LANG_OBJECT, commonDimensionCount), null, mayBeNull); }