Copy default methods from interfaces to implementing classes

This commit is contained in:
Alexey Andreev 2017-06-06 22:42:21 +03:00
parent d8913f85d1
commit ba7f07de4b
2 changed files with 92 additions and 9 deletions

View File

@ -21,6 +21,7 @@ import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Properties; import java.util.Properties;
import java.util.Set; import java.util.Set;
import java.util.stream.Collectors;
import org.teavm.ast.AsyncMethodNode; import org.teavm.ast.AsyncMethodNode;
import org.teavm.ast.AsyncMethodPart; import org.teavm.ast.AsyncMethodPart;
import org.teavm.ast.ClassNode; 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.DebugInformationEmitter;
import org.teavm.debugging.information.DummyDebugInformationEmitter; import org.teavm.debugging.information.DummyDebugInformationEmitter;
import org.teavm.diagnostics.Diagnostics; import org.teavm.diagnostics.Diagnostics;
import org.teavm.model.ClassReader;
import org.teavm.model.ElementModifier; import org.teavm.model.ElementModifier;
import org.teavm.model.FieldReference; import org.teavm.model.FieldReference;
import org.teavm.model.ListableClassReaderSource; import org.teavm.model.ListableClassReaderSource;
@ -281,8 +283,6 @@ public class Renderer implements RenderingManager {
} }
for (ClassNode cls : classes) { for (ClassNode cls : classes) {
renderDeclaration(cls); renderDeclaration(cls);
}
for (ClassNode cls : classes) {
renderMethodBodies(cls); renderMethodBodies(cls);
} }
renderClassMetadata(classes); renderClassMetadata(classes);
@ -481,12 +481,13 @@ public class Renderer implements RenderingManager {
} }
writer.append(',').ws(); writer.append(',').ws();
List<MethodNode> virtualMethods = new ArrayList<>(); List<MethodReference> virtualMethods = new ArrayList<>();
for (MethodNode method : cls.getMethods()) { for (MethodNode method : cls.getMethods()) {
if (!method.getModifiers().contains(ElementModifier.STATIC)) { if (!method.getModifiers().contains(ElementModifier.STATIC)) {
virtualMethods.add(method); virtualMethods.add(method.getReference());
} }
} }
collectMethodsToCopyFromInterfaces(classSource.get(cls.getName()), virtualMethods);
renderVirtualDeclarations(virtualMethods); renderVirtualDeclarations(virtualMethods);
} }
@ -498,6 +499,43 @@ public class Renderer implements RenderingManager {
} }
} }
private void collectMethodsToCopyFromInterfaces(ClassReader cls, List<MethodReference> targetList) {
Set<MethodDescriptor> implementedMethods = new HashSet<>();
implementedMethods.addAll(targetList.stream().map(method -> method.getDescriptor())
.collect(Collectors.toList()));
Set<String> 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<MethodReference> targetList,
Set<MethodDescriptor> visited, Set<String> 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) { private static Object getDefaultValue(ValueType type) {
if (type instanceof ValueType.Primitive) { if (type instanceof ValueType.Primitive) {
ValueType.Primitive primitive = (ValueType.Primitive) type; ValueType.Primitive primitive = (ValueType.Primitive) type;
@ -551,17 +589,16 @@ public class Renderer implements RenderingManager {
return minifying ? RenderingUtil.indexToId(index) : "var_" + index; return minifying ? RenderingUtil.indexToId(index) : "var_" + index;
} }
private void renderVirtualDeclarations(List<MethodNode> methods) throws NamingException, IOException { private void renderVirtualDeclarations(Iterable<MethodReference> methods) throws NamingException, IOException {
writer.append("["); writer.append("[");
boolean first = true; boolean first = true;
for (MethodNode method : methods) { for (MethodReference method : methods) {
debugEmitter.emitMethod(method.getReference().getDescriptor()); debugEmitter.emitMethod(method.getDescriptor());
MethodReference ref = method.getReference();
if (!first) { if (!first) {
writer.append(",").ws(); writer.append(",").ws();
} }
first = false; first = false;
emitVirtualDeclaration(ref); emitVirtualDeclaration(method);
debugEmitter.emitMethod(null); debugEmitter.emitMethod(null);
} }
writer.append("]"); writer.append("]");

View File

@ -225,6 +225,52 @@ public class VMTest {
callback.complete(value); 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]") @JSBody(script = "return [1, 2]")
private static native int[] createArray(); private static native int[] createArray();