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); }
/** Returns a shrunk descriptor or signature of the given method. */ private String shrinkDescriptor(Method method, String descriptor) { // All parameters of non-static methods are shifted by one in the local // variable frame. int parameterIndex = (method.getAccessFlags() & ClassConstants.INTERNAL_ACC_STATIC) != 0 ? 0 : 1; // Go over the parameters. InternalTypeEnumeration internalTypeEnumeration = new InternalTypeEnumeration(descriptor); StringBuffer newDescriptorBuffer = new StringBuffer(); newDescriptorBuffer.append(internalTypeEnumeration.formalTypeParameters()); newDescriptorBuffer.append(ClassConstants.INTERNAL_METHOD_ARGUMENTS_OPEN); while (internalTypeEnumeration.hasMoreTypes()) { String type = internalTypeEnumeration.nextType(); if (ParameterUsageMarker.isParameterUsed(method, parameterIndex)) { newDescriptorBuffer.append(type); } else if (DEBUG) { System.out.println(" Deleting parameter #" + parameterIndex + " [" + type + "]"); } parameterIndex += ClassUtil.isInternalCategory2Type(type) ? 2 : 1; } newDescriptorBuffer.append(ClassConstants.INTERNAL_METHOD_ARGUMENTS_CLOSE); newDescriptorBuffer.append(internalTypeEnumeration.returnType()); return newDescriptorBuffer.toString(); }
/** * 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))))); } }
/** Shrinks the array of referenced classes of the given method. */ private Clazz[] shrinkReferencedClasses( Method method, String descriptor, Clazz[] referencedClasses) { if (referencedClasses != null) { // All parameters of non-static methods are shifted by one in the local // variable frame. int parameterIndex = (method.getAccessFlags() & ClassConstants.INTERNAL_ACC_STATIC) != 0 ? 0 : 1; int referencedClassIndex = 0; int newReferencedClassIndex = 0; // Go over the parameters. InternalTypeEnumeration internalTypeEnumeration = new InternalTypeEnumeration(descriptor); // Also look at the formal type parameters. String type = internalTypeEnumeration.formalTypeParameters(); int count = new DescriptorClassEnumeration(type).classCount(); for (int counter = 0; counter < count; counter++) { referencedClasses[newReferencedClassIndex++] = referencedClasses[referencedClassIndex++]; } while (internalTypeEnumeration.hasMoreTypes()) { // Consider the classes referenced by this parameter type. type = internalTypeEnumeration.nextType(); count = new DescriptorClassEnumeration(type).classCount(); if (ParameterUsageMarker.isParameterUsed(method, parameterIndex)) { // Copy the referenced classes. for (int counter = 0; counter < count; counter++) { referencedClasses[newReferencedClassIndex++] = referencedClasses[referencedClassIndex++]; } } else { // Skip the referenced classes. referencedClassIndex += count; } parameterIndex += ClassUtil.isInternalCategory2Type(type) ? 2 : 1; } // Also look at the return value. type = internalTypeEnumeration.returnType(); count = new DescriptorClassEnumeration(type).classCount(); for (int counter = 0; counter < count; counter++) { referencedClasses[newReferencedClassIndex++] = referencedClasses[referencedClassIndex++]; } // Clear the unused entries. while (newReferencedClassIndex < referencedClassIndex) { referencedClasses[newReferencedClassIndex++] = null; } } return referencedClasses; }
/** * 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))))); } }
/** * 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 visitAnyParameterAnnotationsAttribute( Clazz clazz, Method method, ParameterAnnotationsAttribute parameterAnnotationsAttribute) { int[] annotationsCounts = parameterAnnotationsAttribute.u2parameterAnnotationsCount; Annotation[][] annotations = parameterAnnotationsAttribute.parameterAnnotations; // All parameters of non-static methods are shifted by one in the local // variable frame. int parameterIndex = (method.getAccessFlags() & ClassConstants.INTERNAL_ACC_STATIC) != 0 ? 0 : 1; int annotationIndex = 0; int newAnnotationIndex = 0; // Go over the parameters. String descriptor = method.getDescriptor(clazz); InternalTypeEnumeration internalTypeEnumeration = new InternalTypeEnumeration(descriptor); while (internalTypeEnumeration.hasMoreTypes()) { String type = internalTypeEnumeration.nextType(); if (ParameterUsageMarker.isParameterUsed(method, parameterIndex)) { annotationsCounts[newAnnotationIndex] = annotationsCounts[annotationIndex]; annotations[newAnnotationIndex++] = annotations[annotationIndex]; } annotationIndex++; parameterIndex += ClassUtil.isInternalCategory2Type(type) ? 2 : 1; } // Update the number of parameters. parameterAnnotationsAttribute.u2parametersCount = newAnnotationIndex; // Clear the unused entries. while (newAnnotationIndex < annotationIndex) { annotationsCounts[newAnnotationIndex] = 0; annotations[newAnnotationIndex++] = 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); } }