// and old. in the process we modify the new class so that is's signature
// is exactly compatible with the old class, otherwise an
// IncompatibleClassChange exception will be thrown
while (it.hasNext()) {
MethodInfo m = (MethodInfo) it.next();
MethodData md = null;
boolean upgradedVisibility = false;
for (MethodData i : methods) {
if (i.getMethodName().equals(m.getName()) && i.getDescriptor().equals(m.getDescriptor())) {
// if the access flags do not match then what we need to do
// depends on what has changed
if (i.getAccessFlags() != m.getAccessFlags()) {
if (AccessFlagUtils.upgradeVisibility(m.getAccessFlags(), i.getAccessFlags())) {
upgradedVisibility = true;
} else if (AccessFlagUtils.downgradeVisibility(m.getAccessFlags(), i.getAccessFlags())) {
// ignore this, we don't need to do anything
} else {
// we can't handle this yet
continue;
}
}
m.setAccessFlags(i.getAccessFlags());
// if it is the constructor
if (m.getName().equals("<init>")) {
try {
Constructor<?> meth = i.getConstructor(oldClass);
AnnotationDataStore.recordConstructorAnnotations(meth, (AnnotationsAttribute) m.getAttribute(AnnotationsAttribute.visibleTag));
// now revert the annotations:
m.addAttribute(AnnotationReplacer.duplicateAnnotationsAttribute(file.getConstPool(), meth));
m.addAttribute(AnnotationReplacer.duplicateParameterAnnotationsAttribute(file.getConstPool(), meth));
} catch (Exception e) {
throw new RuntimeException(e);
}
} else if (!m.getName().equals("<clinit>")) {
// other methods
// static constructors cannot have annotations so
// we do not have to worry about them
try {
Method meth = i.getMethod(oldClass);
AnnotationDataStore.recordMethodAnnotations(meth, (AnnotationsAttribute) m.getAttribute(AnnotationsAttribute.visibleTag));
AnnotationDataStore.recordMethodParameterAnnotations(meth, (ParameterAnnotationsAttribute) m.getAttribute(ParameterAnnotationsAttribute.visibleTag));
// now revert the annotations:
m.addAttribute(AnnotationReplacer.duplicateAnnotationsAttribute(file.getConstPool(), meth));
m.addAttribute(AnnotationReplacer.duplicateParameterAnnotationsAttribute(file.getConstPool(), meth));
} catch (Exception e) {
throw new RuntimeException(e);
}
}
md = i;
break;
}
}
// we do not need to deal with these
if (m.getName().equals(Constants.ADDED_METHOD_NAME) || m.getName().equals(Constants.ADDED_STATIC_METHOD_NAME)) {
break;
}
// This is a newly added method.
// or the visilbility has been upgraded
// with the visiblity upgrade we just copy the method
// so it is still in the original
if (md == null || upgradedVisibility) {
if ((m.getAccessFlags() & AccessFlag.STATIC) != 0) {
Class<?> c = addMethod(file, loader, m, builder, staticCodeAttribute, true, oldClass);
if (c != null) {
superclassesToHotswap.add(c);
}
} else if ((m.getName().equals("<init>"))) {
addConstructor(file, loader, m, builder, constructorCodeAttribute, oldClass);
} else if (m.getName().equals("<clinit>")) {
// nop, we can't change this, just ignore it
} else {
Class<?> c = addMethod(file, loader, m, builder, virtualCodeAttribute, false, oldClass);
if (c != null) {
superclassesToHotswap.add(c);
}
}
if (!upgradedVisibility) {
it.remove();
}
} else if (md != null) {
methods.remove(md);
}
if (upgradedVisibility && md != null) {
methods.remove(md);
}
}
// these methods have been removed, change them to throw a
// MethodNotFoundError
for (MethodData md : methods) {
if (md.getType() == MemberType.NORMAL) {
createRemovedMethod(file, md, oldClass, builder);
}
}
// if we did not return from a virtual method we need to call the parent