Fix private method call resolution in Java 11

This commit is contained in:
Alexey Andreev 2022-11-11 10:56:20 +01:00
parent 5fb83ca2ac
commit 1fb70b0903
4 changed files with 50 additions and 6 deletions

View File

@ -22,6 +22,7 @@ import static org.teavm.dependency.AbstractInstructionAnalyzer.MONITOR_EXIT_SYNC
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.teavm.model.AccessLevel;
import org.teavm.model.BasicBlockReader;
import org.teavm.model.CallLocation;
import org.teavm.model.ClassHierarchy;
@ -31,6 +32,7 @@ import org.teavm.model.ElementModifier;
import org.teavm.model.IncomingReader;
import org.teavm.model.MethodDescriptor;
import org.teavm.model.MethodHolder;
import org.teavm.model.MethodReader;
import org.teavm.model.MethodReference;
import org.teavm.model.PhiReader;
import org.teavm.model.Program;
@ -368,6 +370,15 @@ class DependencyGraphBuilder {
@Override
protected void invokeVirtual(VariableReader receiver, VariableReader instance, MethodReference method,
List<? extends VariableReader> arguments) {
ClassReader cls = dependencyAnalyzer.getClassSource().get(method.getClassName());
if (cls != null) {
MethodReader methodHolder = cls.getMethod(method.getDescriptor());
if (methodHolder != null && methodHolder.getLevel() == AccessLevel.PRIVATE) {
invokeSpecial(receiver, instance, method, arguments);
return;
}
}
if (handleSpecialMethod(receiver, instance, method)) {
return;
}
@ -377,7 +388,7 @@ class DependencyGraphBuilder {
actualArgs[i + 1] = nodes[arguments.get(i).getIndex()];
}
actualArgs[0] = getNode(instance);
DependencyConsumer listener = new VirtualCallConsumer(getNode(instance),
DependencyConsumer listener = new VirtualCallConsumer(
method.getClassName(), method.getDescriptor(), dependencyAnalyzer, actualArgs,
receiver != null ? getNode(receiver) : null, getCallLocation(),
currentExceptionConsumer);

View File

@ -16,7 +16,10 @@
package org.teavm.dependency;
import java.util.List;
import org.teavm.model.AccessLevel;
import org.teavm.model.CallLocation;
import org.teavm.model.ClassReader;
import org.teavm.model.MethodReader;
import org.teavm.model.MethodReference;
import org.teavm.model.VariableReader;
@ -46,6 +49,14 @@ class FastInstructionAnalyzer extends AbstractInstructionAnalyzer {
@Override
protected void invokeVirtual(VariableReader receiver, VariableReader instance, MethodReference method,
List<? extends VariableReader> arguments) {
ClassReader cls = dependencyAnalyzer.getClassSource().get(method.getClassName());
if (cls != null) {
MethodReader methodHolder = cls.getMethod(method.getDescriptor());
if (methodHolder != null && methodHolder.getLevel() == AccessLevel.PRIVATE) {
invokeSpecial(receiver, instance, method, arguments);
return;
}
}
invokeGetClass(method);
FastVirtualCallConsumer consumer = dependencyAnalyzer.getVirtualCallConsumer(method);
consumer.addLocation(impreciseLocation);

View File

@ -20,7 +20,6 @@ import org.teavm.model.CallLocation;
import org.teavm.model.MethodDescriptor;
class VirtualCallConsumer implements DependencyConsumer {
private final DependencyNode node;
private final MethodDescriptor methodDesc;
private final DependencyAnalyzer analyzer;
private final DependencyNode[] parameters;
@ -32,11 +31,9 @@ class VirtualCallConsumer implements DependencyConsumer {
private boolean isPolymorphic;
private MethodDependency monomorphicCall;
VirtualCallConsumer(DependencyNode node, String filterClass,
MethodDescriptor methodDesc, DependencyAnalyzer analyzer, DependencyNode[] parameters,
DependencyNode result, CallLocation location,
VirtualCallConsumer(String filterClass, MethodDescriptor methodDesc, DependencyAnalyzer analyzer,
DependencyNode[] parameters, DependencyNode result, CallLocation location,
DependencyGraphBuilder.ExceptionConsumer exceptionConsumer) {
this.node = node;
this.filter = analyzer.getSuperClassFilter(filterClass);
this.methodDesc = methodDesc;
this.analyzer = analyzer;

View File

@ -634,4 +634,29 @@ public class VMTest {
}
return result;
}
@Test
public void virtualCallWithPrivateMethods() {
assertEquals("ap", callA(new B()));
}
private static String callA(A a) {
return a.a();
}
static class A {
String a() {
return "a" + p();
}
private String p() {
return "p";
}
}
static class B extends A {
private String p() {
return "q";
}
}
}