mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2025-01-03 05:44:10 -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.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);
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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) {
|
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) {
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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());
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user