Skip to content

Commit

Permalink
fixed two edge cases when backporting default methods
Browse files Browse the repository at this point in the history
  • Loading branch information
Vasilcenko authored and unknown committed Mar 18, 2015
1 parent c90ca7c commit 5519d0c
Show file tree
Hide file tree
Showing 3 changed files with 63 additions and 15 deletions.
Expand Up @@ -40,7 +40,14 @@ public MethodVisitor visitMethod(int access, String name, String desc, String si
public void visitEnd() {
for (String anInterface : interfaces) {
for (MethodRef interfaceMethod : methodRelocations.getInterfaceMethods(anInterface)) {
if (!methods.contains(interfaceMethod.withOwner(className))) {
boolean hasOverride = false;
for (MethodRef superMethod : methodRelocations.getSuperclassMethods(className)) {
if (superMethod.equals(interfaceMethod.withOwner(superMethod.owner))) {
hasOverride = true;
break;
}
}
if (!hasOverride && !methods.contains(interfaceMethod.withOwner(className))) {
generateDefaultImplementation(interfaceMethod);
}
}
Expand Down
Expand Up @@ -19,8 +19,10 @@ public class ClassHierarchyAnalyzer implements MethodRelocations {

private final List<ClassReader> interfaces = new ArrayList<>();
private final List<ClassReader> classes = new ArrayList<>();
private final Map<String, String> superclasses = new HashMap<>();
private final Map<Type, List<Type>> interfacesByImplementer = new HashMap<>(); // TODO: could use just String instead of Type
private final Map<String, List<MethodRef>> methodsByInterface = new HashMap<>();
private final Map<String, List<MethodRef>> methodsByClass = new HashMap<>();
private final Map<MethodRef, MethodRef> relocatedMethods = new HashMap<>();
private final Map<MethodRef, MethodRef> methodDefaultImpls = new HashMap<>();
private final Map<String, String> companionClasses = new HashMap<>();
Expand All @@ -37,12 +39,35 @@ public void analyze(byte[] bytecode) {

List<Type> interfaces = classNamesToTypes(cr.getInterfaces());
interfacesByImplementer.put(clazz, interfaces);
superclasses.put(cr.getClassName(), cr.getSuperName());

if (Flags.hasFlag(cr.getAccess(), ACC_INTERFACE)) {
analyzeInterface(cr);
} else {
analyzeClass(cr);
}
}

private void analyzeClass(ClassReader cr) {
cr.accept(new ClassVisitor(ASM5) {
private String owner;

@Override
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
this.owner = name;
}

@Override
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
MethodRef method = new MethodRef(owner, name, desc);
methodsByClass.computeIfAbsent(method.owner, key -> new ArrayList<>()).add(method);

return null;
}

}, ClassReader.SKIP_CODE);
}

private void analyzeInterface(ClassReader cr) {
cr.accept(new ClassVisitor(ASM5) {
private String owner;
Expand Down Expand Up @@ -113,21 +138,26 @@ public MethodRef getMethodCallTarget(MethodRef original) {

@Override
public MethodRef getMethodDefaultImplementation(MethodRef interfaceMethod) {
MethodRef impl = methodDefaultImpls.get(interfaceMethod);
if (impl == ABSTRACT_METHOD) {
return null;
}
if (impl != null) {
return impl;
}

// check if a default implementation is inherited from parents
for (Type parentInterface : interfacesByImplementer.getOrDefault(Type.getObjectType(interfaceMethod.owner), Collections.emptyList())) {
impl = getMethodDefaultImplementation(interfaceMethod.withOwner(parentInterface.getInternalName()));
if (impl != null) {
return impl;
MethodRef impl;
List<Type> currentInterfaces = new ArrayList<>();
List<Type> parentInterfaces = new ArrayList<>();
currentInterfaces.add(Type.getObjectType(interfaceMethod.owner));

do {
for (Type anInterface : currentInterfaces) {
impl = methodDefaultImpls.get(interfaceMethod.withOwner(anInterface.getInternalName()));
if (impl == ABSTRACT_METHOD) {
return null;
}
if (impl != null) {
return impl;
}
parentInterfaces.addAll(interfacesByImplementer.getOrDefault(anInterface, Collections.emptyList()));
}
}
currentInterfaces = parentInterfaces;
parentInterfaces = new ArrayList<>();
} while (!currentInterfaces.isEmpty());

return null;
}

Expand All @@ -143,6 +173,15 @@ public List<MethodRef> getInterfaceMethods(String interfaceName) {
return new ArrayList<>(results);
}

public List<MethodRef> getSuperclassMethods(String className) {
Set<MethodRef> results = new LinkedHashSet<>();
while (superclasses.containsKey(className)) {
className = superclasses.get(className);
results.addAll(methodsByClass.getOrDefault(className, Collections.emptyList()));
}
return new ArrayList<>(results);
}

@Override
public String getCompanionClass(String className) {
return companionClasses.get(className);
Expand Down
Expand Up @@ -14,5 +14,7 @@ public interface MethodRelocations {

List<MethodRef> getInterfaceMethods(String interfaceName);

List<MethodRef> getSuperclassMethods(String className);

String getCompanionClass(String className);
}

0 comments on commit 5519d0c

Please sign in to comment.