Fix abstract classes are considered by dependency analysis when constructed by reflection

Also fix #592
This commit is contained in:
Alexey Andreev 2022-07-13 12:48:34 +03:00
parent f0d805fda8
commit 7fe79a1768
4 changed files with 43 additions and 4 deletions

View File

@ -201,13 +201,22 @@ public class ReflectionDependencyListener extends AbstractDependencyListener {
.addLocation(location) .addLocation(location)
.getVariable(0).getClassValueNode(); .getVariable(0).getClassValueNode();
classValueNode.addConsumer(reflectedType -> { classValueNode.addConsumer(reflectedType -> {
if (reflectedType.getName().startsWith("[")) { if (reflectedType.getName().startsWith("[") || reflectedType.getName().startsWith("~")) {
return;
}
ClassReader cls = agent.getClassSource().get(reflectedType.getName());
if (cls == null || cls.hasModifier(ElementModifier.ABSTRACT)
|| cls.hasModifier(ElementModifier.INTERFACE)) {
return; return;
} }
Set<MethodDescriptor> accessibleMethods = getAccessibleMethods(agent, reflectedType.getName()); Set<MethodDescriptor> accessibleMethods = getAccessibleMethods(agent, reflectedType.getName());
ClassReader cls = agent.getClassSource().get(reflectedType.getName());
for (MethodDescriptor methodDescriptor : accessibleMethods) { for (MethodDescriptor methodDescriptor : accessibleMethods) {
if (!methodDescriptor.getName().equals("<init>")) {
continue;
}
MethodReader calledMethod = cls.getMethod(methodDescriptor); MethodReader calledMethod = cls.getMethod(methodDescriptor);
MethodDependency calledMethodDep = agent.linkMethod(calledMethod.getReference()).addLocation(location); MethodDependency calledMethodDep = agent.linkMethod(calledMethod.getReference()).addLocation(location);
calledMethodDep.use(); calledMethodDep.use();
@ -218,8 +227,9 @@ public class ReflectionDependencyListener extends AbstractDependencyListener {
calledMethodDep.getVariable(0).propagate(reflectedType); calledMethodDep.getVariable(0).propagate(reflectedType);
linkClassIfNecessary(agent, calledMethod, location); linkClassIfNecessary(agent, calledMethod, location);
} }
method.getResult().propagate(reflectedType);
}); });
classValueNode.connect(method.getResult());
} }
private void handleInvoke(DependencyAgent agent, MethodDependency method) { private void handleInvoke(DependencyAgent agent, MethodDependency method) {
@ -235,6 +245,9 @@ public class ReflectionDependencyListener extends AbstractDependencyListener {
Set<MethodDescriptor> accessibleMethods = getAccessibleMethods(agent, reflectedType.getName()); Set<MethodDescriptor> accessibleMethods = getAccessibleMethods(agent, reflectedType.getName());
ClassReader cls = agent.getClassSource().get(reflectedType.getName()); ClassReader cls = agent.getClassSource().get(reflectedType.getName());
for (MethodDescriptor methodDescriptor : accessibleMethods) { for (MethodDescriptor methodDescriptor : accessibleMethods) {
if (methodDescriptor.getName().equals("<init>")) {
continue;
}
MethodReader calledMethod = cls.getMethod(methodDescriptor); MethodReader calledMethod = cls.getMethod(methodDescriptor);
MethodDependency calledMethodDep = agent.linkMethod(calledMethod.getReference()).addLocation(location); MethodDependency calledMethodDep = agent.linkMethod(calledMethod.getReference()).addLocation(location);
calledMethodDep.use(); calledMethodDep.use();

View File

@ -67,7 +67,16 @@ public class ClassGenerator implements Generator, Injector, DependencyPlugin {
public void methodReached(DependencyAgent agent, MethodDependency method) { public void methodReached(DependencyAgent agent, MethodDependency method) {
switch (method.getReference().getName()) { switch (method.getReference().getName()) {
case "newEmptyInstance": case "newEmptyInstance":
method.getVariable(0).getClassValueNode().connect(method.getResult()); method.getVariable(0).getClassValueNode().addConsumer(type -> {
String className = type.getName();
if (!className.startsWith("[") && !className.startsWith("~")) {
ClassReader cls = agent.getClassSource().get(className);
if (cls != null && !cls.hasModifier(ElementModifier.ABSTRACT)
&& !cls.hasModifier(ElementModifier.INTERFACE)) {
method.getResult().propagate(type);
}
}
});
break; break;
case "getSuperclass": case "getSuperclass":
reachGetSuperclass(agent, method); reachGetSuperclass(agent, method);

View File

@ -108,6 +108,11 @@ public class DependencyTest {
doTest(); doTest();
} }
@Test
public void reflectionConstructor() {
doTest();
}
private void doTest() { private void doTest() {
TeaVM vm = new TeaVMBuilder(new JavaScriptTarget()) TeaVM vm = new TeaVMBuilder(new JavaScriptTarget())
.setClassLoader(DependencyTest.class.getClassLoader()) .setClassLoader(DependencyTest.class.getClassLoader())

View File

@ -15,6 +15,10 @@
*/ */
package org.teavm.dependency; package org.teavm.dependency;
import java.lang.reflect.InvocationTargetException;
import java.util.AbstractList;
import java.util.ArrayList;
public class DependencyTestData { public class DependencyTestData {
private DependencyTestData() { private DependencyTestData() {
} }
@ -114,6 +118,14 @@ public class DependencyTestData {
} }
} }
public static void reflectionConstructor() throws Exception {
var classes = new Class<?>[] { AbstractList.class, ArrayList.class };
for (var cls : classes) {
var instance = cls.getConstructor().newInstance();
MetaAssertions.assertTypes(instance, ArrayList.class);
}
}
interface I { interface I {
Object foo(); Object foo();
} }