From ba7f07de4b203663115581a189e0af4066dd81dc Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Tue, 6 Jun 2017 22:42:21 +0300 Subject: [PATCH] Copy default methods from interfaces to implementing classes --- .../javascript/rendering/Renderer.java | 55 ++++++++++++++++--- tests/src/test/java/org/teavm/vm/VMTest.java | 46 ++++++++++++++++ 2 files changed, 92 insertions(+), 9 deletions(-) diff --git a/core/src/main/java/org/teavm/backend/javascript/rendering/Renderer.java b/core/src/main/java/org/teavm/backend/javascript/rendering/Renderer.java index 83b3d196f..0fb45c5c9 100644 --- a/core/src/main/java/org/teavm/backend/javascript/rendering/Renderer.java +++ b/core/src/main/java/org/teavm/backend/javascript/rendering/Renderer.java @@ -21,6 +21,7 @@ import java.util.HashSet; import java.util.List; import java.util.Properties; import java.util.Set; +import java.util.stream.Collectors; import org.teavm.ast.AsyncMethodNode; import org.teavm.ast.AsyncMethodPart; import org.teavm.ast.ClassNode; @@ -39,6 +40,7 @@ import org.teavm.common.ServiceRepository; import org.teavm.debugging.information.DebugInformationEmitter; import org.teavm.debugging.information.DummyDebugInformationEmitter; import org.teavm.diagnostics.Diagnostics; +import org.teavm.model.ClassReader; import org.teavm.model.ElementModifier; import org.teavm.model.FieldReference; import org.teavm.model.ListableClassReaderSource; @@ -281,8 +283,6 @@ public class Renderer implements RenderingManager { } for (ClassNode cls : classes) { renderDeclaration(cls); - } - for (ClassNode cls : classes) { renderMethodBodies(cls); } renderClassMetadata(classes); @@ -481,12 +481,13 @@ public class Renderer implements RenderingManager { } writer.append(',').ws(); - List virtualMethods = new ArrayList<>(); + List virtualMethods = new ArrayList<>(); for (MethodNode method : cls.getMethods()) { if (!method.getModifiers().contains(ElementModifier.STATIC)) { - virtualMethods.add(method); + virtualMethods.add(method.getReference()); } } + collectMethodsToCopyFromInterfaces(classSource.get(cls.getName()), virtualMethods); renderVirtualDeclarations(virtualMethods); } @@ -498,6 +499,43 @@ public class Renderer implements RenderingManager { } } + private void collectMethodsToCopyFromInterfaces(ClassReader cls, List targetList) { + Set implementedMethods = new HashSet<>(); + implementedMethods.addAll(targetList.stream().map(method -> method.getDescriptor()) + .collect(Collectors.toList())); + + Set visitedClasses = new HashSet<>(); + for (String ifaceName : cls.getInterfaces()) { + ClassReader iface = classSource.get(ifaceName); + if (iface != null) { + collectMethodsToCopyFromInterfacesImpl(iface, targetList, implementedMethods, visitedClasses); + } + } + } + + private void collectMethodsToCopyFromInterfacesImpl(ClassReader cls, List targetList, + Set visited, Set visitedClasses) { + if (!visitedClasses.add(cls.getName())) { + return; + } + + for (MethodReader method : cls.getMethods()) { + if (!method.hasModifier(ElementModifier.STATIC) + && !method.hasModifier(ElementModifier.ABSTRACT)) { + if (visited.add(method.getDescriptor())) { + targetList.add(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) { if (type instanceof ValueType.Primitive) { ValueType.Primitive primitive = (ValueType.Primitive) type; @@ -551,17 +589,16 @@ public class Renderer implements RenderingManager { return minifying ? RenderingUtil.indexToId(index) : "var_" + index; } - private void renderVirtualDeclarations(List methods) throws NamingException, IOException { + private void renderVirtualDeclarations(Iterable methods) throws NamingException, IOException { writer.append("["); boolean first = true; - for (MethodNode method : methods) { - debugEmitter.emitMethod(method.getReference().getDescriptor()); - MethodReference ref = method.getReference(); + for (MethodReference method : methods) { + debugEmitter.emitMethod(method.getDescriptor()); if (!first) { writer.append(",").ws(); } first = false; - emitVirtualDeclaration(ref); + emitVirtualDeclaration(method); debugEmitter.emitMethod(null); } writer.append("]"); diff --git a/tests/src/test/java/org/teavm/vm/VMTest.java b/tests/src/test/java/org/teavm/vm/VMTest.java index 30d4cf37e..945ccb4ec 100644 --- a/tests/src/test/java/org/teavm/vm/VMTest.java +++ b/tests/src/test/java/org/teavm/vm/VMTest.java @@ -225,6 +225,52 @@ public class VMTest { callback.complete(value); } + @Test + public void defaultMethodsSupported() { + WithDefaultMethod[] instances = { new WithDefaultMethodDerivedA(), new WithDefaultMethodDerivedB(), + new WithDefaultMethodDerivedC() }; + StringBuilder sb = new StringBuilder(); + for (WithDefaultMethod instance : instances) { + sb.append(instance.foo() + "," + instance.bar() + ";"); + } + + assertEquals("default,A;default,B;overridden,C;", sb.toString()); + } + + interface WithDefaultMethod { + default String foo() { + return "default"; + } + + String bar(); + } + + class WithDefaultMethodDerivedA implements WithDefaultMethod { + @Override + public String bar() { + return "A"; + } + } + + class WithDefaultMethodDerivedB implements WithDefaultMethod { + @Override + public String bar() { + return "B"; + } + } + class WithDefaultMethodDerivedC implements WithDefaultMethod { + @Override + public String foo() { + return "overridden"; + } + + @Override + public String bar() { + return "C"; + } + } + + @JSBody(script = "return [1, 2]") private static native int[] createArray();