From 4e20a1de18b7e3b81fcec9edfa50927fb90b7666 Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Tue, 3 Jul 2018 19:11:51 +0300 Subject: [PATCH] Properly handle checkcast to array type in dependency analyzer --- .../classlib/impl/ServiceLoaderSupport.java | 12 ++--- .../classlib/java/lang/ClassGenerator.java | 16 +++++++ .../org/teavm/classlib/java/lang/TClass.java | 1 + .../lang/reflect/ArrayNativeGenerator.java | 13 ++++-- .../classlib/java/lang/reflect/TArray.java | 2 +- .../teavm/dependency/DependencyAnalyzer.java | 27 +++++++++-- .../dependency/DependencyGraphBuilder.java | 18 +++++++- .../org/teavm/dependency/DependencyNode.java | 2 +- .../org/teavm/dependency/ExactTypeFilter.java | 29 ++++++++++++ .../teavm/dependency/SuperArrayFilter.java | 45 +++++++++++++++++++ .../plugin/AnnotationDependencySupport.java | 2 +- .../platform/plugin/PlatformGenerator.java | 3 ++ 12 files changed, 149 insertions(+), 21 deletions(-) create mode 100644 core/src/main/java/org/teavm/dependency/ExactTypeFilter.java create mode 100644 core/src/main/java/org/teavm/dependency/SuperArrayFilter.java diff --git a/classlib/src/main/java/org/teavm/classlib/impl/ServiceLoaderSupport.java b/classlib/src/main/java/org/teavm/classlib/impl/ServiceLoaderSupport.java index 47063b316..92c16b3e0 100644 --- a/classlib/src/main/java/org/teavm/classlib/impl/ServiceLoaderSupport.java +++ b/classlib/src/main/java/org/teavm/classlib/impl/ServiceLoaderSupport.java @@ -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 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)); } diff --git a/classlib/src/main/java/org/teavm/classlib/java/lang/ClassGenerator.java b/classlib/src/main/java/org/teavm/classlib/java/lang/ClassGenerator.java index 3e08148d3..79f88e3dd 100644 --- a/classlib/src/main/java/org/teavm/classlib/java/lang/ClassGenerator.java +++ b/classlib/src/main/java/org/teavm/classlib/java/lang/ClassGenerator.java @@ -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()) { diff --git a/classlib/src/main/java/org/teavm/classlib/java/lang/TClass.java b/classlib/src/main/java/org/teavm/classlib/java/lang/TClass.java index 3bdbff0fa..55ebbc1c4 100644 --- a/classlib/src/main/java/org/teavm/classlib/java/lang/TClass.java +++ b/classlib/src/main/java/org/teavm/classlib/java/lang/TClass.java @@ -179,6 +179,7 @@ public class TClass extends TObject implements TAnnotatedElement { return (platformClass.getMetadata().getFlags() & Flags.INTERFACE) != 0; } + @PluggableDependency(ClassGenerator.class) public TClass getComponentType() { return getClass(Platform.getArrayItem(platformClass)); } diff --git a/classlib/src/main/java/org/teavm/classlib/java/lang/reflect/ArrayNativeGenerator.java b/classlib/src/main/java/org/teavm/classlib/java/lang/reflect/ArrayNativeGenerator.java index e76415b2b..f89e411d4 100644 --- a/classlib/src/main/java/org/teavm/classlib/java/lang/reflect/ArrayNativeGenerator.java +++ b/classlib/src/main/java/org/teavm/classlib/java/lang/reflect/ArrayNativeGenerator.java @@ -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("[")) { diff --git a/classlib/src/main/java/org/teavm/classlib/java/lang/reflect/TArray.java b/classlib/src/main/java/org/teavm/classlib/java/lang/reflect/TArray.java index 23e964db6..6aa9971da 100644 --- a/classlib/src/main/java/org/teavm/classlib/java/lang/reflect/TArray.java +++ b/classlib/src/main/java/org/teavm/classlib/java/lang/reflect/TArray.java @@ -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); diff --git a/core/src/main/java/org/teavm/dependency/DependencyAnalyzer.java b/core/src/main/java/org/teavm/dependency/DependencyAnalyzer.java index 20ee371d8..e445eb3ee 100644 --- a/core/src/main/java/org/teavm/dependency/DependencyAnalyzer.java +++ b/core/src/main/java/org/teavm/dependency/DependencyAnalyzer.java @@ -88,7 +88,7 @@ public class DependencyAnalyzer implements DependencyInfo { Map bootstrapMethodSubstitutors = new HashMap<>(); Map dependencyPlugins = new HashMap<>(); private boolean completing; - private Map superClassFilters = new HashMap<>(); + private Map 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; } - tasks.remove().run(); + 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; } } \ No newline at end of file diff --git a/core/src/main/java/org/teavm/dependency/DependencyGraphBuilder.java b/core/src/main/java/org/teavm/dependency/DependencyGraphBuilder.java index 040d59e81..cb6f3bef7 100644 --- a/core/src/main/java/org/teavm/dependency/DependencyGraphBuilder.java +++ b/core/src/main/java/org/teavm/dependency/DependencyGraphBuilder.java @@ -321,7 +321,7 @@ class DependencyGraphBuilder { private final Set 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); diff --git a/core/src/main/java/org/teavm/dependency/DependencyNode.java b/core/src/main/java/org/teavm/dependency/DependencyNode.java index fd1cc99fe..04b4c7ba6 100644 --- a/core/src/main/java/org/teavm/dependency/DependencyNode.java +++ b/core/src/main/java/org/teavm/dependency/DependencyNode.java @@ -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); diff --git a/core/src/main/java/org/teavm/dependency/ExactTypeFilter.java b/core/src/main/java/org/teavm/dependency/ExactTypeFilter.java new file mode 100644 index 000000000..50ba55da2 --- /dev/null +++ b/core/src/main/java/org/teavm/dependency/ExactTypeFilter.java @@ -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()); + } +} diff --git a/core/src/main/java/org/teavm/dependency/SuperArrayFilter.java b/core/src/main/java/org/teavm/dependency/SuperArrayFilter.java new file mode 100644 index 000000000..f532b177b --- /dev/null +++ b/core/src/main/java/org/teavm/dependency/SuperArrayFilter.java @@ -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)); + } +} diff --git a/platform/src/main/java/org/teavm/platform/plugin/AnnotationDependencySupport.java b/platform/src/main/java/org/teavm/platform/plugin/AnnotationDependencySupport.java index 9382d9f4e..035793da7 100644 --- a/platform/src/main/java/org/teavm/platform/plugin/AnnotationDependencySupport.java +++ b/platform/src/main/java/org/teavm/platform/plugin/AnnotationDependencySupport.java @@ -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 -> { diff --git a/platform/src/main/java/org/teavm/platform/plugin/PlatformGenerator.java b/platform/src/main/java/org/teavm/platform/plugin/PlatformGenerator.java index 9c1f89b49..498ab45a9 100644 --- a/platform/src/main/java/org/teavm/platform/plugin/PlatformGenerator.java +++ b/platform/src/main/java/org/teavm/platform/plugin/PlatformGenerator.java @@ -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; } }