mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 16:14:10 -08:00
Properly handle checkcast to array type in dependency analyzer
This commit is contained in:
parent
3653e39bec
commit
4e20a1de18
|
@ -20,6 +20,7 @@ import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Enumeration;
|
import java.util.Enumeration;
|
||||||
import java.util.HashMap;
|
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 {
|
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) {
|
while (true) {
|
||||||
String line = reader.readLine();
|
String line = reader.readLine();
|
||||||
if (line == null) {
|
if (line == null) {
|
||||||
|
@ -119,12 +120,7 @@ public class ServiceLoaderSupport extends AbstractDependencyListener implements
|
||||||
if (line.startsWith("#") || line.isEmpty()) {
|
if (line.startsWith("#") || line.isEmpty()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
List<String> implementors = serviceMap.get(service);
|
serviceMap.computeIfAbsent(service, k -> new ArrayList<>()).add(line);
|
||||||
if (implementors == null) {
|
|
||||||
implementors = new ArrayList<>();
|
|
||||||
serviceMap.put(service, implementors);
|
|
||||||
}
|
|
||||||
implementors.add(line);
|
|
||||||
allClassesNode.propagate(agent.getType(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) {
|
public void methodReached(final DependencyAgent agent, MethodDependency method, final CallLocation location) {
|
||||||
MethodReference ref = method.getReference();
|
MethodReference ref = method.getReference();
|
||||||
if (ref.getClassName().equals("java.util.ServiceLoader") && ref.getName().equals("loadServices")) {
|
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());
|
allClassesNode.connect(method.getResult().getArrayItem());
|
||||||
method.getResult().getArrayItem().addConsumer(type -> initConstructor(agent, type.getName(), location));
|
method.getResult().getArrayItem().addConsumer(type -> initConstructor(agent, type.getName(), location));
|
||||||
}
|
}
|
||||||
|
|
|
@ -76,6 +76,9 @@ public class ClassGenerator implements Generator, Injector, DependencyPlugin {
|
||||||
case "getInterfaces":
|
case "getInterfaces":
|
||||||
reachGetInterfaces(agent, method);
|
reachGetInterfaces(agent, method);
|
||||||
break;
|
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 {
|
private void generateCreateMetadata(GeneratorContext context, SourceWriter writer) throws IOException {
|
||||||
ReflectionDependencyListener reflection = context.getService(ReflectionDependencyListener.class);
|
ReflectionDependencyListener reflection = context.getService(ReflectionDependencyListener.class);
|
||||||
for (String className : reflection.getClassesWithReflectableFields()) {
|
for (String className : reflection.getClassesWithReflectableFields()) {
|
||||||
|
|
|
@ -179,6 +179,7 @@ public class TClass<T> extends TObject implements TAnnotatedElement {
|
||||||
return (platformClass.getMetadata().getFlags() & Flags.INTERFACE) != 0;
|
return (platformClass.getMetadata().getFlags() & Flags.INTERFACE) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PluggableDependency(ClassGenerator.class)
|
||||||
public TClass<?> getComponentType() {
|
public TClass<?> getComponentType() {
|
||||||
return getClass(Platform.getArrayItem(platformClass));
|
return getClass(Platform.getArrayItem(platformClass));
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,11 +42,16 @@ public class ArrayNativeGenerator implements Generator, DependencyPlugin {
|
||||||
case "getLength":
|
case "getLength":
|
||||||
achieveGetLength(agent, method);
|
achieveGetLength(agent, method);
|
||||||
break;
|
break;
|
||||||
case "newInstanceImpl":
|
case "newInstance":
|
||||||
method.getResult().propagate(agent.getType("[java.lang.Object"));
|
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;
|
break;
|
||||||
case "getImpl":
|
case "getImpl":
|
||||||
achieveGet(agent, method);
|
reachGet(agent, method);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -121,7 +126,7 @@ public class ArrayNativeGenerator implements Generator, DependencyPlugin {
|
||||||
writer.outdent().append("}").softNewLine();
|
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).getArrayItem().connect(method.getResult());
|
||||||
method.getVariable(1).addConsumer(type -> {
|
method.getVariable(1).addConsumer(type -> {
|
||||||
if (type.getName().startsWith("[")) {
|
if (type.getName().startsWith("[")) {
|
||||||
|
|
|
@ -47,6 +47,7 @@ public final class TArray extends TObject {
|
||||||
return array.size;
|
return array.size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PluggableDependency(ArrayNativeGenerator.class)
|
||||||
public static TObject newInstance(TClass<?> componentType, int length) throws TNegativeArraySizeException {
|
public static TObject newInstance(TClass<?> componentType, int length) throws TNegativeArraySizeException {
|
||||||
if (componentType == null) {
|
if (componentType == null) {
|
||||||
throw new TNullPointerException();
|
throw new TNullPointerException();
|
||||||
|
@ -61,7 +62,6 @@ public final class TArray extends TObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
@GeneratedBy(ArrayNativeGenerator.class)
|
@GeneratedBy(ArrayNativeGenerator.class)
|
||||||
@PluggableDependency(ArrayNativeGenerator.class)
|
|
||||||
@DelegateTo("newInstanceLowLevel")
|
@DelegateTo("newInstanceLowLevel")
|
||||||
private static native TObject newInstanceImpl(PlatformClass componentType, int length);
|
private static native TObject newInstanceImpl(PlatformClass componentType, int length);
|
||||||
|
|
||||||
|
|
|
@ -88,7 +88,7 @@ public class DependencyAnalyzer implements DependencyInfo {
|
||||||
Map<MethodReference, BootstrapMethodSubstitutor> bootstrapMethodSubstitutors = new HashMap<>();
|
Map<MethodReference, BootstrapMethodSubstitutor> bootstrapMethodSubstitutors = new HashMap<>();
|
||||||
Map<MethodReference, DependencyPlugin> dependencyPlugins = new HashMap<>();
|
Map<MethodReference, DependencyPlugin> dependencyPlugins = new HashMap<>();
|
||||||
private boolean completing;
|
private boolean completing;
|
||||||
private Map<String, SuperClassFilter> superClassFilters = new HashMap<>();
|
private Map<String, DependencyTypeFilter> superClassFilters = new HashMap<>();
|
||||||
boolean asyncSupported;
|
boolean asyncSupported;
|
||||||
|
|
||||||
public DependencyAnalyzer(ClassReaderSource classSource, ClassLoader classLoader, ServiceRepository services,
|
public DependencyAnalyzer(ClassReaderSource classSource, ClassLoader classLoader, ServiceRepository services,
|
||||||
|
@ -591,7 +591,9 @@ public class DependencyAnalyzer implements DependencyInfo {
|
||||||
if (tasks.isEmpty()) {
|
if (tasks.isEmpty()) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
while (!tasks.isEmpty()) {
|
||||||
tasks.remove().run();
|
tasks.remove().run();
|
||||||
|
}
|
||||||
if (++index == 100) {
|
if (++index == 100) {
|
||||||
if (interruptor != null && !interruptor.shouldContinue()) {
|
if (interruptor != null && !interruptor.shouldContinue()) {
|
||||||
interrupted = true;
|
interrupted = true;
|
||||||
|
@ -729,7 +731,24 @@ public class DependencyAnalyzer implements DependencyInfo {
|
||||||
dependencyPlugins.put(method, dependencyPlugin);
|
dependencyPlugins.put(method, dependencyPlugin);
|
||||||
}
|
}
|
||||||
|
|
||||||
SuperClassFilter getSuperClassFilter(String superClass) {
|
DependencyTypeFilter getSuperClassFilter(String superClass) {
|
||||||
return superClassFilters.computeIfAbsent(superClass, s -> new SuperClassFilter(classSource, s));
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -321,7 +321,7 @@ class DependencyGraphBuilder {
|
||||||
private final Set<MethodReference> knownMethods = new HashSet<>();
|
private final Set<MethodReference> knownMethods = new HashSet<>();
|
||||||
private final BitSet knownTypes = new BitSet();
|
private final BitSet knownTypes = new BitSet();
|
||||||
private ExceptionConsumer exceptionConsumer;
|
private ExceptionConsumer exceptionConsumer;
|
||||||
private SuperClassFilter filter;
|
private DependencyTypeFilter filter;
|
||||||
|
|
||||||
VirtualCallConsumer(DependencyNode node, String filterClass,
|
VirtualCallConsumer(DependencyNode node, String filterClass,
|
||||||
MethodDescriptor methodDesc, DependencyAnalyzer analyzer, DependencyNode[] parameters,
|
MethodDescriptor methodDesc, DependencyAnalyzer analyzer, DependencyNode[] parameters,
|
||||||
|
@ -441,13 +441,27 @@ class DependencyGraphBuilder {
|
||||||
ClassReaderSource classSource = dependencyAnalyzer.getClassSource();
|
ClassReaderSource classSource = dependencyAnalyzer.getClassSource();
|
||||||
if (targetType instanceof ValueType.Object) {
|
if (targetType instanceof ValueType.Object) {
|
||||||
String targetClsName = ((ValueType.Object) targetType).getClassName();
|
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 (targetClass != null && !(targetClass.getName().equals("java.lang.Object"))) {
|
||||||
if (valueNode != null && receiverNode != null) {
|
if (valueNode != null && receiverNode != null) {
|
||||||
valueNode.connect(receiverNode, dependencyAnalyzer.getSuperClassFilter(targetClass.getName()));
|
valueNode.connect(receiverNode, dependencyAnalyzer.getSuperClassFilter(targetClass.getName()));
|
||||||
}
|
}
|
||||||
return;
|
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) {
|
if (valueNode != null && receiverNode != null) {
|
||||||
valueNode.connect(receiverNode);
|
valueNode.connect(receiverNode);
|
||||||
|
|
|
@ -33,7 +33,7 @@ public class DependencyNode implements ValueDependencyInfo {
|
||||||
boolean locked;
|
boolean locked;
|
||||||
MethodReference method;
|
MethodReference method;
|
||||||
private ValueType typeFilter;
|
private ValueType typeFilter;
|
||||||
private SuperClassFilter cachedTypeFilter;
|
private DependencyTypeFilter cachedTypeFilter;
|
||||||
|
|
||||||
DependencyNode(DependencyAnalyzer dependencyAnalyzer, ValueType typeFilter) {
|
DependencyNode(DependencyAnalyzer dependencyAnalyzer, ValueType typeFilter) {
|
||||||
this(dependencyAnalyzer, typeFilter, 0);
|
this(dependencyAnalyzer, typeFilter, 0);
|
||||||
|
|
29
core/src/main/java/org/teavm/dependency/ExactTypeFilter.java
Normal file
29
core/src/main/java/org/teavm/dependency/ExactTypeFilter.java
Normal 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());
|
||||||
|
}
|
||||||
|
}
|
|
@ -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));
|
||||||
|
}
|
||||||
|
}
|
|
@ -43,7 +43,7 @@ public class AnnotationDependencySupport extends AbstractDependencyListener {
|
||||||
public void methodReached(DependencyAgent agent, MethodDependency method, CallLocation location) {
|
public void methodReached(DependencyAgent agent, MethodDependency method, CallLocation location) {
|
||||||
if (method.getReference().getClassName().equals(Platform.class.getName())
|
if (method.getReference().getClassName().equals(Platform.class.getName())
|
||||||
&& method.getReference().getName().equals("getAnnotations")) {
|
&& 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",
|
agent.linkMethod(new MethodReference(PlatformAnnotationProvider.class, "getAnnotations",
|
||||||
Annotation[].class), location);
|
Annotation[].class), location);
|
||||||
allClasses.addConsumer(type -> {
|
allClasses.addConsumer(type -> {
|
||||||
|
|
|
@ -51,6 +51,9 @@ public class PlatformGenerator implements Generator, Injector, DependencyPlugin
|
||||||
case "getCurrentThread":
|
case "getCurrentThread":
|
||||||
method.getResult().propagate(agent.getType("java.lang.Thread"));
|
method.getResult().propagate(agent.getType("java.lang.Thread"));
|
||||||
break;
|
break;
|
||||||
|
case "getEnumConstants":
|
||||||
|
method.getResult().propagate(agent.getType("[Ljava/lang/Enum;"));
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user