From 4f941e0a0a4ff6ca1b3aba4f9e72db863319f385 Mon Sep 17 00:00:00 2001 From: konsoletyper Date: Wed, 3 Sep 2014 21:08:19 +0400 Subject: [PATCH] Refactor dependency checker --- .../impl/ClassLookupDependencySupport.java | 6 +- .../classlib/impl/EnumDependencySupport.java | 4 +- .../impl/NewInstanceDependencySupport.java | 6 +- .../classlib/impl/ServiceLoaderSupport.java | 12 ++-- .../java/lang/CharacterNativeGenerator.java | 2 +- .../java/lang/ClassNativeGenerator.java | 2 +- .../java/lang/ObjectNativeGenerator.java | 2 +- .../lang/reflect/ArrayNativeGenerator.java | 16 ++--- .../java/util/DateNativeGenerator.java | 2 +- .../org/teavm/dependency/DependencyAgent.java | 2 + .../teavm/dependency/DependencyAgentType.java | 26 +++++++ .../teavm/dependency/DependencyChecker.java | 70 +++++++++++-------- .../teavm/dependency/DependencyConsumer.java | 2 +- .../dependency/DependencyGraphBuilder.java | 36 ++++++---- .../org/teavm/dependency/DependencyNode.java | 49 +++++++++---- .../DependencyNodeToNodeTransition.java | 4 +- .../org/teavm/dependency/DependencyType.java | 46 ++++++++++++ .../dependency/DependencyTypeFilter.java | 2 +- .../teavm/dependency/MethodDependency.java | 11 ++- .../ProgrammableDependencyGraphCreator.java | 6 +- .../tooling/TestExceptionDependency.java | 2 +- .../src/main/java/org/teavm/vm/TeaVM.java | 2 +- .../java/org/teavm/vm/TeaVMEntryPoint.java | 2 +- .../html4j/JavaScriptBodyDependency.java | 10 +-- .../java/org/teavm/jso/JSNativeGenerator.java | 4 +- .../teavm/maven/TestExceptionDependency.java | 2 +- .../ResourceAccessorDependencyListener.java | 2 +- 27 files changed, 226 insertions(+), 104 deletions(-) create mode 100644 teavm-core/src/main/java/org/teavm/dependency/DependencyAgentType.java create mode 100644 teavm-core/src/main/java/org/teavm/dependency/DependencyType.java diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/impl/ClassLookupDependencySupport.java b/teavm-classlib/src/main/java/org/teavm/classlib/impl/ClassLookupDependencySupport.java index 80ea34007..f0cceba18 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/impl/ClassLookupDependencySupport.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/impl/ClassLookupDependencySupport.java @@ -32,7 +32,7 @@ public class ClassLookupDependencySupport implements DependencyListener { @Override public void classAchieved(DependencyAgent agent, String className) { - allClasses.propagate(className); + allClasses.propagate(agent.getType(className)); } @Override @@ -41,8 +41,8 @@ public class ClassLookupDependencySupport implements DependencyListener { if (ref.getClassName().equals("java.lang.Class") && ref.getName().equals("forNameImpl")) { final DependencyStack stack = method.getStack(); allClasses.addConsumer(new DependencyConsumer() { - @Override public void consume(String type) { - ClassReader cls = agent.getClassSource().get(type); + @Override public void consume(DependencyAgentType type) { + ClassReader cls = agent.getClassSource().get(type.getName()); if (cls == null) { return; } diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/impl/EnumDependencySupport.java b/teavm-classlib/src/main/java/org/teavm/classlib/impl/EnumDependencySupport.java index 649d37370..69dd90c13 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/impl/EnumDependencySupport.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/impl/EnumDependencySupport.java @@ -40,7 +40,7 @@ public class EnumDependencySupport implements DependencyListener { if (cls == null || cls.getParent() == null || !cls.getParent().equals("java.lang.Enum")) { return; } - allEnums.propagate(className); + allEnums.propagate(agent.getType(className)); if (enumConstantsStack != null) { MethodReader method = cls.getMethod(new MethodDescriptor("values", ValueType.arrayOf(ValueType.object(cls.getName())))); @@ -55,7 +55,7 @@ public class EnumDependencySupport implements DependencyListener { if (method.getReference().getClassName().equals("java.lang.Class") && method.getReference().getName().equals("getEnumConstantsImpl")) { allEnums.connect(method.getResult().getArrayItem()); - method.getResult().propagate("[java.lang.Enum"); + method.getResult().propagate(agent.getType("[java.lang.Enum")); enumConstantsStack = method.getStack(); for (String cls : agent.getAchievableClasses()) { classAchieved(agent, cls); diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/impl/NewInstanceDependencySupport.java b/teavm-classlib/src/main/java/org/teavm/classlib/impl/NewInstanceDependencySupport.java index cbf9016a0..21ead214f 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/impl/NewInstanceDependencySupport.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/impl/NewInstanceDependencySupport.java @@ -42,7 +42,7 @@ public class NewInstanceDependencySupport implements DependencyListener { } MethodReader method = cls.getMethod(new MethodDescriptor("", ValueType.VOID)); if (method != null) { - allClassesNode.propagate(className); + allClassesNode.propagate(agent.getType(className)); } } @@ -53,8 +53,8 @@ public class NewInstanceDependencySupport implements DependencyListener { newInstanceStack = method.getStack(); allClassesNode.connect(method.getResult()); method.getResult().addConsumer(new DependencyConsumer() { - @Override public void consume(String type) { - attachConstructor(agent, type); + @Override public void consume(DependencyAgentType type) { + attachConstructor(agent, type.getName()); } }); } diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/impl/ServiceLoaderSupport.java b/teavm-classlib/src/main/java/org/teavm/classlib/impl/ServiceLoaderSupport.java index 16c06c192..6510d90c6 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/impl/ServiceLoaderSupport.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/impl/ServiceLoaderSupport.java @@ -89,7 +89,7 @@ public class ServiceLoaderSupport implements Generator, DependencyListener { while (resources.hasMoreElements()) { URL resource = resources.nextElement(); try (InputStream stream = resource.openStream()) { - parseServiceFile(className, stream); + parseServiceFile(agent, className, stream); } } } catch (IOException e) { @@ -97,7 +97,7 @@ public class ServiceLoaderSupport implements Generator, DependencyListener { } } - private void parseServiceFile(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")); while (true) { String line = reader.readLine(); @@ -114,7 +114,7 @@ public class ServiceLoaderSupport implements Generator, DependencyListener { serviceMap.put(service, implementors); } implementors.add(line); - allClassesNode.propagate(line); + allClassesNode.propagate(agent.getType(line)); } } @@ -122,12 +122,12 @@ public class ServiceLoaderSupport implements Generator, DependencyListener { public void methodAchieved(final DependencyAgent agent, MethodDependency method) { MethodReference ref = method.getReference(); if (ref.getClassName().equals("java.util.ServiceLoader") && ref.getName().equals("loadServices")) { - method.getResult().propagate("[java.lang.Object"); + method.getResult().propagate(agent.getType("[java.lang.Object")); stack = method.getStack(); allClassesNode.connect(method.getResult().getArrayItem()); method.getResult().getArrayItem().addConsumer(new DependencyConsumer() { - @Override public void consume(String type) { - initConstructor(agent, type); + @Override public void consume(DependencyAgentType type) { + initConstructor(agent, type.getName()); } }); } diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/CharacterNativeGenerator.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/CharacterNativeGenerator.java index 6a4470e07..0aeae2eb8 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/CharacterNativeGenerator.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/CharacterNativeGenerator.java @@ -56,7 +56,7 @@ public class CharacterNativeGenerator implements Generator, DependencyPlugin { switch (method.getReference().getName()) { case "obtainDigitMapping": case "obtainClasses": - method.getResult().propagate("java.lang.String"); + method.getResult().propagate(agent.getType("java.lang.String")); break; } } diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/ClassNativeGenerator.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/ClassNativeGenerator.java index 30b71de3b..86def4e9b 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/ClassNativeGenerator.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/ClassNativeGenerator.java @@ -202,7 +202,7 @@ public class ClassNativeGenerator implements Generator, Injector, DependencyPlug case "getComponentType0": case "forNameImpl": case "getDeclaringClass": - graph.getResult().propagate("java.lang.Class"); + graph.getResult().propagate(agent.getType("java.lang.Class")); break; case "newInstance": agent.linkMethod(new MethodReference(InstantiationException.class.getName(), "", diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/ObjectNativeGenerator.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/ObjectNativeGenerator.java index 64c3c2d1a..5e2124c01 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/ObjectNativeGenerator.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/ObjectNativeGenerator.java @@ -86,7 +86,7 @@ public class ObjectNativeGenerator implements Generator, Injector, DependencyPlu private void achieveGetClass(DependencyAgent agent, MethodDependency method) { MethodReference initMethod = new MethodReference(Class.class, "createNew", Class.class); agent.linkMethod(initMethod, method.getStack()).use(); - method.getResult().propagate("java.lang.Class"); + method.getResult().propagate(agent.getType("java.lang.Class")); } private void generateHashCode(GeneratorContext context, SourceWriter writer) throws IOException { diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/reflect/ArrayNativeGenerator.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/reflect/ArrayNativeGenerator.java index 0f65e69ed..ef3240682 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/reflect/ArrayNativeGenerator.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/reflect/ArrayNativeGenerator.java @@ -44,7 +44,7 @@ public class ArrayNativeGenerator implements Generator, DependencyPlugin { achieveGetLength(agent, method); break; case "newInstanceImpl": - method.getResult().propagate("[java.lang.Object"); + method.getResult().propagate(agent.getType("[java.lang.Object")); break; case "getImpl": achieveGet(agent, method); @@ -80,8 +80,8 @@ public class ArrayNativeGenerator implements Generator, DependencyPlugin { private void achieveGetLength(final DependencyAgent agent, final MethodDependency method) { method.getVariable(1).addConsumer(new DependencyConsumer() { - @Override public void consume(String type) { - if (!type.startsWith("[")) { + @Override public void consume(DependencyAgentType type) { + if (!type.getName().startsWith("[")) { MethodReference cons = new MethodReference(IllegalArgumentException.class, "", void.class); agent.linkMethod(cons, method.getStack()).use(); } @@ -128,16 +128,16 @@ public class ArrayNativeGenerator implements Generator, DependencyPlugin { private void achieveGet(final DependencyAgent agent, final MethodDependency method) { method.getVariable(1).getArrayItem().connect(method.getResult()); method.getVariable(1).addConsumer(new DependencyConsumer() { - @Override public void consume(String type) { - if (type.startsWith("[")) { - type = type.substring(1); + @Override public void consume(DependencyAgentType type) { + if (type.getName().startsWith("[")) { + String typeName = type.getName().substring(1); for (int i = 0; i < primitiveTypes.length; ++i) { - if (primitiveTypes[i].toString().equals(type)) { + if (primitiveTypes[i].toString().equals(typeName)) { String wrapper = "java.lang." + primitiveWrappers[i]; MethodReference methodRef = new MethodReference(wrapper, "valueOf", primitiveTypes[i], ValueType.object(wrapper)); agent.linkMethod(methodRef, method.getStack()).use(); - method.getResult().propagate("java.lang." + primitiveWrappers[i]); + method.getResult().propagate(agent.getType("java.lang." + primitiveWrappers[i])); } } } diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/util/DateNativeGenerator.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/util/DateNativeGenerator.java index 16529f4b7..8ab1bd5e3 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/java/util/DateNativeGenerator.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/util/DateNativeGenerator.java @@ -75,7 +75,7 @@ public class DateNativeGenerator implements Generator, DependencyPlugin { case "toString": case "toLocaleFormat": case "toGMTString": - method.getResult().propagate("java.lang.String"); + method.getResult().propagate(agent.getType("java.lang.String")); break; } } diff --git a/teavm-core/src/main/java/org/teavm/dependency/DependencyAgent.java b/teavm-core/src/main/java/org/teavm/dependency/DependencyAgent.java index c9a7ae198..c0d5f2908 100644 --- a/teavm-core/src/main/java/org/teavm/dependency/DependencyAgent.java +++ b/teavm-core/src/main/java/org/teavm/dependency/DependencyAgent.java @@ -27,6 +27,8 @@ import org.teavm.model.MethodReference; public interface DependencyAgent extends DependencyInfo, ServiceRepository { DependencyNode createNode(); + DependencyAgentType getType(String name); + String generateClassName(); void submitClass(ClassHolder cls); diff --git a/teavm-core/src/main/java/org/teavm/dependency/DependencyAgentType.java b/teavm-core/src/main/java/org/teavm/dependency/DependencyAgentType.java new file mode 100644 index 000000000..95de7bbde --- /dev/null +++ b/teavm-core/src/main/java/org/teavm/dependency/DependencyAgentType.java @@ -0,0 +1,26 @@ +/* + * Copyright 2014 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; + +/** + * + * @author Alexey Andreev + */ +public interface DependencyAgentType { + String getName(); + + DependencyAgent getDependencyAgent(); +} diff --git a/teavm-core/src/main/java/org/teavm/dependency/DependencyChecker.java b/teavm-core/src/main/java/org/teavm/dependency/DependencyChecker.java index 7f3c52976..3663ff18e 100644 --- a/teavm-core/src/main/java/org/teavm/dependency/DependencyChecker.java +++ b/teavm-core/src/main/java/org/teavm/dependency/DependencyChecker.java @@ -17,8 +17,6 @@ package org.teavm.dependency; import java.io.IOException; import java.util.*; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; import org.teavm.common.*; import org.teavm.common.ConcurrentCachedMapper.KeyListener; import org.teavm.model.*; @@ -29,7 +27,6 @@ import org.teavm.model.util.ModelUtils; * @author Alexey Andreev */ public class DependencyChecker implements DependencyInfo, DependencyAgent { - private static Object dummyValue = new Object(); static final boolean shouldLog = System.getProperty("org.teavm.logDependencies", "false").equals("true"); private int classNameSuffix; private DependencyClassSource classSource; @@ -37,18 +34,20 @@ public class DependencyChecker implements DependencyInfo, DependencyAgent { private FiniteExecutor executor; private Mapper methodReaderCache; private Mapper fieldReaderCache; - private ConcurrentMap stacks = new ConcurrentHashMap<>(); - private ConcurrentMap fieldStacks = new ConcurrentHashMap<>(); - private ConcurrentMap classStacks = new ConcurrentHashMap<>(); + private Map stacks = new HashMap<>(); + private Map fieldStacks = new HashMap<>(); + private Map classStacks = new HashMap<>(); private ConcurrentCachedMapper methodCache; private ConcurrentCachedMapper fieldCache; - private ConcurrentMap achievableClasses = new ConcurrentHashMap<>(); - private ConcurrentMap initializedClasses = new ConcurrentHashMap<>(); + private Set achievableClasses = new HashSet<>(); + private Set initializedClasses = new HashSet<>(); private List listeners = new ArrayList<>(); private ServiceRepository services; - ConcurrentMap missingMethods = new ConcurrentHashMap<>(); - ConcurrentMap missingClasses = new ConcurrentHashMap<>(); - ConcurrentMap missingFields = new ConcurrentHashMap<>(); + Map missingMethods = new HashMap<>(); + Map missingClasses = new HashMap<>(); + Map missingFields = new HashMap<>(); + List types = new ArrayList<>(); + Map typeMap = new HashMap<>(); public DependencyChecker(ClassReaderSource classSource, ClassLoader classLoader, ServiceRepository services) { this(classSource, classLoader, services, new SimpleFiniteExecutor()); @@ -113,6 +112,17 @@ public class DependencyChecker implements DependencyInfo, DependencyAgent { }); } + @Override + public DependencyType getType(String name) { + DependencyType type = typeMap.get(name); + if (type == null) { + type = new DependencyType(this, name, types.size()); + types.add(type); + typeMap.put(name, type); + } + return type; + } + @Override public DependencyNode createNode() { return new DependencyNode(this); @@ -155,13 +165,13 @@ public class DependencyChecker implements DependencyInfo, DependencyAgent { MethodDependency method = linkMethod(methodRef, DependencyStack.ROOT); method.use(); DependencyNode[] varNodes = method.getVariables(); - varNodes[0].propagate(methodRef.getClassName()); + varNodes[0].propagate(getType(methodRef.getClassName())); for (int i = 0; i < argumentTypes.length; ++i) { - varNodes[i + 1].propagate(argumentTypes[i]); + varNodes[i + 1].propagate(getType(argumentTypes[i])); } } - void schedulePropagation(final DependencyConsumer consumer, final String type) { + void schedulePropagation(final DependencyConsumer consumer, final DependencyType type) { executor.executeFast(new Runnable() { @Override public void run() { consumer.consume(type); @@ -174,14 +184,14 @@ public class DependencyChecker implements DependencyInfo, DependencyAgent { } boolean achieveClass(String className, DependencyStack stack) { - classStacks.putIfAbsent(className, stack); - boolean result = achievableClasses.putIfAbsent(className, dummyValue) == null; - if (result) { - for (DependencyListener listener : listeners) { - listener.classAchieved(this, className); - } + classStacks.put(className, stack); + if (!achievableClasses.add(className)) { + return false; } - return result; + for (DependencyListener listener : listeners) { + listener.classAchieved(this, className); + } + return true; } @Override @@ -189,16 +199,16 @@ public class DependencyChecker implements DependencyInfo, DependencyAgent { if (methodRef == null) { throw new IllegalArgumentException(); } - stacks.putIfAbsent(methodRef, stack); + stacks.put(methodRef, stack); return methodCache.map(methodRef); } @Override public void initClass(String className, final DependencyStack stack) { - classStacks.putIfAbsent(className, stack); + classStacks.put(className, stack); MethodDescriptor clinitDesc = new MethodDescriptor("", ValueType.VOID); while (className != null) { - if (initializedClasses.putIfAbsent(className, clinitDesc) != null) { + if (!initializedClasses.add(className)) { break; } achieveClass(className, stack); @@ -221,7 +231,7 @@ public class DependencyChecker implements DependencyInfo, DependencyAgent { } private void achieveInterfaces(String className, DependencyStack stack) { - classStacks.putIfAbsent(className, stack); + classStacks.put(className, stack); ClassReader cls = classSource.get(className); if (cls == null) { missingClasses.put(className, stack); @@ -306,7 +316,7 @@ public class DependencyChecker implements DependencyInfo, DependencyAgent { thrown.setTag(methodRef + ":THROWN"); } stack = new DependencyStack(methodRef, stack); - final MethodDependency dep = new MethodDependency(parameterNodes, paramCount, resultNode, thrown, + final MethodDependency dep = new MethodDependency(this, parameterNodes, paramCount, resultNode, thrown, stack, method, methodRef); if (method != null) { executor.execute(new Runnable() { @@ -316,7 +326,7 @@ public class DependencyChecker implements DependencyInfo, DependencyAgent { } }); } else { - missingMethods.putIfAbsent(methodRef, stack); + missingMethods.put(methodRef, stack); } if (method != null) { final DependencyStack callerStack = stack; @@ -347,12 +357,12 @@ public class DependencyChecker implements DependencyInfo, DependencyAgent { @Override public Collection getAchievableClasses() { - return new HashSet<>(achievableClasses.keySet()); + return new HashSet<>(achievableClasses); } @Override public FieldDependency linkField(FieldReference fieldRef, DependencyStack stack) { - fieldStacks.putIfAbsent(fieldRef, stack); + fieldStacks.put(fieldRef, stack); return fieldCache.map(fieldRef); } @@ -364,7 +374,7 @@ public class DependencyChecker implements DependencyInfo, DependencyAgent { private FieldDependency createFieldNode(FieldReference fieldRef, FieldReader field, DependencyStack stack) { DependencyNode node = new DependencyNode(this); if (field == null) { - missingFields.putIfAbsent(fieldRef, stack); + missingFields.put(fieldRef, stack); } if (shouldLog) { node.setTag(fieldRef.getClassName() + "#" + fieldRef.getFieldName()); diff --git a/teavm-core/src/main/java/org/teavm/dependency/DependencyConsumer.java b/teavm-core/src/main/java/org/teavm/dependency/DependencyConsumer.java index aaaca5ab6..60821beb6 100644 --- a/teavm-core/src/main/java/org/teavm/dependency/DependencyConsumer.java +++ b/teavm-core/src/main/java/org/teavm/dependency/DependencyConsumer.java @@ -20,5 +20,5 @@ package org.teavm.dependency; * @author Alexey Andreev */ public interface DependencyConsumer { - void consume(String type); + void consume(DependencyAgentType type); } diff --git a/teavm-core/src/main/java/org/teavm/dependency/DependencyGraphBuilder.java b/teavm-core/src/main/java/org/teavm/dependency/DependencyGraphBuilder.java index 32d997738..8c1d61661 100644 --- a/teavm-core/src/main/java/org/teavm/dependency/DependencyGraphBuilder.java +++ b/teavm-core/src/main/java/org/teavm/dependency/DependencyGraphBuilder.java @@ -104,9 +104,10 @@ class DependencyGraphBuilder { } @Override - public void consume(String type) { + public void consume(DependencyAgentType type) { for (int i = 0; i < exceptions.length; ++i) { - if (exceptions[i] == null || isAssignableFrom(checker.getClassSource(), exceptions[i], type)) { + if (exceptions[i] == null || isAssignableFrom(checker.getClassSource(), exceptions[i], + type.getName())) { vars[i].propagate(type); return; } @@ -140,7 +141,8 @@ class DependencyGraphBuilder { } @Override - public void consume(String className) { + public void consume(DependencyAgentType type) { + String className = type.getName(); if (DependencyChecker.shouldLog) { System.out.println("Virtual call of " + methodDesc + " detected on " + node.getTag() + ". " + "Target class is " + className); @@ -201,8 +203,8 @@ class DependencyGraphBuilder { private static class TypePropagationRunner implements Runnable { private DependencyNode node; - private String type; - public TypePropagationRunner(DependencyNode node, String type) { + private DependencyType type; + public TypePropagationRunner(DependencyNode node, DependencyType type) { this.node = node; this.type = type; } @@ -222,7 +224,8 @@ class DependencyGraphBuilder { @Override public void classConstant(VariableReader receiver, ValueType cst) { - useRunners.add(new TypePropagationRunner(nodes[receiver.getIndex()], "java.lang.Class")); + useRunners.add(new TypePropagationRunner(nodes[receiver.getIndex()], + dependencyChecker.getType("java.lang.Class"))); while (cst instanceof ValueType.Array) { cst = ((ValueType.Array)cst).getItemType(); } @@ -258,9 +261,10 @@ class DependencyGraphBuilder { @Override public void stringConstant(VariableReader receiver, String cst) { - useRunners.add(new TypePropagationRunner(nodes[receiver.getIndex()], "java.lang.String")); + useRunners.add(new TypePropagationRunner(nodes[receiver.getIndex()], + dependencyChecker.getType("java.lang.String"))); MethodDependency method = dependencyChecker.linkMethod(new MethodReference(String.class, - "", char.class, void.class), callerStack); + "", char[].class, void.class), callerStack); method.use(); } @@ -289,11 +293,11 @@ class DependencyGraphBuilder { final ClassReader targetClass = dependencyChecker.getClassSource().get(targetClsName); if (targetClass != null) { valueNode.connect(receiverNode, new DependencyTypeFilter() { - @Override public boolean match(String type) { + @Override public boolean match(DependencyAgentType type) { if (targetClass.getName().equals("java.lang.Object")) { return true; } - return isAssignableFrom(dependencyChecker.getClassSource(), targetClass, type); + return isAssignableFrom(dependencyChecker.getClassSource(), targetClass, type.getName()); } }); return; @@ -345,7 +349,8 @@ class DependencyGraphBuilder { @Override public void createArray(VariableReader receiver, ValueType itemType, VariableReader size) { - useRunners.add(new TypePropagationRunner(nodes[receiver.getIndex()], "[" + itemType)); + useRunners.add(new TypePropagationRunner(nodes[receiver.getIndex()], + dependencyChecker.getType("[" + itemType))); final String className = extractClassName(itemType); if (className != null) { useRunners.add(new Runnable() { @@ -371,12 +376,13 @@ class DependencyGraphBuilder { sb.append('['); } sb.append(itemType); - useRunners.add(new TypePropagationRunner(nodes[receiver.getIndex()], sb.toString())); + useRunners.add(new TypePropagationRunner(nodes[receiver.getIndex()], + dependencyChecker.getType(sb.toString()))); } @Override public void create(VariableReader receiver, String type) { - useRunners.add(new TypePropagationRunner(nodes[receiver.getIndex()], type)); + useRunners.add(new TypePropagationRunner(nodes[receiver.getIndex()], dependencyChecker.getType(type))); } @Override @@ -405,7 +411,7 @@ class DependencyGraphBuilder { DependencyNode arrayNode = nodes[array.getIndex()]; final DependencyNode receiverNode = nodes[receiver.getIndex()]; arrayNode.addConsumer(new DependencyConsumer() { - @Override public void consume(String type) { + @Override public void consume(DependencyAgentType type) { receiverNode.propagate(type); } }); @@ -522,7 +528,7 @@ class DependencyGraphBuilder { "", ValueType.VOID), callerStack); } }); - currentExceptionConsumer.consume("java.lang.NullPointerException"); + currentExceptionConsumer.consume(dependencyChecker.getType("java.lang.NullPointerException")); } }; } diff --git a/teavm-core/src/main/java/org/teavm/dependency/DependencyNode.java b/teavm-core/src/main/java/org/teavm/dependency/DependencyNode.java index 2317647e0..d1daa9299 100644 --- a/teavm-core/src/main/java/org/teavm/dependency/DependencyNode.java +++ b/teavm-core/src/main/java/org/teavm/dependency/DependencyNode.java @@ -15,10 +15,12 @@ */ package org.teavm.dependency; +import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicReference; +import org.teavm.common.IntegerArray; /** * @@ -26,9 +28,8 @@ import java.util.concurrent.atomic.AtomicReference; */ public class DependencyNode implements ValueDependencyInfo { private DependencyChecker dependencyChecker; - private static final Object mapValue = new Object(); - private ConcurrentMap followers = new ConcurrentHashMap<>(); - private ConcurrentMap types = new ConcurrentHashMap<>(); + private Set followers = new HashSet<>(); + private BitSet types = new BitSet(); private ConcurrentMap transitions = new ConcurrentHashMap<>(); private volatile String tag; private final AtomicReference arrayItemNode = new AtomicReference<>(); @@ -44,24 +45,36 @@ public class DependencyNode implements ValueDependencyInfo { this.degree = degree; } - public void propagate(String type) { + public void propagate(DependencyAgentType agentType) { + if (!(agentType instanceof DependencyType)) { + throw new IllegalArgumentException("The given type does not belong to the same dependency checker"); + } + DependencyType type = (DependencyType)agentType; + if (type.getDependencyChecker() != dependencyChecker) { + throw new IllegalArgumentException("The given type does not belong to the same dependency checker"); + } if (degree > 2) { return; } - if (types.putIfAbsent(type, mapValue) == null) { + if (!types.get(type.index)) { + types.set(type.index); if (DependencyChecker.shouldLog) { - System.out.println(tag + " -> " + type); + System.out.println(tag + " -> " + type.getName()); } - for (DependencyConsumer consumer : followers.keySet().toArray(new DependencyConsumer[0])) { + for (DependencyConsumer consumer : followers.toArray(new DependencyConsumer[followers.size()])) { dependencyChecker.schedulePropagation(consumer, type); } } } public void addConsumer(DependencyConsumer consumer) { - if (followers.putIfAbsent(consumer, mapValue) == null) { - for (String type : types.keySet().toArray(new String[0])) { - dependencyChecker.schedulePropagation(consumer, type); + if (followers.add(consumer)) { + IntegerArray indexes = new IntegerArray(8); + for (int index = types.nextSetBit(0); index >= 0; index = types.nextSetBit(index + 1)) { + indexes.add(index); + } + for (int index : indexes.getAll()) { + dependencyChecker.schedulePropagation(consumer, dependencyChecker.types.get(index)); } } } @@ -112,14 +125,26 @@ public class DependencyNode implements ValueDependencyInfo { return arrayItemNode.get() != null && !arrayItemNode.get().types.isEmpty(); } + public boolean hasType(DependencyAgentType type) { + if (!(type instanceof DependencyType)) { + return false; + } + DependencyType typeImpl = (DependencyType)type; + return typeImpl.getDependencyChecker() == dependencyChecker && types.get(typeImpl.index); + } + @Override public boolean hasType(String type) { - return types.containsKey(type); + return hasType(dependencyChecker.getType(type)); } @Override public String[] getTypes() { - return types != null ? types.keySet().toArray(new String[types.size()]) : new String[0]; + List result = new ArrayList<>(); + for (int index = types.nextSetBit(0); index >= 0; index = types.nextSetBit(index + 1)) { + result.add(dependencyChecker.types.get(index).getName()); + } + return result.toArray(new String[result.size()]); } public String getTag() { diff --git a/teavm-core/src/main/java/org/teavm/dependency/DependencyNodeToNodeTransition.java b/teavm-core/src/main/java/org/teavm/dependency/DependencyNodeToNodeTransition.java index c19624e17..4eab6eb1e 100644 --- a/teavm-core/src/main/java/org/teavm/dependency/DependencyNodeToNodeTransition.java +++ b/teavm-core/src/main/java/org/teavm/dependency/DependencyNodeToNodeTransition.java @@ -32,11 +32,11 @@ class DependencyNodeToNodeTransition implements DependencyConsumer { } @Override - public void consume(String type) { + public void consume(DependencyAgentType type) { if (filter != null && !filter.match(type)) { return; } - if (type.startsWith("[")) { + if (type.getName().startsWith("[")) { source.getArrayItem().connect(destination.getArrayItem()); destination.getArrayItem().connect(source.getArrayItem()); } diff --git a/teavm-core/src/main/java/org/teavm/dependency/DependencyType.java b/teavm-core/src/main/java/org/teavm/dependency/DependencyType.java new file mode 100644 index 000000000..f74d7bb02 --- /dev/null +++ b/teavm-core/src/main/java/org/teavm/dependency/DependencyType.java @@ -0,0 +1,46 @@ +/* + * Copyright 2014 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; + +/** + * + * @author Alexey Andreev + */ +public class DependencyType implements DependencyAgentType { + private DependencyChecker dependencyChecker; + private String name; + int index; + + public DependencyType(DependencyChecker dependencyChecker, String name, int index) { + this.dependencyChecker = dependencyChecker; + this.name = name; + this.index = index; + } + + public DependencyChecker getDependencyChecker() { + return dependencyChecker; + } + + @Override + public String getName() { + return name; + } + + @Override + public DependencyAgent getDependencyAgent() { + return dependencyChecker; + } +} diff --git a/teavm-core/src/main/java/org/teavm/dependency/DependencyTypeFilter.java b/teavm-core/src/main/java/org/teavm/dependency/DependencyTypeFilter.java index 96ff12e64..dc1d01049 100644 --- a/teavm-core/src/main/java/org/teavm/dependency/DependencyTypeFilter.java +++ b/teavm-core/src/main/java/org/teavm/dependency/DependencyTypeFilter.java @@ -20,5 +20,5 @@ package org.teavm.dependency; * @author Alexey Andreev */ public interface DependencyTypeFilter { - boolean match(String type); + boolean match(DependencyAgentType type); } diff --git a/teavm-core/src/main/java/org/teavm/dependency/MethodDependency.java b/teavm-core/src/main/java/org/teavm/dependency/MethodDependency.java index c8b0a9ff8..af1b684a5 100644 --- a/teavm-core/src/main/java/org/teavm/dependency/MethodDependency.java +++ b/teavm-core/src/main/java/org/teavm/dependency/MethodDependency.java @@ -25,6 +25,7 @@ import org.teavm.model.MethodReference; * @author Alexey Andreev */ public class MethodDependency implements MethodDependencyInfo { + private DependencyChecker dependencyChecker; private DependencyNode[] variableNodes; private int parameterCount; private DependencyNode resultNode; @@ -35,8 +36,10 @@ public class MethodDependency implements MethodDependencyInfo { private AtomicBoolean used = new AtomicBoolean(); private volatile Runnable useRunner; - MethodDependency(DependencyNode[] variableNodes, int parameterCount, DependencyNode resultNode, - DependencyNode thrown, DependencyStack stack, MethodReader method, MethodReference reference) { + MethodDependency(DependencyChecker dependencyChecker, DependencyNode[] variableNodes, int parameterCount, + DependencyNode resultNode, DependencyNode thrown, DependencyStack stack, MethodReader method, + MethodReference reference) { + this.dependencyChecker = dependencyChecker; this.variableNodes = Arrays.copyOf(variableNodes, variableNodes.length); this.parameterCount = parameterCount; this.thrown = thrown; @@ -46,6 +49,10 @@ public class MethodDependency implements MethodDependencyInfo { this.reference = reference; } + public DependencyAgent getDependencyAgent() { + return dependencyChecker; + } + @Override public DependencyNode[] getVariables() { return Arrays.copyOf(variableNodes, variableNodes.length); diff --git a/teavm-core/src/main/java/org/teavm/dependency/ProgrammableDependencyGraphCreator.java b/teavm-core/src/main/java/org/teavm/dependency/ProgrammableDependencyGraphCreator.java index 11153f564..ff7049e97 100644 --- a/teavm-core/src/main/java/org/teavm/dependency/ProgrammableDependencyGraphCreator.java +++ b/teavm-core/src/main/java/org/teavm/dependency/ProgrammableDependencyGraphCreator.java @@ -48,7 +48,7 @@ class ProgrammableDependencyGraphCreator implements DependencyGraphCreator { checker.getClassSource().get(conn.superclass))); } for (TypePropagation propagation : typePropagations) { - nodes[propagation.var].propagate(propagation.type); + nodes[propagation.var].propagate(checker.getType(propagation.type)); } for (String className : initializedClasses) { checker.initClass(className, stack); @@ -79,11 +79,11 @@ class ProgrammableDependencyGraphCreator implements DependencyGraphCreator { this.classSource = classSource; this.superClass = superClass; } - @Override public boolean match(String type) { + @Override public boolean match(DependencyAgentType type) { if (superClass.getName().equals("java.lang.Object")) { return true; } - return isAssignableFrom(classSource, superClass, type); + return isAssignableFrom(classSource, superClass, type.getName()); } } diff --git a/teavm-core/src/main/java/org/teavm/tooling/TestExceptionDependency.java b/teavm-core/src/main/java/org/teavm/tooling/TestExceptionDependency.java index a96fa2d13..3a26b9335 100644 --- a/teavm-core/src/main/java/org/teavm/tooling/TestExceptionDependency.java +++ b/teavm-core/src/main/java/org/teavm/tooling/TestExceptionDependency.java @@ -37,7 +37,7 @@ class TestExceptionDependency implements DependencyListener { @Override public void classAchieved(DependencyAgent agent, String className) { if (isException(agent.getClassSource(), className)) { - allClasses.propagate(className); + allClasses.propagate(agent.getType(className)); } } diff --git a/teavm-core/src/main/java/org/teavm/vm/TeaVM.java b/teavm-core/src/main/java/org/teavm/vm/TeaVM.java index 726447bda..22e7388b6 100644 --- a/teavm-core/src/main/java/org/teavm/vm/TeaVM.java +++ b/teavm-core/src/main/java/org/teavm/vm/TeaVM.java @@ -301,7 +301,7 @@ public class TeaVM implements TeaVMHost, ServiceRepository { ValueType.VOID), DependencyStack.ROOT).use(); MethodDependency internDep = dependencyChecker.linkMethod(new MethodReference("java.lang.String", "intern", ValueType.object("java.lang.String")), DependencyStack.ROOT); - internDep.getVariable(0).propagate("java.lang.String"); + internDep.getVariable(0).propagate(dependencyChecker.getType("java.lang.String")); internDep.use(); dependencyChecker.linkMethod(new MethodReference("java.lang.String", "length", ValueType.INTEGER), DependencyStack.ROOT).use(); diff --git a/teavm-core/src/main/java/org/teavm/vm/TeaVMEntryPoint.java b/teavm-core/src/main/java/org/teavm/vm/TeaVMEntryPoint.java index b75ec9728..ca6b948d1 100644 --- a/teavm-core/src/main/java/org/teavm/vm/TeaVMEntryPoint.java +++ b/teavm-core/src/main/java/org/teavm/vm/TeaVMEntryPoint.java @@ -88,7 +88,7 @@ public class TeaVMEntryPoint { if (argument > reference.parameterCount()) { throw new IllegalArgumentException("Illegal argument #" + argument + " of " + reference.parameterCount()); } - method.getVariable(argument).propagate(type); + method.getVariable(argument).propagate(method.getDependencyAgent().getType(type)); return this; } } diff --git a/teavm-html4j/src/main/java/org/teavm/html4j/JavaScriptBodyDependency.java b/teavm-html4j/src/main/java/org/teavm/html4j/JavaScriptBodyDependency.java index a64d8b4d7..a4939ea46 100644 --- a/teavm-html4j/src/main/java/org/teavm/html4j/JavaScriptBodyDependency.java +++ b/teavm-html4j/src/main/java/org/teavm/html4j/JavaScriptBodyDependency.java @@ -37,7 +37,7 @@ public class JavaScriptBodyDependency implements DependencyListener { public OneDirectionalConnection( DependencyNode target) { this.target = target; } - @Override public void consume(String type) { + @Override public void consume(DependencyAgentType type) { target.propagate(type); } } @@ -47,7 +47,7 @@ public class JavaScriptBodyDependency implements DependencyListener { ClassReader cls = agent.getClassSource().get(className); if (cls != null && !cls.hasModifier(ElementModifier.ABSTRACT) && !cls.hasModifier(ElementModifier.INTERFACE)) { - allClassesNode.propagate(className); + allClassesNode.propagate(agent.getType(className)); } } @@ -161,11 +161,11 @@ public class JavaScriptBodyDependency implements DependencyListener { this.caller = caller; this.superClass = agent.getClassSource().get(superMethod.getOwnerName()); } - @Override public void consume(String type) { - if (!isAssignableFrom(superClass, type)) { + @Override public void consume(DependencyAgentType type) { + if (!isAssignableFrom(superClass, type.getName())) { return; } - MethodReference methodRef = new MethodReference(type, superMethod.getDescriptor()); + MethodReference methodRef = new MethodReference(type.getName(), superMethod.getDescriptor()); MethodDependency method = agent.linkMethod(methodRef, caller.getStack()); method.use(); for (int i = 0; i < method.getParameterCount(); ++i) { diff --git a/teavm-jso/src/main/java/org/teavm/jso/JSNativeGenerator.java b/teavm-jso/src/main/java/org/teavm/jso/JSNativeGenerator.java index 74157561c..dc2a77392 100644 --- a/teavm-jso/src/main/java/org/teavm/jso/JSNativeGenerator.java +++ b/teavm-jso/src/main/java/org/teavm/jso/JSNativeGenerator.java @@ -118,8 +118,8 @@ public class JSNativeGenerator implements Generator, Injector, DependencyPlugin public void methodAchieved(final DependencyAgent agent, final MethodDependency method) { for (int i = 0; i < method.getReference().parameterCount(); ++i) { method.getVariable(i).addConsumer(new DependencyConsumer() { - @Override public void consume(String type) { - achieveFunctorMethods(agent, type, method); + @Override public void consume(DependencyAgentType type) { + achieveFunctorMethods(agent, type.getName(), method); } }); } diff --git a/teavm-maven-plugin/src/main/java/org/teavm/maven/TestExceptionDependency.java b/teavm-maven-plugin/src/main/java/org/teavm/maven/TestExceptionDependency.java index 7fad5ec08..5276d63e4 100644 --- a/teavm-maven-plugin/src/main/java/org/teavm/maven/TestExceptionDependency.java +++ b/teavm-maven-plugin/src/main/java/org/teavm/maven/TestExceptionDependency.java @@ -38,7 +38,7 @@ class TestExceptionDependency implements DependencyListener { @Override public void classAchieved(DependencyAgent agent, String className) { if (isException(agent.getClassSource(), className)) { - allClasses.propagate(className); + allClasses.propagate(agent.getType(className)); } } diff --git a/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceAccessorDependencyListener.java b/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceAccessorDependencyListener.java index 72c572674..627ab413e 100644 --- a/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceAccessorDependencyListener.java +++ b/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceAccessorDependencyListener.java @@ -37,7 +37,7 @@ class ResourceAccessorDependencyListener implements DependencyListener { public void methodAchieved(DependencyAgent agent, MethodDependency method) { switch (method.getReference().getName()) { case "castToString": - method.getResult().propagate("java.lang.String"); + method.getResult().propagate(agent.getType("java.lang.String")); break; } }