mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 08:14:09 -08:00
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:
parent
600151a69b
commit
de14a57fe1
|
@ -84,7 +84,7 @@ public class DependencyChecker implements DependencyInfo {
|
|||
this.classSource = new DependencyClassSource(classSource, diagnostics);
|
||||
this.classLoader = classLoader;
|
||||
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));
|
||||
methodCache = new CachedMapper<>(preimage -> {
|
||||
MethodHolder method = methodReaderCache.map(preimage);
|
||||
|
|
|
@ -305,7 +305,7 @@ class DependencyGraphBuilder {
|
|||
|
||||
private static class VirtualCallConsumer implements DependencyConsumer {
|
||||
private final DependencyNode node;
|
||||
private final ClassReader filterClass;
|
||||
private final String filterClass;
|
||||
private final MethodDescriptor methodDesc;
|
||||
private final DependencyChecker checker;
|
||||
private final DependencyNode[] parameters;
|
||||
|
@ -315,7 +315,7 @@ class DependencyGraphBuilder {
|
|||
private final Set<MethodReference> knownMethods = new HashSet<>();
|
||||
private ExceptionConsumer exceptionConsumer;
|
||||
|
||||
public VirtualCallConsumer(DependencyNode node, ClassReader filterClass,
|
||||
public VirtualCallConsumer(DependencyNode node, String filterClass,
|
||||
MethodDescriptor methodDesc, DependencyChecker checker, DependencyNode[] parameters,
|
||||
DependencyNode result, DefaultCallGraphNode caller, TextLocation location,
|
||||
ExceptionConsumer exceptionConsumer) {
|
||||
|
@ -342,7 +342,7 @@ class DependencyGraphBuilder {
|
|||
}
|
||||
|
||||
ClassReaderSource classSource = checker.getClassSource();
|
||||
if (!classSource.isSuperType(filterClass.getName(), className).orElse(false)) {
|
||||
if (!classSource.isSuperType(filterClass, className).orElse(false)) {
|
||||
return;
|
||||
}
|
||||
MethodReference methodRef = new MethodReference(className, methodDesc);
|
||||
|
@ -642,19 +642,13 @@ class DependencyGraphBuilder {
|
|||
|
||||
private void invokeVirtual(VariableReader receiver, VariableReader instance, MethodReference method,
|
||||
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];
|
||||
for (int i = 0; i < arguments.size(); ++i) {
|
||||
actualArgs[i + 1] = nodes[arguments.get(i).getIndex()];
|
||||
}
|
||||
actualArgs[0] = nodes[instance.getIndex()];
|
||||
DependencyConsumer listener = new VirtualCallConsumer(nodes[instance.getIndex()],
|
||||
dependencyChecker.getClassSource().get(methodDep.getMethod().getOwnerName()),
|
||||
method.getDescriptor(), dependencyChecker, actualArgs,
|
||||
method.getClassName(), method.getDescriptor(), dependencyChecker, actualArgs,
|
||||
receiver != null ? nodes[receiver.getIndex()] : null, caller, currentLocation,
|
||||
currentExceptionConsumer);
|
||||
nodes[instance.getIndex()].addConsumer(listener);
|
||||
|
|
|
@ -15,27 +15,66 @@
|
|||
*/
|
||||
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.BasicBlockReader;
|
||||
import org.teavm.model.ClassHolder;
|
||||
import org.teavm.model.ClassReader;
|
||||
import org.teavm.model.ElementModifier;
|
||||
import org.teavm.model.FieldHolder;
|
||||
import org.teavm.model.FieldReference;
|
||||
import org.teavm.model.Instruction;
|
||||
import org.teavm.model.MethodHolder;
|
||||
import org.teavm.model.MethodReader;
|
||||
import org.teavm.model.MethodReference;
|
||||
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.InitClassInstruction;
|
||||
import org.teavm.model.instructions.InvocationType;
|
||||
import org.teavm.model.instructions.InvokeInstruction;
|
||||
import org.teavm.model.instructions.PutFieldInstruction;
|
||||
|
||||
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) {
|
||||
for (MethodHolder method : cls.getMethods().toArray(new MethodHolder[0])) {
|
||||
MethodReference methodRef = new MethodReference(cls.getName(), method.getDescriptor());
|
||||
MethodDependencyInfo methodDep = dependency.getMethod(methodRef);
|
||||
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()) {
|
||||
method.getModifiers().add(ElementModifier.ABSTRACT);
|
||||
method.setProgram(null);
|
||||
|
@ -58,9 +97,11 @@ public class Linker {
|
|||
for (Instruction insn : block) {
|
||||
if (insn instanceof InvokeInstruction) {
|
||||
InvokeInstruction invoke = (InvokeInstruction) insn;
|
||||
MethodDependencyInfo linkedMethod = dependency.getMethodImplementation(invoke.getMethod());
|
||||
if (linkedMethod != null) {
|
||||
invoke.setMethod(linkedMethod.getReference());
|
||||
if (invoke.getType() == InvocationType.SPECIAL) {
|
||||
MethodDependencyInfo linkedMethod = dependency.getMethodImplementation(invoke.getMethod());
|
||||
if (linkedMethod != null) {
|
||||
invoke.setMethod(linkedMethod.getReference());
|
||||
}
|
||||
}
|
||||
} else if (insn instanceof GetFieldInstruction) {
|
||||
GetFieldInstruction getField = (GetFieldInstruction) insn;
|
||||
|
|
|
@ -33,11 +33,15 @@ public interface ClassHolderSource extends ClassReaderSource {
|
|||
return (MethodHolder) resolve(method);
|
||||
}
|
||||
|
||||
default MethodHolder resolveMutableImplementation(MethodReference method) {
|
||||
return (MethodHolder) resolveImplementation(method);
|
||||
}
|
||||
|
||||
default FieldHolder resolveMutable(FieldReference field) {
|
||||
return (FieldHolder) resolve(field);
|
||||
}
|
||||
|
||||
default Stream<MethodHolder> mutableOverridenMethods(MethodReference method) {
|
||||
default Stream<MethodHolder> mutableOverriddenMethods(MethodReference method) {
|
||||
return overriddenMethods(method).map(m -> (MethodHolder) m);
|
||||
}
|
||||
}
|
|
@ -19,6 +19,7 @@ import java.util.ArrayDeque;
|
|||
import java.util.Deque;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Stream;
|
||||
|
@ -89,14 +90,19 @@ public interface ClassReaderSource {
|
|||
default MethodReader resolve(MethodReference method) {
|
||||
return getAncestors(method.getClassName())
|
||||
.map(cls -> cls.getMethod(method.getDescriptor()))
|
||||
.filter(candidate -> candidate != null)
|
||||
.filter(Objects::nonNull)
|
||||
.findFirst().orElse(null);
|
||||
}
|
||||
|
||||
default MethodReader resolveImplementation(MethodReference methodReference) {
|
||||
return ClassReaderSourceHelper.resolveMethodImplementation(this, methodReference.getClassName(),
|
||||
methodReference.getDescriptor(), new HashSet<>());
|
||||
}
|
||||
|
||||
default FieldReader resolve(FieldReference field) {
|
||||
return getAncestors(field.getClassName())
|
||||
.map(cls -> cls.getField(field.getFieldName()))
|
||||
.filter(candidate -> candidate != null)
|
||||
.filter(Objects::nonNull)
|
||||
.findFirst().orElse(null);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -165,8 +165,7 @@ public class ClassInference {
|
|||
private void propagate(Program program) {
|
||||
ClassReaderSource classSource = dependencyInfo.getClassSource();
|
||||
|
||||
Queue<Task> queue = new ArrayDeque<>();
|
||||
queue.addAll(initialTasks);
|
||||
Queue<Task> queue = new ArrayDeque<>(initialTasks);
|
||||
initialTasks = null;
|
||||
|
||||
while (!queue.isEmpty()) {
|
||||
|
@ -239,7 +238,7 @@ public class ClassInference {
|
|||
for (VirtualCallSite callSite : callSites) {
|
||||
MethodReference rawMethod = new MethodReference(task.className,
|
||||
callSite.method.getDescriptor());
|
||||
MethodReader resolvedMethod = classSource.resolve(rawMethod);
|
||||
MethodReader resolvedMethod = classSource.resolveImplementation(rawMethod);
|
||||
if (resolvedMethod == null) {
|
||||
continue;
|
||||
}
|
||||
|
@ -295,10 +294,10 @@ public class ClassInference {
|
|||
GraphBuilder arrayGraphBuilder;
|
||||
GraphBuilder itemGraphBuilder;
|
||||
MethodDependencyInfo thisMethodDep;
|
||||
List<IntObjectMap<ValueType>> casts = new ArrayList<>();
|
||||
List<IntObjectMap<ValueType>> casts;
|
||||
IntObjectMap<IntSet> exceptionMap = new IntObjectOpenHashMap<>();
|
||||
List<Task> tasks = new ArrayList<>();
|
||||
List<List<VirtualCallSite>> virtualCallSites = new ArrayList<>();
|
||||
List<List<VirtualCallSite>> virtualCallSites;
|
||||
BasicBlock currentBlock;
|
||||
|
||||
GraphBuildingVisitor(int variableCount, DependencyInfo dependencyInfo) {
|
||||
|
|
|
@ -19,7 +19,6 @@ import org.teavm.model.BasicBlock;
|
|||
import org.teavm.model.ClassReaderSource;
|
||||
import org.teavm.model.FieldReference;
|
||||
import org.teavm.model.Incoming;
|
||||
import org.teavm.model.MethodReader;
|
||||
import org.teavm.model.MethodReference;
|
||||
import org.teavm.model.Phi;
|
||||
import org.teavm.model.PrimitiveType;
|
||||
|
@ -437,10 +436,6 @@ public class ValueEmitter {
|
|||
.orElse(true)) {
|
||||
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;
|
||||
if (method.getReturnType() != ValueType.VOID) {
|
||||
|
|
|
@ -20,7 +20,13 @@ import java.util.Set;
|
|||
import org.teavm.dependency.DependencyInfo;
|
||||
import org.teavm.dependency.MethodDependencyInfo;
|
||||
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.InvokeInstruction;
|
||||
|
||||
|
|
|
@ -324,7 +324,7 @@ public class Inlining {
|
|||
Set<MethodReference> implementations = new HashSet<>();
|
||||
for (String className : inference.classesOf(invoke.getInstance().getIndex())) {
|
||||
MethodReference rawMethod = new MethodReference(className, invoke.getMethod().getDescriptor());
|
||||
MethodReader resolvedMethod = dependencyInfo.getClassSource().resolve(rawMethod);
|
||||
MethodReader resolvedMethod = dependencyInfo.getClassSource().resolveImplementation(rawMethod);
|
||||
if (resolvedMethod != null) {
|
||||
implementations.add(resolvedMethod.getReference());
|
||||
}
|
||||
|
|
|
@ -90,7 +90,7 @@ public class AsyncMethodFinder {
|
|||
}
|
||||
}
|
||||
for (MethodReference method : asyncMethods) {
|
||||
addOverridenToFamily(method);
|
||||
addOverriddenToFamily(method);
|
||||
}
|
||||
for (String clsName : classSource.getClassNames()) {
|
||||
ClassReader cls = classSource.get(clsName);
|
||||
|
@ -175,11 +175,11 @@ public class AsyncMethodFinder {
|
|||
}
|
||||
}
|
||||
|
||||
private class CallStack {
|
||||
static class CallStack {
|
||||
MethodReference method;
|
||||
CallStack next;
|
||||
|
||||
public CallStack(MethodReference method, CallStack next) {
|
||||
CallStack(MethodReference method, CallStack next) {
|
||||
this.method = method;
|
||||
this.next = next;
|
||||
}
|
||||
|
@ -196,14 +196,14 @@ public class AsyncMethodFinder {
|
|||
}
|
||||
}
|
||||
|
||||
private void addOverridenToFamily(MethodReference methodRef) {
|
||||
private void addOverriddenToFamily(MethodReference methodRef) {
|
||||
asyncFamilyMethods.put(methodRef, true);
|
||||
ClassReader cls = classSource.get(methodRef.getClassName());
|
||||
if (cls == null) {
|
||||
return;
|
||||
}
|
||||
for (MethodReference overridenMethod : findOverriddenMethods(cls, methodRef)) {
|
||||
addOverridenToFamily(overridenMethod);
|
||||
for (MethodReference overriddenMethod : findOverriddenMethods(cls, methodRef)) {
|
||||
addOverriddenToFamily(overriddenMethod);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -225,8 +225,8 @@ public class AsyncMethodFinder {
|
|||
if (cls == null) {
|
||||
return false;
|
||||
}
|
||||
for (MethodReference overridenMethod : findOverriddenMethods(cls, methodRef)) {
|
||||
if (addToFamily(overridenMethod)) {
|
||||
for (MethodReference overriddenMethod : findOverriddenMethods(cls, methodRef)) {
|
||||
if (addToFamily(overriddenMethod)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -241,11 +241,11 @@ public class AsyncMethodFinder {
|
|||
parents.addAll(cls.getInterfaces());
|
||||
|
||||
Set<MethodReference> visited = new HashSet<>();
|
||||
Set<MethodReference> overriden = new HashSet<>();
|
||||
Set<MethodReference> overridden = new HashSet<>();
|
||||
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,
|
||||
|
|
|
@ -19,7 +19,6 @@ import java.util.ArrayDeque;
|
|||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Queue;
|
||||
|
@ -48,7 +47,7 @@ public class AsyncProgramSplitter {
|
|||
private List<Part> parts = new ArrayList<>();
|
||||
private Map<Instruction, Integer> partMap = new HashMap<>();
|
||||
private ClassReaderSource classSource;
|
||||
private Set<MethodReference> asyncMethods = new HashSet<>();
|
||||
private Set<MethodReference> asyncMethods;
|
||||
private Program program;
|
||||
|
||||
public AsyncProgramSplitter(ClassReaderSource classSource, Set<MethodReference> asyncMethods) {
|
||||
|
|
|
@ -19,6 +19,7 @@ import java.util.ArrayList;
|
|||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import org.teavm.dependency.DependencyInfo;
|
||||
import org.teavm.dependency.MethodDependencyInfo;
|
||||
import org.teavm.diagnostics.Diagnostics;
|
||||
import org.teavm.model.*;
|
||||
import org.teavm.model.instructions.*;
|
||||
|
@ -31,20 +32,20 @@ public class MissingItemsProcessor {
|
|||
private MethodHolder methodHolder;
|
||||
private Program program;
|
||||
private Collection<String> achievableClasses;
|
||||
private Collection<MethodReference> achievableMethods;
|
||||
private Collection<MethodReference> reachableMethods;
|
||||
private Collection<FieldReference> achievableFields;
|
||||
|
||||
public MissingItemsProcessor(DependencyInfo dependencyInfo, Diagnostics diagnostics) {
|
||||
this.dependencyInfo = dependencyInfo;
|
||||
this.diagnostics = diagnostics;
|
||||
achievableClasses = dependencyInfo.getReachableClasses();
|
||||
achievableMethods = dependencyInfo.getReachableMethods();
|
||||
reachableMethods = dependencyInfo.getReachableMethods();
|
||||
achievableFields = dependencyInfo.getReachableFields();
|
||||
}
|
||||
|
||||
public void processClass(ClassHolder cls) {
|
||||
for (MethodHolder method : cls.getMethods()) {
|
||||
if (achievableMethods.contains(method.getReference()) && method.getProgram() != null) {
|
||||
if (reachableMethods.contains(method.getReference()) && method.getProgram() != null) {
|
||||
processMethod(method);
|
||||
}
|
||||
}
|
||||
|
@ -148,9 +149,14 @@ public class MissingItemsProcessor {
|
|||
if (!checkClass(location, method.getClassName())) {
|
||||
return false;
|
||||
}
|
||||
if (!achievableMethods.contains(method) || !dependencyInfo.getMethod(method).isMissing()) {
|
||||
if (!reachableMethods.contains(method)) {
|
||||
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",
|
||||
method);
|
||||
emitExceptionThrow(location, NoSuchMethodError.class.getName(), "Method not found: " + method);
|
||||
|
|
|
@ -398,6 +398,17 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
|
|||
return cutClasses;
|
||||
}
|
||||
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()) {
|
||||
ClassReader clsReader = dependency.getClassSource().get(className);
|
||||
if (clsReader == null) {
|
||||
|
@ -522,11 +533,11 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
|
|||
method.setProgram(optimizedProgram);
|
||||
}
|
||||
|
||||
private class MethodOptimizationContextImpl implements MethodOptimizationContext {
|
||||
class MethodOptimizationContextImpl implements MethodOptimizationContext {
|
||||
private MethodReader method;
|
||||
private ClassReaderSource classSource;
|
||||
|
||||
public MethodOptimizationContextImpl(MethodReader method, ClassReaderSource classSource) {
|
||||
MethodOptimizationContextImpl(MethodReader method, ClassReaderSource classSource) {
|
||||
this.method = method;
|
||||
this.classSource = classSource;
|
||||
}
|
||||
|
|
|
@ -371,4 +371,26 @@ public class VMTest {
|
|||
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 {
|
||||
}
|
||||
}
|
|
@ -16,6 +16,7 @@
|
|||
package org.teavm.junit;
|
||||
|
||||
import com.gargoylesoftware.htmlunit.BrowserVersion;
|
||||
import com.gargoylesoftware.htmlunit.Page;
|
||||
import com.gargoylesoftware.htmlunit.WebClient;
|
||||
import com.gargoylesoftware.htmlunit.WebWindow;
|
||||
import com.gargoylesoftware.htmlunit.html.HtmlPage;
|
||||
|
@ -69,7 +70,10 @@ class HtmlUnitRunStrategy implements TestRunStrategy {
|
|||
}
|
||||
|
||||
private void cleanUp() {
|
||||
page.get().cleanUp();
|
||||
Page p = page.get();
|
||||
if (p != null) {
|
||||
p.cleanUp();
|
||||
}
|
||||
for (WebWindow window : webClient.get().getWebWindows()) {
|
||||
window.getJobManager().removeAllJobs();
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user