JS: fix overrides with default methods

This commit is contained in:
JohannesS 2021-03-12 15:36:35 +01:00 committed by Alexey Andreev
parent 4fe012740d
commit f97484365c
2 changed files with 47 additions and 22 deletions

View File

@ -22,6 +22,7 @@ import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Properties; import java.util.Properties;
@ -621,16 +622,16 @@ public class Renderer implements RenderingManager {
} }
writer.append(',').ws(); writer.append(',').ws();
List<MethodReference> virtualMethods = new ArrayList<>(); Map<MethodDescriptor, MethodReference> virtualMethods = new LinkedHashMap<>();
collectMethodsToCopyFromInterfaces(classSource.get(cls.getName()), virtualMethods);
for (PreparedMethod method : cls.getMethods()) { for (PreparedMethod method : cls.getMethods()) {
if (!method.methodHolder.getModifiers().contains(ElementModifier.STATIC) if (!method.methodHolder.getModifiers().contains(ElementModifier.STATIC)
&& method.methodHolder.getLevel() != AccessLevel.PRIVATE) { && method.methodHolder.getLevel() != AccessLevel.PRIVATE) {
virtualMethods.add(method.reference); virtualMethods.put(method.reference.getDescriptor(), method.reference);
} }
} }
collectMethodsToCopyFromInterfaces(classSource.get(cls.getName()), virtualMethods);
renderVirtualDeclarations(virtualMethods); renderVirtualDeclarations(virtualMethods.values());
debugEmitter.emitClass(null); debugEmitter.emitClass(null);
} }
writer.append("]);").newLine(); writer.append("]);").newLine();
@ -702,7 +703,7 @@ public class Renderer implements RenderingManager {
} }
} }
private void collectMethodsToCopyFromInterfaces(ClassReader cls, List<MethodReference> targetList) { private void collectMethodsToCopyFromInterfaces(ClassReader cls, Map<MethodDescriptor, MethodReference> target) {
Set<MethodDescriptor> implementedMethods = new HashSet<>(); Set<MethodDescriptor> implementedMethods = new HashSet<>();
ClassReader superclass = cls; ClassReader superclass = cls;
while (superclass != null) { while (superclass != null) {
@ -717,35 +718,40 @@ public class Renderer implements RenderingManager {
} }
Set<String> visitedClasses = new HashSet<>(); Set<String> visitedClasses = new HashSet<>();
for (String ifaceName : cls.getInterfaces()) { superclass = cls;
ClassReader iface = classSource.get(ifaceName); while (superclass != null) {
if (iface != null) { for (String ifaceName : superclass.getInterfaces()) {
collectMethodsToCopyFromInterfacesImpl(iface, targetList, implementedMethods, visitedClasses); ClassReader iface = classSource.get(ifaceName);
if (iface != null) {
collectMethodsToCopyFromInterfacesImpl(iface, target, implementedMethods, visitedClasses);
}
} }
superclass = superclass.getParent() != null ? classSource.get(superclass.getParent()) : null;
} }
} }
private void collectMethodsToCopyFromInterfacesImpl(ClassReader cls, List<MethodReference> targetList, private void collectMethodsToCopyFromInterfacesImpl(ClassReader cls, Map<MethodDescriptor, MethodReference> target,
Set<MethodDescriptor> visited, Set<String> visitedClasses) { Set<MethodDescriptor> implementedMethods, Set<String> visitedClasses) {
if (!visitedClasses.add(cls.getName())) { if (!visitedClasses.add(cls.getName())) {
return; return;
} }
for (String ifaceName : cls.getInterfaces()) {
ClassReader iface = classSource.get(ifaceName);
if (iface != null) {
collectMethodsToCopyFromInterfacesImpl(iface, target, implementedMethods, visitedClasses);
}
}
for (MethodReader method : cls.getMethods()) { for (MethodReader method : cls.getMethods()) {
if (!method.hasModifier(ElementModifier.STATIC) if (!method.hasModifier(ElementModifier.STATIC)
&& !method.hasModifier(ElementModifier.ABSTRACT)) { && !method.hasModifier(ElementModifier.ABSTRACT)) {
if (visited.add(method.getDescriptor())) { MethodDescriptor descriptor = method.getDescriptor();
targetList.add(method.getReference()); if (!implementedMethods.contains(descriptor)) {
target.put(descriptor, method.getReference());
} }
} }
} }
for (String ifaceName : cls.getInterfaces()) {
ClassReader iface = classSource.get(ifaceName);
if (iface != null) {
collectMethodsToCopyFromInterfacesImpl(iface, targetList, visited, visitedClasses);
}
}
} }
private static Object getDefaultValue(ValueType type) { private static Object getDefaultValue(ValueType type) {

View File

@ -491,8 +491,20 @@ public class VMTest {
@Test @Test
public void indirectDefaultMethod() { public void indirectDefaultMethod() {
PathJoint o = new PathJoint(); StringBuilder sb = new StringBuilder();
assertEquals("SecondPath.foo", o.foo()); for (FirstPath o : new FirstPath[] { new PathJoint(), new FirstPathOptimizationPrevention() }) {
sb.append(o.foo()).append(";");
}
assertEquals("SecondPath.foo;FirstPath.foo;", sb.toString());
}
@Test
public void indirectDefaultMethodSubclass() {
StringBuilder sb = new StringBuilder();
for (FirstPath o : new FirstPath[] { new PathJointSubclass(), new FirstPathOptimizationPrevention() }) {
sb.append(o.foo()).append(";");
}
assertEquals("SecondPath.foo;FirstPath.foo;", sb.toString());
} }
interface FirstPath { interface FirstPath {
@ -511,6 +523,13 @@ public class VMTest {
class PathJoint implements FirstPath, SecondPath { class PathJoint implements FirstPath, SecondPath {
} }
class PathJointSubclass extends PathJoint implements FirstPath {
}
class FirstPathOptimizationPrevention implements FirstPath {
// Used to ensure that the implementation of FirstPath.foo() is not optimized away by TeaVM.
}
@Test @Test
public void cloneArray() { public void cloneArray() {
String[] a = new String[] { "foo" }; String[] a = new String[] { "foo" };