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.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
@ -621,16 +622,16 @@ public class Renderer implements RenderingManager {
}
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()) {
if (!method.methodHolder.getModifiers().contains(ElementModifier.STATIC)
&& 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);
}
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<>();
ClassReader superclass = cls;
while (superclass != null) {
@ -717,35 +718,40 @@ public class Renderer implements RenderingManager {
}
Set<String> visitedClasses = new HashSet<>();
for (String ifaceName : cls.getInterfaces()) {
ClassReader iface = classSource.get(ifaceName);
if (iface != null) {
collectMethodsToCopyFromInterfacesImpl(iface, targetList, implementedMethods, visitedClasses);
superclass = cls;
while (superclass != null) {
for (String ifaceName : superclass.getInterfaces()) {
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,
Set<MethodDescriptor> visited, Set<String> visitedClasses) {
private void collectMethodsToCopyFromInterfacesImpl(ClassReader cls, Map<MethodDescriptor, MethodReference> target,
Set<MethodDescriptor> implementedMethods, Set<String> visitedClasses) {
if (!visitedClasses.add(cls.getName())) {
return;
}
for (String ifaceName : cls.getInterfaces()) {
ClassReader iface = classSource.get(ifaceName);
if (iface != null) {
collectMethodsToCopyFromInterfacesImpl(iface, target, implementedMethods, visitedClasses);
}
}
for (MethodReader method : cls.getMethods()) {
if (!method.hasModifier(ElementModifier.STATIC)
&& !method.hasModifier(ElementModifier.ABSTRACT)) {
if (visited.add(method.getDescriptor())) {
targetList.add(method.getReference());
MethodDescriptor descriptor = method.getDescriptor();
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) {

View File

@ -491,8 +491,20 @@ public class VMTest {
@Test
public void indirectDefaultMethod() {
PathJoint o = new PathJoint();
assertEquals("SecondPath.foo", o.foo());
StringBuilder sb = new StringBuilder();
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 {
@ -511,6 +523,13 @@ public class VMTest {
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
public void cloneArray() {
String[] a = new String[] { "foo" };