Change how method resolution work in DCE and devirtualization.

Since in Java 8 there are default methods in interface, method
resolution algorithm becomes more complicated. This alseocauses
several related changes.

1. Resolve methods as late as possible; do not resolve
   virtual call sites during DCE.
2. Due to several reasons we have to improve linking phase
   to preserve super methods that aren't actually ever called,
   but present in virtual call sites.

Related issue: #311
This commit is contained in:
Alexey Andreev 2017-10-22 17:56:31 +03:00
parent 600151a69b
commit de14a57fe1
16 changed files with 199 additions and 50 deletions

View File

@ -84,7 +84,7 @@ public class DependencyChecker implements DependencyInfo {
this.classSource = new DependencyClassSource(classSource, diagnostics); this.classSource = new DependencyClassSource(classSource, diagnostics);
this.classLoader = classLoader; this.classLoader = classLoader;
this.services = services; this.services = services;
methodReaderCache = new CachedMapper<>(preimage -> this.classSource.resolveMutable(preimage)); methodReaderCache = new CachedMapper<>(preimage -> this.classSource.resolveMutableImplementation(preimage));
fieldReaderCache = new CachedMapper<>(preimage -> this.classSource.resolveMutable(preimage)); fieldReaderCache = new CachedMapper<>(preimage -> this.classSource.resolveMutable(preimage));
methodCache = new CachedMapper<>(preimage -> { methodCache = new CachedMapper<>(preimage -> {
MethodHolder method = methodReaderCache.map(preimage); MethodHolder method = methodReaderCache.map(preimage);

View File

@ -305,7 +305,7 @@ class DependencyGraphBuilder {
private static class VirtualCallConsumer implements DependencyConsumer { private static class VirtualCallConsumer implements DependencyConsumer {
private final DependencyNode node; private final DependencyNode node;
private final ClassReader filterClass; private final String filterClass;
private final MethodDescriptor methodDesc; private final MethodDescriptor methodDesc;
private final DependencyChecker checker; private final DependencyChecker checker;
private final DependencyNode[] parameters; private final DependencyNode[] parameters;
@ -315,7 +315,7 @@ class DependencyGraphBuilder {
private final Set<MethodReference> knownMethods = new HashSet<>(); private final Set<MethodReference> knownMethods = new HashSet<>();
private ExceptionConsumer exceptionConsumer; private ExceptionConsumer exceptionConsumer;
public VirtualCallConsumer(DependencyNode node, ClassReader filterClass, public VirtualCallConsumer(DependencyNode node, String filterClass,
MethodDescriptor methodDesc, DependencyChecker checker, DependencyNode[] parameters, MethodDescriptor methodDesc, DependencyChecker checker, DependencyNode[] parameters,
DependencyNode result, DefaultCallGraphNode caller, TextLocation location, DependencyNode result, DefaultCallGraphNode caller, TextLocation location,
ExceptionConsumer exceptionConsumer) { ExceptionConsumer exceptionConsumer) {
@ -342,7 +342,7 @@ class DependencyGraphBuilder {
} }
ClassReaderSource classSource = checker.getClassSource(); ClassReaderSource classSource = checker.getClassSource();
if (!classSource.isSuperType(filterClass.getName(), className).orElse(false)) { if (!classSource.isSuperType(filterClass, className).orElse(false)) {
return; return;
} }
MethodReference methodRef = new MethodReference(className, methodDesc); MethodReference methodRef = new MethodReference(className, methodDesc);
@ -642,19 +642,13 @@ class DependencyGraphBuilder {
private void invokeVirtual(VariableReader receiver, VariableReader instance, MethodReference method, private void invokeVirtual(VariableReader receiver, VariableReader instance, MethodReference method,
List<? extends VariableReader> arguments) { List<? extends VariableReader> arguments) {
MethodDependency methodDep = dependencyChecker.linkMethod(method,
new CallLocation(caller.getMethod(), currentLocation));
if (methodDep.isMissing()) {
return;
}
DependencyNode[] actualArgs = new DependencyNode[arguments.size() + 1]; DependencyNode[] actualArgs = new DependencyNode[arguments.size() + 1];
for (int i = 0; i < arguments.size(); ++i) { for (int i = 0; i < arguments.size(); ++i) {
actualArgs[i + 1] = nodes[arguments.get(i).getIndex()]; actualArgs[i + 1] = nodes[arguments.get(i).getIndex()];
} }
actualArgs[0] = nodes[instance.getIndex()]; actualArgs[0] = nodes[instance.getIndex()];
DependencyConsumer listener = new VirtualCallConsumer(nodes[instance.getIndex()], DependencyConsumer listener = new VirtualCallConsumer(nodes[instance.getIndex()],
dependencyChecker.getClassSource().get(methodDep.getMethod().getOwnerName()), method.getClassName(), method.getDescriptor(), dependencyChecker, actualArgs,
method.getDescriptor(), dependencyChecker, actualArgs,
receiver != null ? nodes[receiver.getIndex()] : null, caller, currentLocation, receiver != null ? nodes[receiver.getIndex()] : null, caller, currentLocation,
currentExceptionConsumer); currentExceptionConsumer);
nodes[instance.getIndex()].addConsumer(listener); nodes[instance.getIndex()].addConsumer(listener);

View File

@ -15,27 +15,66 @@
*/ */
package org.teavm.dependency; package org.teavm.dependency;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.teavm.model.BasicBlock; import org.teavm.model.BasicBlock;
import org.teavm.model.BasicBlockReader;
import org.teavm.model.ClassHolder; import org.teavm.model.ClassHolder;
import org.teavm.model.ClassReader;
import org.teavm.model.ElementModifier; import org.teavm.model.ElementModifier;
import org.teavm.model.FieldHolder; import org.teavm.model.FieldHolder;
import org.teavm.model.FieldReference; import org.teavm.model.FieldReference;
import org.teavm.model.Instruction; import org.teavm.model.Instruction;
import org.teavm.model.MethodHolder; import org.teavm.model.MethodHolder;
import org.teavm.model.MethodReader;
import org.teavm.model.MethodReference; import org.teavm.model.MethodReference;
import org.teavm.model.Program; import org.teavm.model.Program;
import org.teavm.model.ProgramReader;
import org.teavm.model.VariableReader;
import org.teavm.model.instructions.AbstractInstructionReader;
import org.teavm.model.instructions.GetFieldInstruction; import org.teavm.model.instructions.GetFieldInstruction;
import org.teavm.model.instructions.InitClassInstruction; import org.teavm.model.instructions.InitClassInstruction;
import org.teavm.model.instructions.InvocationType;
import org.teavm.model.instructions.InvokeInstruction; import org.teavm.model.instructions.InvokeInstruction;
import org.teavm.model.instructions.PutFieldInstruction; import org.teavm.model.instructions.PutFieldInstruction;
public class Linker { public class Linker {
private Set<MethodReference> methodsToPreserve = new HashSet<>();
public void prepare(DependencyInfo dependency, ClassReader cls) {
for (MethodReader method : cls.getMethods().toArray(new MethodReader[0])) {
MethodReference methodRef = new MethodReference(cls.getName(), method.getDescriptor());
MethodDependencyInfo methodDep = dependency.getMethod(methodRef);
if (methodDep != null && method.getProgram() != null) {
collectMethodsToPreserve(method.getProgram());
}
}
}
private void collectMethodsToPreserve(ProgramReader program) {
for (BasicBlockReader block : program.getBasicBlocks()) {
block.readAllInstructions(new AbstractInstructionReader() {
@Override
public void invoke(VariableReader receiver, VariableReader instance, MethodReference method,
List<? extends VariableReader> arguments, InvocationType type) {
methodsToPreserve.add(method);
}
});
}
}
public void link(DependencyInfo dependency, ClassHolder cls) { public void link(DependencyInfo dependency, ClassHolder cls) {
for (MethodHolder method : cls.getMethods().toArray(new MethodHolder[0])) { for (MethodHolder method : cls.getMethods().toArray(new MethodHolder[0])) {
MethodReference methodRef = new MethodReference(cls.getName(), method.getDescriptor()); MethodReference methodRef = new MethodReference(cls.getName(), method.getDescriptor());
MethodDependencyInfo methodDep = dependency.getMethod(methodRef); MethodDependencyInfo methodDep = dependency.getMethod(methodRef);
if (methodDep == null) { if (methodDep == null) {
cls.removeMethod(method); if (methodsToPreserve.contains(methodRef)) {
method.getModifiers().add(ElementModifier.ABSTRACT);
method.setProgram(null);
} else {
cls.removeMethod(method);
}
} else if (!methodDep.isUsed()) { } else if (!methodDep.isUsed()) {
method.getModifiers().add(ElementModifier.ABSTRACT); method.getModifiers().add(ElementModifier.ABSTRACT);
method.setProgram(null); method.setProgram(null);
@ -58,9 +97,11 @@ public class Linker {
for (Instruction insn : block) { for (Instruction insn : block) {
if (insn instanceof InvokeInstruction) { if (insn instanceof InvokeInstruction) {
InvokeInstruction invoke = (InvokeInstruction) insn; InvokeInstruction invoke = (InvokeInstruction) insn;
MethodDependencyInfo linkedMethod = dependency.getMethodImplementation(invoke.getMethod()); if (invoke.getType() == InvocationType.SPECIAL) {
if (linkedMethod != null) { MethodDependencyInfo linkedMethod = dependency.getMethodImplementation(invoke.getMethod());
invoke.setMethod(linkedMethod.getReference()); if (linkedMethod != null) {
invoke.setMethod(linkedMethod.getReference());
}
} }
} else if (insn instanceof GetFieldInstruction) { } else if (insn instanceof GetFieldInstruction) {
GetFieldInstruction getField = (GetFieldInstruction) insn; GetFieldInstruction getField = (GetFieldInstruction) insn;

View File

@ -33,11 +33,15 @@ public interface ClassHolderSource extends ClassReaderSource {
return (MethodHolder) resolve(method); return (MethodHolder) resolve(method);
} }
default MethodHolder resolveMutableImplementation(MethodReference method) {
return (MethodHolder) resolveImplementation(method);
}
default FieldHolder resolveMutable(FieldReference field) { default FieldHolder resolveMutable(FieldReference field) {
return (FieldHolder) resolve(field); return (FieldHolder) resolve(field);
} }
default Stream<MethodHolder> mutableOverridenMethods(MethodReference method) { default Stream<MethodHolder> mutableOverriddenMethods(MethodReference method) {
return overriddenMethods(method).map(m -> (MethodHolder) m); return overriddenMethods(method).map(m -> (MethodHolder) m);
} }
} }

View File

@ -19,6 +19,7 @@ import java.util.ArrayDeque;
import java.util.Deque; import java.util.Deque;
import java.util.HashSet; import java.util.HashSet;
import java.util.Iterator; import java.util.Iterator;
import java.util.Objects;
import java.util.Optional; import java.util.Optional;
import java.util.Set; import java.util.Set;
import java.util.stream.Stream; import java.util.stream.Stream;
@ -89,14 +90,19 @@ public interface ClassReaderSource {
default MethodReader resolve(MethodReference method) { default MethodReader resolve(MethodReference method) {
return getAncestors(method.getClassName()) return getAncestors(method.getClassName())
.map(cls -> cls.getMethod(method.getDescriptor())) .map(cls -> cls.getMethod(method.getDescriptor()))
.filter(candidate -> candidate != null) .filter(Objects::nonNull)
.findFirst().orElse(null); .findFirst().orElse(null);
} }
default MethodReader resolveImplementation(MethodReference methodReference) {
return ClassReaderSourceHelper.resolveMethodImplementation(this, methodReference.getClassName(),
methodReference.getDescriptor(), new HashSet<>());
}
default FieldReader resolve(FieldReference field) { default FieldReader resolve(FieldReference field) {
return getAncestors(field.getClassName()) return getAncestors(field.getClassName())
.map(cls -> cls.getField(field.getFieldName())) .map(cls -> cls.getField(field.getFieldName()))
.filter(candidate -> candidate != null) .filter(Objects::nonNull)
.findFirst().orElse(null); .findFirst().orElse(null);
} }

View File

@ -0,0 +1,62 @@
/*
* Copyright 2017 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.model;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
final class ClassReaderSourceHelper {
private ClassReaderSourceHelper() {
}
static MethodReader resolveMethodImplementation(ClassReaderSource classSource, String className,
MethodDescriptor methodDescriptor, Set<String> visited) {
if (!visited.add(className)) {
return null;
}
ClassReader cls = classSource.get(className);
if (cls == null) {
return null;
}
MethodReader method = cls.getMethod(methodDescriptor);
if (method != null && !method.hasModifier(ElementModifier.ABSTRACT)) {
return method;
}
MethodReader mostSpecificMethod = null;
List<String> superClasses = new ArrayList<>();
if (cls.getParent() != null) {
superClasses.add(cls.getParent());
}
superClasses.addAll(cls.getInterfaces());
for (String superClass : superClasses) {
MethodReader resultFromSuperClass = resolveMethodImplementation(classSource, superClass,
methodDescriptor, visited);
if (resultFromSuperClass != null) {
if (mostSpecificMethod == null || classSource.isSuperType(mostSpecificMethod.getOwnerName(),
resultFromSuperClass.getOwnerName()).orElse(false)) {
mostSpecificMethod = resultFromSuperClass;
}
}
}
return mostSpecificMethod;
}
}

View File

@ -165,8 +165,7 @@ public class ClassInference {
private void propagate(Program program) { private void propagate(Program program) {
ClassReaderSource classSource = dependencyInfo.getClassSource(); ClassReaderSource classSource = dependencyInfo.getClassSource();
Queue<Task> queue = new ArrayDeque<>(); Queue<Task> queue = new ArrayDeque<>(initialTasks);
queue.addAll(initialTasks);
initialTasks = null; initialTasks = null;
while (!queue.isEmpty()) { while (!queue.isEmpty()) {
@ -239,7 +238,7 @@ public class ClassInference {
for (VirtualCallSite callSite : callSites) { for (VirtualCallSite callSite : callSites) {
MethodReference rawMethod = new MethodReference(task.className, MethodReference rawMethod = new MethodReference(task.className,
callSite.method.getDescriptor()); callSite.method.getDescriptor());
MethodReader resolvedMethod = classSource.resolve(rawMethod); MethodReader resolvedMethod = classSource.resolveImplementation(rawMethod);
if (resolvedMethod == null) { if (resolvedMethod == null) {
continue; continue;
} }
@ -295,10 +294,10 @@ public class ClassInference {
GraphBuilder arrayGraphBuilder; GraphBuilder arrayGraphBuilder;
GraphBuilder itemGraphBuilder; GraphBuilder itemGraphBuilder;
MethodDependencyInfo thisMethodDep; MethodDependencyInfo thisMethodDep;
List<IntObjectMap<ValueType>> casts = new ArrayList<>(); List<IntObjectMap<ValueType>> casts;
IntObjectMap<IntSet> exceptionMap = new IntObjectOpenHashMap<>(); IntObjectMap<IntSet> exceptionMap = new IntObjectOpenHashMap<>();
List<Task> tasks = new ArrayList<>(); List<Task> tasks = new ArrayList<>();
List<List<VirtualCallSite>> virtualCallSites = new ArrayList<>(); List<List<VirtualCallSite>> virtualCallSites;
BasicBlock currentBlock; BasicBlock currentBlock;
GraphBuildingVisitor(int variableCount, DependencyInfo dependencyInfo) { GraphBuildingVisitor(int variableCount, DependencyInfo dependencyInfo) {

View File

@ -19,7 +19,6 @@ import org.teavm.model.BasicBlock;
import org.teavm.model.ClassReaderSource; import org.teavm.model.ClassReaderSource;
import org.teavm.model.FieldReference; import org.teavm.model.FieldReference;
import org.teavm.model.Incoming; import org.teavm.model.Incoming;
import org.teavm.model.MethodReader;
import org.teavm.model.MethodReference; import org.teavm.model.MethodReference;
import org.teavm.model.Phi; import org.teavm.model.Phi;
import org.teavm.model.PrimitiveType; import org.teavm.model.PrimitiveType;
@ -437,10 +436,6 @@ public class ValueEmitter {
.orElse(true)) { .orElse(true)) {
throw new EmitException("Can't call " + method + " on non-compatible class " + type); throw new EmitException("Can't call " + method + " on non-compatible class " + type);
} }
MethodReader resolvedMethod = pe.classSource.resolve(method);
if (resolvedMethod != null) {
method = resolvedMethod.getReference();
}
Variable result = null; Variable result = null;
if (method.getReturnType() != ValueType.VOID) { if (method.getReturnType() != ValueType.VOID) {

View File

@ -20,7 +20,13 @@ import java.util.Set;
import org.teavm.dependency.DependencyInfo; import org.teavm.dependency.DependencyInfo;
import org.teavm.dependency.MethodDependencyInfo; import org.teavm.dependency.MethodDependencyInfo;
import org.teavm.dependency.ValueDependencyInfo; import org.teavm.dependency.ValueDependencyInfo;
import org.teavm.model.*; import org.teavm.model.BasicBlock;
import org.teavm.model.ClassReader;
import org.teavm.model.ClassReaderSource;
import org.teavm.model.Instruction;
import org.teavm.model.MethodHolder;
import org.teavm.model.MethodReference;
import org.teavm.model.Program;
import org.teavm.model.instructions.InvocationType; import org.teavm.model.instructions.InvocationType;
import org.teavm.model.instructions.InvokeInstruction; import org.teavm.model.instructions.InvokeInstruction;

View File

@ -324,7 +324,7 @@ public class Inlining {
Set<MethodReference> implementations = new HashSet<>(); Set<MethodReference> implementations = new HashSet<>();
for (String className : inference.classesOf(invoke.getInstance().getIndex())) { for (String className : inference.classesOf(invoke.getInstance().getIndex())) {
MethodReference rawMethod = new MethodReference(className, invoke.getMethod().getDescriptor()); MethodReference rawMethod = new MethodReference(className, invoke.getMethod().getDescriptor());
MethodReader resolvedMethod = dependencyInfo.getClassSource().resolve(rawMethod); MethodReader resolvedMethod = dependencyInfo.getClassSource().resolveImplementation(rawMethod);
if (resolvedMethod != null) { if (resolvedMethod != null) {
implementations.add(resolvedMethod.getReference()); implementations.add(resolvedMethod.getReference());
} }

View File

@ -90,7 +90,7 @@ public class AsyncMethodFinder {
} }
} }
for (MethodReference method : asyncMethods) { for (MethodReference method : asyncMethods) {
addOverridenToFamily(method); addOverriddenToFamily(method);
} }
for (String clsName : classSource.getClassNames()) { for (String clsName : classSource.getClassNames()) {
ClassReader cls = classSource.get(clsName); ClassReader cls = classSource.get(clsName);
@ -175,11 +175,11 @@ public class AsyncMethodFinder {
} }
} }
private class CallStack { static class CallStack {
MethodReference method; MethodReference method;
CallStack next; CallStack next;
public CallStack(MethodReference method, CallStack next) { CallStack(MethodReference method, CallStack next) {
this.method = method; this.method = method;
this.next = next; this.next = next;
} }
@ -196,14 +196,14 @@ public class AsyncMethodFinder {
} }
} }
private void addOverridenToFamily(MethodReference methodRef) { private void addOverriddenToFamily(MethodReference methodRef) {
asyncFamilyMethods.put(methodRef, true); asyncFamilyMethods.put(methodRef, true);
ClassReader cls = classSource.get(methodRef.getClassName()); ClassReader cls = classSource.get(methodRef.getClassName());
if (cls == null) { if (cls == null) {
return; return;
} }
for (MethodReference overridenMethod : findOverriddenMethods(cls, methodRef)) { for (MethodReference overriddenMethod : findOverriddenMethods(cls, methodRef)) {
addOverridenToFamily(overridenMethod); addOverriddenToFamily(overriddenMethod);
} }
} }
@ -225,8 +225,8 @@ public class AsyncMethodFinder {
if (cls == null) { if (cls == null) {
return false; return false;
} }
for (MethodReference overridenMethod : findOverriddenMethods(cls, methodRef)) { for (MethodReference overriddenMethod : findOverriddenMethods(cls, methodRef)) {
if (addToFamily(overridenMethod)) { if (addToFamily(overriddenMethod)) {
return true; return true;
} }
} }
@ -241,11 +241,11 @@ public class AsyncMethodFinder {
parents.addAll(cls.getInterfaces()); parents.addAll(cls.getInterfaces());
Set<MethodReference> visited = new HashSet<>(); Set<MethodReference> visited = new HashSet<>();
Set<MethodReference> overriden = new HashSet<>(); Set<MethodReference> overridden = new HashSet<>();
for (String parent : parents) { for (String parent : parents) {
findOverriddenMethods(new MethodReference(parent, methodRef.getDescriptor()), overriden, visited); findOverriddenMethods(new MethodReference(parent, methodRef.getDescriptor()), overridden, visited);
} }
return overriden; return overridden;
} }
private void findOverriddenMethods(MethodReference methodRef, Set<MethodReference> result, private void findOverriddenMethods(MethodReference methodRef, Set<MethodReference> result,

View File

@ -19,7 +19,6 @@ import java.util.ArrayDeque;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Queue; import java.util.Queue;
@ -48,7 +47,7 @@ public class AsyncProgramSplitter {
private List<Part> parts = new ArrayList<>(); private List<Part> parts = new ArrayList<>();
private Map<Instruction, Integer> partMap = new HashMap<>(); private Map<Instruction, Integer> partMap = new HashMap<>();
private ClassReaderSource classSource; private ClassReaderSource classSource;
private Set<MethodReference> asyncMethods = new HashSet<>(); private Set<MethodReference> asyncMethods;
private Program program; private Program program;
public AsyncProgramSplitter(ClassReaderSource classSource, Set<MethodReference> asyncMethods) { public AsyncProgramSplitter(ClassReaderSource classSource, Set<MethodReference> asyncMethods) {

View File

@ -19,6 +19,7 @@ import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
import org.teavm.dependency.DependencyInfo; import org.teavm.dependency.DependencyInfo;
import org.teavm.dependency.MethodDependencyInfo;
import org.teavm.diagnostics.Diagnostics; import org.teavm.diagnostics.Diagnostics;
import org.teavm.model.*; import org.teavm.model.*;
import org.teavm.model.instructions.*; import org.teavm.model.instructions.*;
@ -31,20 +32,20 @@ public class MissingItemsProcessor {
private MethodHolder methodHolder; private MethodHolder methodHolder;
private Program program; private Program program;
private Collection<String> achievableClasses; private Collection<String> achievableClasses;
private Collection<MethodReference> achievableMethods; private Collection<MethodReference> reachableMethods;
private Collection<FieldReference> achievableFields; private Collection<FieldReference> achievableFields;
public MissingItemsProcessor(DependencyInfo dependencyInfo, Diagnostics diagnostics) { public MissingItemsProcessor(DependencyInfo dependencyInfo, Diagnostics diagnostics) {
this.dependencyInfo = dependencyInfo; this.dependencyInfo = dependencyInfo;
this.diagnostics = diagnostics; this.diagnostics = diagnostics;
achievableClasses = dependencyInfo.getReachableClasses(); achievableClasses = dependencyInfo.getReachableClasses();
achievableMethods = dependencyInfo.getReachableMethods(); reachableMethods = dependencyInfo.getReachableMethods();
achievableFields = dependencyInfo.getReachableFields(); achievableFields = dependencyInfo.getReachableFields();
} }
public void processClass(ClassHolder cls) { public void processClass(ClassHolder cls) {
for (MethodHolder method : cls.getMethods()) { for (MethodHolder method : cls.getMethods()) {
if (achievableMethods.contains(method.getReference()) && method.getProgram() != null) { if (reachableMethods.contains(method.getReference()) && method.getProgram() != null) {
processMethod(method); processMethod(method);
} }
} }
@ -148,9 +149,14 @@ public class MissingItemsProcessor {
if (!checkClass(location, method.getClassName())) { if (!checkClass(location, method.getClassName())) {
return false; return false;
} }
if (!achievableMethods.contains(method) || !dependencyInfo.getMethod(method).isMissing()) { if (!reachableMethods.contains(method)) {
return true; return true;
} }
MethodDependencyInfo methodDep = dependencyInfo.getMethod(method);
if (!methodDep.isMissing() || !methodDep.isUsed()) {
return true;
}
diagnostics.error(new CallLocation(methodHolder.getReference(), location), "Method {{m0}} was not found", diagnostics.error(new CallLocation(methodHolder.getReference(), location), "Method {{m0}} was not found",
method); method);
emitExceptionThrow(location, NoSuchMethodError.class.getName(), "Method not found: " + method); emitExceptionThrow(location, NoSuchMethodError.class.getName(), "Method not found: " + method);

View File

@ -398,6 +398,17 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
return cutClasses; return cutClasses;
} }
int index = 0; int index = 0;
for (String className : dependency.getReachableClasses()) {
ClassReader clsReader = dependency.getClassSource().get(className);
if (clsReader != null) {
linker.prepare(dependency, clsReader);
}
}
if (wasCancelled()) {
return cutClasses;
}
for (String className : dependency.getReachableClasses()) { for (String className : dependency.getReachableClasses()) {
ClassReader clsReader = dependency.getClassSource().get(className); ClassReader clsReader = dependency.getClassSource().get(className);
if (clsReader == null) { if (clsReader == null) {
@ -522,11 +533,11 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
method.setProgram(optimizedProgram); method.setProgram(optimizedProgram);
} }
private class MethodOptimizationContextImpl implements MethodOptimizationContext { class MethodOptimizationContextImpl implements MethodOptimizationContext {
private MethodReader method; private MethodReader method;
private ClassReaderSource classSource; private ClassReaderSource classSource;
public MethodOptimizationContextImpl(MethodReader method, ClassReaderSource classSource) { MethodOptimizationContextImpl(MethodReader method, ClassReaderSource classSource) {
this.method = method; this.method = method;
this.classSource = classSource; this.classSource = classSource;
} }

View File

@ -371,4 +371,26 @@ public class VMTest {
super(ONE); super(ONE);
} }
} }
@Test
public void indirectDefaultMethod() {
PathJoint o = new PathJoint();
assertEquals("SecondPath.foo", o.foo());
}
interface FirstPath {
default String foo() {
return "FirstPath.foo";
}
}
interface SecondPath extends FirstPath {
@Override
default String foo() {
return "SecondPath.foo";
}
}
class PathJoint implements FirstPath, SecondPath {
}
} }

View File

@ -16,6 +16,7 @@
package org.teavm.junit; package org.teavm.junit;
import com.gargoylesoftware.htmlunit.BrowserVersion; import com.gargoylesoftware.htmlunit.BrowserVersion;
import com.gargoylesoftware.htmlunit.Page;
import com.gargoylesoftware.htmlunit.WebClient; import com.gargoylesoftware.htmlunit.WebClient;
import com.gargoylesoftware.htmlunit.WebWindow; import com.gargoylesoftware.htmlunit.WebWindow;
import com.gargoylesoftware.htmlunit.html.HtmlPage; import com.gargoylesoftware.htmlunit.html.HtmlPage;
@ -69,7 +70,10 @@ class HtmlUnitRunStrategy implements TestRunStrategy {
} }
private void cleanUp() { private void cleanUp() {
page.get().cleanUp(); Page p = page.get();
if (p != null) {
p.cleanUp();
}
for (WebWindow window : webClient.get().getWebWindows()) { for (WebWindow window : webClient.get().getWebWindows()) {
window.getJobManager().removeAllJobs(); window.getJobManager().removeAllJobs();
} }