Properly handle checkcast to array type in dependency analyzer

This commit is contained in:
Alexey Andreev 2018-07-03 19:11:51 +03:00
parent 3653e39bec
commit 4e20a1de18
12 changed files with 149 additions and 21 deletions

View File

@ -20,6 +20,7 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
@ -109,7 +110,7 @@ public class ServiceLoaderSupport extends AbstractDependencyListener implements
}
private void parseServiceFile(DependencyAgent agent, String service, InputStream input) throws IOException {
BufferedReader reader = new BufferedReader(new InputStreamReader(input, "UTF-8"));
BufferedReader reader = new BufferedReader(new InputStreamReader(input, StandardCharsets.UTF_8));
while (true) {
String line = reader.readLine();
if (line == null) {
@ -119,12 +120,7 @@ public class ServiceLoaderSupport extends AbstractDependencyListener implements
if (line.startsWith("#") || line.isEmpty()) {
continue;
}
List<String> implementors = serviceMap.get(service);
if (implementors == null) {
implementors = new ArrayList<>();
serviceMap.put(service, implementors);
}
implementors.add(line);
serviceMap.computeIfAbsent(service, k -> new ArrayList<>()).add(line);
allClassesNode.propagate(agent.getType(line));
}
}
@ -133,7 +129,7 @@ public class ServiceLoaderSupport extends AbstractDependencyListener implements
public void methodReached(final DependencyAgent agent, MethodDependency method, final CallLocation location) {
MethodReference ref = method.getReference();
if (ref.getClassName().equals("java.util.ServiceLoader") && ref.getName().equals("loadServices")) {
method.getResult().propagate(agent.getType("[java.lang.Object"));
method.getResult().propagate(agent.getType("[Ljava/lang/Object;"));
allClassesNode.connect(method.getResult().getArrayItem());
method.getResult().getArrayItem().addConsumer(type -> initConstructor(agent, type.getName(), location));
}

View File

@ -76,6 +76,9 @@ public class ClassGenerator implements Generator, Injector, DependencyPlugin {
case "getInterfaces":
reachGetInterfaces(agent, method);
break;
case "getComponentType":
reachGetComponentType(agent, method);
break;
}
}
@ -109,6 +112,19 @@ public class ClassGenerator implements Generator, Injector, DependencyPlugin {
});
}
private void reachGetComponentType(DependencyAgent agent, MethodDependency method) {
method.getVariable(0).getClassValueNode().addConsumer(t -> {
if (!t.getName().startsWith("[")) {
return;
}
String typeName = t.getName().substring(1);
if (typeName.charAt(0) == 'L') {
typeName = ((ValueType.Object) ValueType.parse(typeName)).getClassName();
}
method.getResult().getClassValueNode().propagate(agent.getType(typeName));
});
}
private void generateCreateMetadata(GeneratorContext context, SourceWriter writer) throws IOException {
ReflectionDependencyListener reflection = context.getService(ReflectionDependencyListener.class);
for (String className : reflection.getClassesWithReflectableFields()) {

View File

@ -179,6 +179,7 @@ public class TClass<T> extends TObject implements TAnnotatedElement {
return (platformClass.getMetadata().getFlags() & Flags.INTERFACE) != 0;
}
@PluggableDependency(ClassGenerator.class)
public TClass<?> getComponentType() {
return getClass(Platform.getArrayItem(platformClass));
}

View File

@ -42,11 +42,16 @@ public class ArrayNativeGenerator implements Generator, DependencyPlugin {
case "getLength":
achieveGetLength(agent, method);
break;
case "newInstanceImpl":
method.getResult().propagate(agent.getType("[java.lang.Object"));
case "newInstance":
method.getVariable(1).getClassValueNode().addConsumer(t -> {
String arrayTypeName = t.getName().startsWith("[")
? t.getName()
: ValueType.object(t.getName()).toString();
method.getResult().propagate(agent.getType("[" + arrayTypeName));
});
break;
case "getImpl":
achieveGet(agent, method);
reachGet(agent, method);
break;
}
}
@ -121,7 +126,7 @@ public class ArrayNativeGenerator implements Generator, DependencyPlugin {
writer.outdent().append("}").softNewLine();
}
private void achieveGet(final DependencyAgent agent, final MethodDependency method) {
private void reachGet(DependencyAgent agent, MethodDependency method) {
method.getVariable(1).getArrayItem().connect(method.getResult());
method.getVariable(1).addConsumer(type -> {
if (type.getName().startsWith("[")) {

View File

@ -47,6 +47,7 @@ public final class TArray extends TObject {
return array.size;
}
@PluggableDependency(ArrayNativeGenerator.class)
public static TObject newInstance(TClass<?> componentType, int length) throws TNegativeArraySizeException {
if (componentType == null) {
throw new TNullPointerException();
@ -61,7 +62,6 @@ public final class TArray extends TObject {
}
@GeneratedBy(ArrayNativeGenerator.class)
@PluggableDependency(ArrayNativeGenerator.class)
@DelegateTo("newInstanceLowLevel")
private static native TObject newInstanceImpl(PlatformClass componentType, int length);

View File

@ -88,7 +88,7 @@ public class DependencyAnalyzer implements DependencyInfo {
Map<MethodReference, BootstrapMethodSubstitutor> bootstrapMethodSubstitutors = new HashMap<>();
Map<MethodReference, DependencyPlugin> dependencyPlugins = new HashMap<>();
private boolean completing;
private Map<String, SuperClassFilter> superClassFilters = new HashMap<>();
private Map<String, DependencyTypeFilter> superClassFilters = new HashMap<>();
boolean asyncSupported;
public DependencyAnalyzer(ClassReaderSource classSource, ClassLoader classLoader, ServiceRepository services,
@ -591,7 +591,9 @@ public class DependencyAnalyzer implements DependencyInfo {
if (tasks.isEmpty()) {
break;
}
while (!tasks.isEmpty()) {
tasks.remove().run();
}
if (++index == 100) {
if (interruptor != null && !interruptor.shouldContinue()) {
interrupted = true;
@ -729,7 +731,24 @@ public class DependencyAnalyzer implements DependencyInfo {
dependencyPlugins.put(method, dependencyPlugin);
}
SuperClassFilter getSuperClassFilter(String superClass) {
return superClassFilters.computeIfAbsent(superClass, s -> new SuperClassFilter(classSource, s));
DependencyTypeFilter getSuperClassFilter(String superClass) {
DependencyTypeFilter result = superClassFilters.get(superClass);
if (result == null) {
if (superClass.startsWith("[")) {
char second = superClass.charAt(1);
if (second == '[') {
result = new SuperArrayFilter(this, getSuperClassFilter(superClass.substring(1)));
} else if (second == 'L') {
ValueType.Object itemType = (ValueType.Object) ValueType.parse(superClass.substring(1));
result = new SuperArrayFilter(this, getSuperClassFilter(itemType.getClassName()));
} else {
result = new ExactTypeFilter(superClass);
}
} else {
result = new SuperClassFilter(classSource, superClass);
}
superClassFilters.put(superClass, result);
}
return result;
}
}

View File

@ -321,7 +321,7 @@ class DependencyGraphBuilder {
private final Set<MethodReference> knownMethods = new HashSet<>();
private final BitSet knownTypes = new BitSet();
private ExceptionConsumer exceptionConsumer;
private SuperClassFilter filter;
private DependencyTypeFilter filter;
VirtualCallConsumer(DependencyNode node, String filterClass,
MethodDescriptor methodDesc, DependencyAnalyzer analyzer, DependencyNode[] parameters,
@ -441,13 +441,27 @@ class DependencyGraphBuilder {
ClassReaderSource classSource = dependencyAnalyzer.getClassSource();
if (targetType instanceof ValueType.Object) {
String targetClsName = ((ValueType.Object) targetType).getClassName();
final ClassReader targetClass = classSource.get(targetClsName);
ClassReader targetClass = classSource.get(targetClsName);
if (targetClass != null && !(targetClass.getName().equals("java.lang.Object"))) {
if (valueNode != null && receiverNode != null) {
valueNode.connect(receiverNode, dependencyAnalyzer.getSuperClassFilter(targetClass.getName()));
}
return;
}
} else if (targetType instanceof ValueType.Array) {
ValueType itemType = targetType;
while (itemType instanceof ValueType.Array) {
itemType = ((ValueType.Array) itemType).getItemType();
}
if (itemType instanceof ValueType.Object) {
ClassReader targetClass = classSource.get(((ValueType.Object) itemType).getClassName());
if (targetClass == null) {
valueNode.connect(receiverNode);
return;
}
}
valueNode.connect(receiverNode, dependencyAnalyzer.getSuperClassFilter(targetType.toString()));
return;
}
if (valueNode != null && receiverNode != null) {
valueNode.connect(receiverNode);

View File

@ -33,7 +33,7 @@ public class DependencyNode implements ValueDependencyInfo {
boolean locked;
MethodReference method;
private ValueType typeFilter;
private SuperClassFilter cachedTypeFilter;
private DependencyTypeFilter cachedTypeFilter;
DependencyNode(DependencyAnalyzer dependencyAnalyzer, ValueType typeFilter) {
this(dependencyAnalyzer, typeFilter, 0);

View File

@ -0,0 +1,29 @@
/*
* Copyright 2018 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.dependency;
class ExactTypeFilter implements DependencyTypeFilter {
String typeName;
ExactTypeFilter(String typeName) {
this.typeName = typeName;
}
@Override
public boolean match(DependencyType type) {
return typeName.equals(type.getName());
}
}

View File

@ -0,0 +1,45 @@
/*
* Copyright 2018 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.dependency;
import org.teavm.model.ValueType;
class SuperArrayFilter implements DependencyTypeFilter {
private DependencyAnalyzer analyzer;
private DependencyTypeFilter itemTypeFilter;
SuperArrayFilter(DependencyAnalyzer analyzer, DependencyTypeFilter itemTypeFilter) {
this.analyzer = analyzer;
this.itemTypeFilter = itemTypeFilter;
}
@Override
public boolean match(DependencyType type) {
if (!type.getName().startsWith("[")) {
return false;
}
String typeName = type.getName().substring(1);
ValueType valueType = ValueType.parseIfPossible(typeName);
if (valueType == null || valueType instanceof ValueType.Primitive) {
return false;
}
if (valueType instanceof ValueType.Object) {
typeName = ((ValueType.Object) valueType).getClassName();
}
return itemTypeFilter.match(analyzer.getType(typeName));
}
}

View File

@ -43,7 +43,7 @@ public class AnnotationDependencySupport extends AbstractDependencyListener {
public void methodReached(DependencyAgent agent, MethodDependency method, CallLocation location) {
if (method.getReference().getClassName().equals(Platform.class.getName())
&& method.getReference().getName().equals("getAnnotations")) {
method.getResult().propagate(agent.getType("[" + Annotation.class.getName()));
method.getResult().propagate(agent.getType("[" + ValueType.parse(Annotation.class).toString()));
agent.linkMethod(new MethodReference(PlatformAnnotationProvider.class, "getAnnotations",
Annotation[].class), location);
allClasses.addConsumer(type -> {

View File

@ -51,6 +51,9 @@ public class PlatformGenerator implements Generator, Injector, DependencyPlugin
case "getCurrentThread":
method.getResult().propagate(agent.getType("java.lang.Thread"));
break;
case "getEnumConstants":
method.getResult().propagate(agent.getType("[Ljava/lang/Enum;"));
break;
}
}