Refactor dependency checker

This commit is contained in:
konsoletyper 2014-09-03 21:08:19 +04:00
parent e60be5d518
commit 4f941e0a0a
27 changed files with 226 additions and 104 deletions

View File

@ -32,7 +32,7 @@ public class ClassLookupDependencySupport implements DependencyListener {
@Override @Override
public void classAchieved(DependencyAgent agent, String className) { public void classAchieved(DependencyAgent agent, String className) {
allClasses.propagate(className); allClasses.propagate(agent.getType(className));
} }
@Override @Override
@ -41,8 +41,8 @@ public class ClassLookupDependencySupport implements DependencyListener {
if (ref.getClassName().equals("java.lang.Class") && ref.getName().equals("forNameImpl")) { if (ref.getClassName().equals("java.lang.Class") && ref.getName().equals("forNameImpl")) {
final DependencyStack stack = method.getStack(); final DependencyStack stack = method.getStack();
allClasses.addConsumer(new DependencyConsumer() { allClasses.addConsumer(new DependencyConsumer() {
@Override public void consume(String type) { @Override public void consume(DependencyAgentType type) {
ClassReader cls = agent.getClassSource().get(type); ClassReader cls = agent.getClassSource().get(type.getName());
if (cls == null) { if (cls == null) {
return; return;
} }

View File

@ -40,7 +40,7 @@ public class EnumDependencySupport implements DependencyListener {
if (cls == null || cls.getParent() == null || !cls.getParent().equals("java.lang.Enum")) { if (cls == null || cls.getParent() == null || !cls.getParent().equals("java.lang.Enum")) {
return; return;
} }
allEnums.propagate(className); allEnums.propagate(agent.getType(className));
if (enumConstantsStack != null) { if (enumConstantsStack != null) {
MethodReader method = cls.getMethod(new MethodDescriptor("values", MethodReader method = cls.getMethod(new MethodDescriptor("values",
ValueType.arrayOf(ValueType.object(cls.getName())))); ValueType.arrayOf(ValueType.object(cls.getName()))));
@ -55,7 +55,7 @@ public class EnumDependencySupport implements DependencyListener {
if (method.getReference().getClassName().equals("java.lang.Class") && if (method.getReference().getClassName().equals("java.lang.Class") &&
method.getReference().getName().equals("getEnumConstantsImpl")) { method.getReference().getName().equals("getEnumConstantsImpl")) {
allEnums.connect(method.getResult().getArrayItem()); allEnums.connect(method.getResult().getArrayItem());
method.getResult().propagate("[java.lang.Enum"); method.getResult().propagate(agent.getType("[java.lang.Enum"));
enumConstantsStack = method.getStack(); enumConstantsStack = method.getStack();
for (String cls : agent.getAchievableClasses()) { for (String cls : agent.getAchievableClasses()) {
classAchieved(agent, cls); classAchieved(agent, cls);

View File

@ -42,7 +42,7 @@ public class NewInstanceDependencySupport implements DependencyListener {
} }
MethodReader method = cls.getMethod(new MethodDescriptor("<init>", ValueType.VOID)); MethodReader method = cls.getMethod(new MethodDescriptor("<init>", ValueType.VOID));
if (method != null) { if (method != null) {
allClassesNode.propagate(className); allClassesNode.propagate(agent.getType(className));
} }
} }
@ -53,8 +53,8 @@ public class NewInstanceDependencySupport implements DependencyListener {
newInstanceStack = method.getStack(); newInstanceStack = method.getStack();
allClassesNode.connect(method.getResult()); allClassesNode.connect(method.getResult());
method.getResult().addConsumer(new DependencyConsumer() { method.getResult().addConsumer(new DependencyConsumer() {
@Override public void consume(String type) { @Override public void consume(DependencyAgentType type) {
attachConstructor(agent, type); attachConstructor(agent, type.getName());
} }
}); });
} }

View File

@ -89,7 +89,7 @@ public class ServiceLoaderSupport implements Generator, DependencyListener {
while (resources.hasMoreElements()) { while (resources.hasMoreElements()) {
URL resource = resources.nextElement(); URL resource = resources.nextElement();
try (InputStream stream = resource.openStream()) { try (InputStream stream = resource.openStream()) {
parseServiceFile(className, stream); parseServiceFile(agent, className, stream);
} }
} }
} catch (IOException e) { } 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")); BufferedReader reader = new BufferedReader(new InputStreamReader(input, "UTF-8"));
while (true) { while (true) {
String line = reader.readLine(); String line = reader.readLine();
@ -114,7 +114,7 @@ public class ServiceLoaderSupport implements Generator, DependencyListener {
serviceMap.put(service, implementors); serviceMap.put(service, implementors);
} }
implementors.add(line); 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) { public void methodAchieved(final DependencyAgent agent, MethodDependency method) {
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("[java.lang.Object"); method.getResult().propagate(agent.getType("[java.lang.Object"));
stack = method.getStack(); stack = method.getStack();
allClassesNode.connect(method.getResult().getArrayItem()); allClassesNode.connect(method.getResult().getArrayItem());
method.getResult().getArrayItem().addConsumer(new DependencyConsumer() { method.getResult().getArrayItem().addConsumer(new DependencyConsumer() {
@Override public void consume(String type) { @Override public void consume(DependencyAgentType type) {
initConstructor(agent, type); initConstructor(agent, type.getName());
} }
}); });
} }

View File

@ -56,7 +56,7 @@ public class CharacterNativeGenerator implements Generator, DependencyPlugin {
switch (method.getReference().getName()) { switch (method.getReference().getName()) {
case "obtainDigitMapping": case "obtainDigitMapping":
case "obtainClasses": case "obtainClasses":
method.getResult().propagate("java.lang.String"); method.getResult().propagate(agent.getType("java.lang.String"));
break; break;
} }
} }

View File

@ -202,7 +202,7 @@ public class ClassNativeGenerator implements Generator, Injector, DependencyPlug
case "getComponentType0": case "getComponentType0":
case "forNameImpl": case "forNameImpl":
case "getDeclaringClass": case "getDeclaringClass":
graph.getResult().propagate("java.lang.Class"); graph.getResult().propagate(agent.getType("java.lang.Class"));
break; break;
case "newInstance": case "newInstance":
agent.linkMethod(new MethodReference(InstantiationException.class.getName(), "<init>", agent.linkMethod(new MethodReference(InstantiationException.class.getName(), "<init>",

View File

@ -86,7 +86,7 @@ public class ObjectNativeGenerator implements Generator, Injector, DependencyPlu
private void achieveGetClass(DependencyAgent agent, MethodDependency method) { private void achieveGetClass(DependencyAgent agent, MethodDependency method) {
MethodReference initMethod = new MethodReference(Class.class, "createNew", Class.class); MethodReference initMethod = new MethodReference(Class.class, "createNew", Class.class);
agent.linkMethod(initMethod, method.getStack()).use(); 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 { private void generateHashCode(GeneratorContext context, SourceWriter writer) throws IOException {

View File

@ -44,7 +44,7 @@ public class ArrayNativeGenerator implements Generator, DependencyPlugin {
achieveGetLength(agent, method); achieveGetLength(agent, method);
break; break;
case "newInstanceImpl": case "newInstanceImpl":
method.getResult().propagate("[java.lang.Object"); method.getResult().propagate(agent.getType("[java.lang.Object"));
break; break;
case "getImpl": case "getImpl":
achieveGet(agent, method); achieveGet(agent, method);
@ -80,8 +80,8 @@ public class ArrayNativeGenerator implements Generator, DependencyPlugin {
private void achieveGetLength(final DependencyAgent agent, final MethodDependency method) { private void achieveGetLength(final DependencyAgent agent, final MethodDependency method) {
method.getVariable(1).addConsumer(new DependencyConsumer() { method.getVariable(1).addConsumer(new DependencyConsumer() {
@Override public void consume(String type) { @Override public void consume(DependencyAgentType type) {
if (!type.startsWith("[")) { if (!type.getName().startsWith("[")) {
MethodReference cons = new MethodReference(IllegalArgumentException.class, "<init>", void.class); MethodReference cons = new MethodReference(IllegalArgumentException.class, "<init>", void.class);
agent.linkMethod(cons, method.getStack()).use(); 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) { private void achieveGet(final DependencyAgent agent, final MethodDependency method) {
method.getVariable(1).getArrayItem().connect(method.getResult()); method.getVariable(1).getArrayItem().connect(method.getResult());
method.getVariable(1).addConsumer(new DependencyConsumer() { method.getVariable(1).addConsumer(new DependencyConsumer() {
@Override public void consume(String type) { @Override public void consume(DependencyAgentType type) {
if (type.startsWith("[")) { if (type.getName().startsWith("[")) {
type = type.substring(1); String typeName = type.getName().substring(1);
for (int i = 0; i < primitiveTypes.length; ++i) { 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]; String wrapper = "java.lang." + primitiveWrappers[i];
MethodReference methodRef = new MethodReference(wrapper, "valueOf", MethodReference methodRef = new MethodReference(wrapper, "valueOf",
primitiveTypes[i], ValueType.object(wrapper)); primitiveTypes[i], ValueType.object(wrapper));
agent.linkMethod(methodRef, method.getStack()).use(); agent.linkMethod(methodRef, method.getStack()).use();
method.getResult().propagate("java.lang." + primitiveWrappers[i]); method.getResult().propagate(agent.getType("java.lang." + primitiveWrappers[i]));
} }
} }
} }

View File

@ -75,7 +75,7 @@ public class DateNativeGenerator implements Generator, DependencyPlugin {
case "toString": case "toString":
case "toLocaleFormat": case "toLocaleFormat":
case "toGMTString": case "toGMTString":
method.getResult().propagate("java.lang.String"); method.getResult().propagate(agent.getType("java.lang.String"));
break; break;
} }
} }

View File

@ -27,6 +27,8 @@ import org.teavm.model.MethodReference;
public interface DependencyAgent extends DependencyInfo, ServiceRepository { public interface DependencyAgent extends DependencyInfo, ServiceRepository {
DependencyNode createNode(); DependencyNode createNode();
DependencyAgentType getType(String name);
String generateClassName(); String generateClassName();
void submitClass(ClassHolder cls); void submitClass(ClassHolder cls);

View File

@ -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 <konsoletyper@gmail.com>
*/
public interface DependencyAgentType {
String getName();
DependencyAgent getDependencyAgent();
}

View File

@ -17,8 +17,6 @@ package org.teavm.dependency;
import java.io.IOException; import java.io.IOException;
import java.util.*; import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import org.teavm.common.*; import org.teavm.common.*;
import org.teavm.common.ConcurrentCachedMapper.KeyListener; import org.teavm.common.ConcurrentCachedMapper.KeyListener;
import org.teavm.model.*; import org.teavm.model.*;
@ -29,7 +27,6 @@ import org.teavm.model.util.ModelUtils;
* @author Alexey Andreev * @author Alexey Andreev
*/ */
public class DependencyChecker implements DependencyInfo, DependencyAgent { public class DependencyChecker implements DependencyInfo, DependencyAgent {
private static Object dummyValue = new Object();
static final boolean shouldLog = System.getProperty("org.teavm.logDependencies", "false").equals("true"); static final boolean shouldLog = System.getProperty("org.teavm.logDependencies", "false").equals("true");
private int classNameSuffix; private int classNameSuffix;
private DependencyClassSource classSource; private DependencyClassSource classSource;
@ -37,18 +34,20 @@ public class DependencyChecker implements DependencyInfo, DependencyAgent {
private FiniteExecutor executor; private FiniteExecutor executor;
private Mapper<MethodReference, MethodReader> methodReaderCache; private Mapper<MethodReference, MethodReader> methodReaderCache;
private Mapper<FieldReference, FieldReader> fieldReaderCache; private Mapper<FieldReference, FieldReader> fieldReaderCache;
private ConcurrentMap<MethodReference, DependencyStack> stacks = new ConcurrentHashMap<>(); private Map<MethodReference, DependencyStack> stacks = new HashMap<>();
private ConcurrentMap<FieldReference, DependencyStack> fieldStacks = new ConcurrentHashMap<>(); private Map<FieldReference, DependencyStack> fieldStacks = new HashMap<>();
private ConcurrentMap<String, DependencyStack> classStacks = new ConcurrentHashMap<>(); private Map<String, DependencyStack> classStacks = new HashMap<>();
private ConcurrentCachedMapper<MethodReference, MethodDependency> methodCache; private ConcurrentCachedMapper<MethodReference, MethodDependency> methodCache;
private ConcurrentCachedMapper<FieldReference, FieldDependency> fieldCache; private ConcurrentCachedMapper<FieldReference, FieldDependency> fieldCache;
private ConcurrentMap<String, Object> achievableClasses = new ConcurrentHashMap<>(); private Set<String> achievableClasses = new HashSet<>();
private ConcurrentMap<String, Object> initializedClasses = new ConcurrentHashMap<>(); private Set<String> initializedClasses = new HashSet<>();
private List<DependencyListener> listeners = new ArrayList<>(); private List<DependencyListener> listeners = new ArrayList<>();
private ServiceRepository services; private ServiceRepository services;
ConcurrentMap<MethodReference, DependencyStack> missingMethods = new ConcurrentHashMap<>(); Map<MethodReference, DependencyStack> missingMethods = new HashMap<>();
ConcurrentMap<String, DependencyStack> missingClasses = new ConcurrentHashMap<>(); Map<String, DependencyStack> missingClasses = new HashMap<>();
ConcurrentMap<FieldReference, DependencyStack> missingFields = new ConcurrentHashMap<>(); Map<FieldReference, DependencyStack> missingFields = new HashMap<>();
List<DependencyType> types = new ArrayList<>();
Map<String, DependencyType> typeMap = new HashMap<>();
public DependencyChecker(ClassReaderSource classSource, ClassLoader classLoader, ServiceRepository services) { public DependencyChecker(ClassReaderSource classSource, ClassLoader classLoader, ServiceRepository services) {
this(classSource, classLoader, services, new SimpleFiniteExecutor()); 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 @Override
public DependencyNode createNode() { public DependencyNode createNode() {
return new DependencyNode(this); return new DependencyNode(this);
@ -155,13 +165,13 @@ public class DependencyChecker implements DependencyInfo, DependencyAgent {
MethodDependency method = linkMethod(methodRef, DependencyStack.ROOT); MethodDependency method = linkMethod(methodRef, DependencyStack.ROOT);
method.use(); method.use();
DependencyNode[] varNodes = method.getVariables(); DependencyNode[] varNodes = method.getVariables();
varNodes[0].propagate(methodRef.getClassName()); varNodes[0].propagate(getType(methodRef.getClassName()));
for (int i = 0; i < argumentTypes.length; ++i) { 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() { executor.executeFast(new Runnable() {
@Override public void run() { @Override public void run() {
consumer.consume(type); consumer.consume(type);
@ -174,14 +184,14 @@ public class DependencyChecker implements DependencyInfo, DependencyAgent {
} }
boolean achieveClass(String className, DependencyStack stack) { boolean achieveClass(String className, DependencyStack stack) {
classStacks.putIfAbsent(className, stack); classStacks.put(className, stack);
boolean result = achievableClasses.putIfAbsent(className, dummyValue) == null; if (!achievableClasses.add(className)) {
if (result) { return false;
for (DependencyListener listener : listeners) {
listener.classAchieved(this, className);
}
} }
return result; for (DependencyListener listener : listeners) {
listener.classAchieved(this, className);
}
return true;
} }
@Override @Override
@ -189,16 +199,16 @@ public class DependencyChecker implements DependencyInfo, DependencyAgent {
if (methodRef == null) { if (methodRef == null) {
throw new IllegalArgumentException(); throw new IllegalArgumentException();
} }
stacks.putIfAbsent(methodRef, stack); stacks.put(methodRef, stack);
return methodCache.map(methodRef); return methodCache.map(methodRef);
} }
@Override @Override
public void initClass(String className, final DependencyStack stack) { public void initClass(String className, final DependencyStack stack) {
classStacks.putIfAbsent(className, stack); classStacks.put(className, stack);
MethodDescriptor clinitDesc = new MethodDescriptor("<clinit>", ValueType.VOID); MethodDescriptor clinitDesc = new MethodDescriptor("<clinit>", ValueType.VOID);
while (className != null) { while (className != null) {
if (initializedClasses.putIfAbsent(className, clinitDesc) != null) { if (!initializedClasses.add(className)) {
break; break;
} }
achieveClass(className, stack); achieveClass(className, stack);
@ -221,7 +231,7 @@ public class DependencyChecker implements DependencyInfo, DependencyAgent {
} }
private void achieveInterfaces(String className, DependencyStack stack) { private void achieveInterfaces(String className, DependencyStack stack) {
classStacks.putIfAbsent(className, stack); classStacks.put(className, stack);
ClassReader cls = classSource.get(className); ClassReader cls = classSource.get(className);
if (cls == null) { if (cls == null) {
missingClasses.put(className, stack); missingClasses.put(className, stack);
@ -306,7 +316,7 @@ public class DependencyChecker implements DependencyInfo, DependencyAgent {
thrown.setTag(methodRef + ":THROWN"); thrown.setTag(methodRef + ":THROWN");
} }
stack = new DependencyStack(methodRef, stack); 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); stack, method, methodRef);
if (method != null) { if (method != null) {
executor.execute(new Runnable() { executor.execute(new Runnable() {
@ -316,7 +326,7 @@ public class DependencyChecker implements DependencyInfo, DependencyAgent {
} }
}); });
} else { } else {
missingMethods.putIfAbsent(methodRef, stack); missingMethods.put(methodRef, stack);
} }
if (method != null) { if (method != null) {
final DependencyStack callerStack = stack; final DependencyStack callerStack = stack;
@ -347,12 +357,12 @@ public class DependencyChecker implements DependencyInfo, DependencyAgent {
@Override @Override
public Collection<String> getAchievableClasses() { public Collection<String> getAchievableClasses() {
return new HashSet<>(achievableClasses.keySet()); return new HashSet<>(achievableClasses);
} }
@Override @Override
public FieldDependency linkField(FieldReference fieldRef, DependencyStack stack) { public FieldDependency linkField(FieldReference fieldRef, DependencyStack stack) {
fieldStacks.putIfAbsent(fieldRef, stack); fieldStacks.put(fieldRef, stack);
return fieldCache.map(fieldRef); return fieldCache.map(fieldRef);
} }
@ -364,7 +374,7 @@ public class DependencyChecker implements DependencyInfo, DependencyAgent {
private FieldDependency createFieldNode(FieldReference fieldRef, FieldReader field, DependencyStack stack) { private FieldDependency createFieldNode(FieldReference fieldRef, FieldReader field, DependencyStack stack) {
DependencyNode node = new DependencyNode(this); DependencyNode node = new DependencyNode(this);
if (field == null) { if (field == null) {
missingFields.putIfAbsent(fieldRef, stack); missingFields.put(fieldRef, stack);
} }
if (shouldLog) { if (shouldLog) {
node.setTag(fieldRef.getClassName() + "#" + fieldRef.getFieldName()); node.setTag(fieldRef.getClassName() + "#" + fieldRef.getFieldName());

View File

@ -20,5 +20,5 @@ package org.teavm.dependency;
* @author Alexey Andreev <konsoletyper@gmail.com> * @author Alexey Andreev <konsoletyper@gmail.com>
*/ */
public interface DependencyConsumer { public interface DependencyConsumer {
void consume(String type); void consume(DependencyAgentType type);
} }

View File

@ -104,9 +104,10 @@ class DependencyGraphBuilder {
} }
@Override @Override
public void consume(String type) { public void consume(DependencyAgentType type) {
for (int i = 0; i < exceptions.length; ++i) { 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); vars[i].propagate(type);
return; return;
} }
@ -140,7 +141,8 @@ class DependencyGraphBuilder {
} }
@Override @Override
public void consume(String className) { public void consume(DependencyAgentType type) {
String className = type.getName();
if (DependencyChecker.shouldLog) { if (DependencyChecker.shouldLog) {
System.out.println("Virtual call of " + methodDesc + " detected on " + node.getTag() + ". " + System.out.println("Virtual call of " + methodDesc + " detected on " + node.getTag() + ". " +
"Target class is " + className); "Target class is " + className);
@ -201,8 +203,8 @@ class DependencyGraphBuilder {
private static class TypePropagationRunner implements Runnable { private static class TypePropagationRunner implements Runnable {
private DependencyNode node; private DependencyNode node;
private String type; private DependencyType type;
public TypePropagationRunner(DependencyNode node, String type) { public TypePropagationRunner(DependencyNode node, DependencyType type) {
this.node = node; this.node = node;
this.type = type; this.type = type;
} }
@ -222,7 +224,8 @@ class DependencyGraphBuilder {
@Override @Override
public void classConstant(VariableReader receiver, ValueType cst) { 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) { while (cst instanceof ValueType.Array) {
cst = ((ValueType.Array)cst).getItemType(); cst = ((ValueType.Array)cst).getItemType();
} }
@ -258,9 +261,10 @@ class DependencyGraphBuilder {
@Override @Override
public void stringConstant(VariableReader receiver, String cst) { 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, MethodDependency method = dependencyChecker.linkMethod(new MethodReference(String.class,
"<init>", char.class, void.class), callerStack); "<init>", char[].class, void.class), callerStack);
method.use(); method.use();
} }
@ -289,11 +293,11 @@ class DependencyGraphBuilder {
final ClassReader targetClass = dependencyChecker.getClassSource().get(targetClsName); final ClassReader targetClass = dependencyChecker.getClassSource().get(targetClsName);
if (targetClass != null) { if (targetClass != null) {
valueNode.connect(receiverNode, new DependencyTypeFilter() { valueNode.connect(receiverNode, new DependencyTypeFilter() {
@Override public boolean match(String type) { @Override public boolean match(DependencyAgentType type) {
if (targetClass.getName().equals("java.lang.Object")) { if (targetClass.getName().equals("java.lang.Object")) {
return true; return true;
} }
return isAssignableFrom(dependencyChecker.getClassSource(), targetClass, type); return isAssignableFrom(dependencyChecker.getClassSource(), targetClass, type.getName());
} }
}); });
return; return;
@ -345,7 +349,8 @@ class DependencyGraphBuilder {
@Override @Override
public void createArray(VariableReader receiver, ValueType itemType, VariableReader size) { 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); final String className = extractClassName(itemType);
if (className != null) { if (className != null) {
useRunners.add(new Runnable() { useRunners.add(new Runnable() {
@ -371,12 +376,13 @@ class DependencyGraphBuilder {
sb.append('['); sb.append('[');
} }
sb.append(itemType); sb.append(itemType);
useRunners.add(new TypePropagationRunner(nodes[receiver.getIndex()], sb.toString())); useRunners.add(new TypePropagationRunner(nodes[receiver.getIndex()],
dependencyChecker.getType(sb.toString())));
} }
@Override @Override
public void create(VariableReader receiver, String type) { 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 @Override
@ -405,7 +411,7 @@ class DependencyGraphBuilder {
DependencyNode arrayNode = nodes[array.getIndex()]; DependencyNode arrayNode = nodes[array.getIndex()];
final DependencyNode receiverNode = nodes[receiver.getIndex()]; final DependencyNode receiverNode = nodes[receiver.getIndex()];
arrayNode.addConsumer(new DependencyConsumer() { arrayNode.addConsumer(new DependencyConsumer() {
@Override public void consume(String type) { @Override public void consume(DependencyAgentType type) {
receiverNode.propagate(type); receiverNode.propagate(type);
} }
}); });
@ -522,7 +528,7 @@ class DependencyGraphBuilder {
"<init>", ValueType.VOID), callerStack); "<init>", ValueType.VOID), callerStack);
} }
}); });
currentExceptionConsumer.consume("java.lang.NullPointerException"); currentExceptionConsumer.consume(dependencyChecker.getType("java.lang.NullPointerException"));
} }
}; };
} }

View File

@ -15,10 +15,12 @@
*/ */
package org.teavm.dependency; package org.teavm.dependency;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicReference; 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 { public class DependencyNode implements ValueDependencyInfo {
private DependencyChecker dependencyChecker; private DependencyChecker dependencyChecker;
private static final Object mapValue = new Object(); private Set<DependencyConsumer> followers = new HashSet<>();
private ConcurrentMap<DependencyConsumer, Object> followers = new ConcurrentHashMap<>(); private BitSet types = new BitSet();
private ConcurrentMap<String, Object> types = new ConcurrentHashMap<>();
private ConcurrentMap<DependencyNode, DependencyNodeToNodeTransition> transitions = new ConcurrentHashMap<>(); private ConcurrentMap<DependencyNode, DependencyNodeToNodeTransition> transitions = new ConcurrentHashMap<>();
private volatile String tag; private volatile String tag;
private final AtomicReference<DependencyNode> arrayItemNode = new AtomicReference<>(); private final AtomicReference<DependencyNode> arrayItemNode = new AtomicReference<>();
@ -44,24 +45,36 @@ public class DependencyNode implements ValueDependencyInfo {
this.degree = degree; 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) { if (degree > 2) {
return; return;
} }
if (types.putIfAbsent(type, mapValue) == null) { if (!types.get(type.index)) {
types.set(type.index);
if (DependencyChecker.shouldLog) { 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); dependencyChecker.schedulePropagation(consumer, type);
} }
} }
} }
public void addConsumer(DependencyConsumer consumer) { public void addConsumer(DependencyConsumer consumer) {
if (followers.putIfAbsent(consumer, mapValue) == null) { if (followers.add(consumer)) {
for (String type : types.keySet().toArray(new String[0])) { IntegerArray indexes = new IntegerArray(8);
dependencyChecker.schedulePropagation(consumer, type); 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(); 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 @Override
public boolean hasType(String type) { public boolean hasType(String type) {
return types.containsKey(type); return hasType(dependencyChecker.getType(type));
} }
@Override @Override
public String[] getTypes() { public String[] getTypes() {
return types != null ? types.keySet().toArray(new String[types.size()]) : new String[0]; List<String> 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() { public String getTag() {

View File

@ -32,11 +32,11 @@ class DependencyNodeToNodeTransition implements DependencyConsumer {
} }
@Override @Override
public void consume(String type) { public void consume(DependencyAgentType type) {
if (filter != null && !filter.match(type)) { if (filter != null && !filter.match(type)) {
return; return;
} }
if (type.startsWith("[")) { if (type.getName().startsWith("[")) {
source.getArrayItem().connect(destination.getArrayItem()); source.getArrayItem().connect(destination.getArrayItem());
destination.getArrayItem().connect(source.getArrayItem()); destination.getArrayItem().connect(source.getArrayItem());
} }

View File

@ -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 <konsoletyper@gmail.com>
*/
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;
}
}

View File

@ -20,5 +20,5 @@ package org.teavm.dependency;
* @author Alexey Andreev * @author Alexey Andreev
*/ */
public interface DependencyTypeFilter { public interface DependencyTypeFilter {
boolean match(String type); boolean match(DependencyAgentType type);
} }

View File

@ -25,6 +25,7 @@ import org.teavm.model.MethodReference;
* @author Alexey Andreev * @author Alexey Andreev
*/ */
public class MethodDependency implements MethodDependencyInfo { public class MethodDependency implements MethodDependencyInfo {
private DependencyChecker dependencyChecker;
private DependencyNode[] variableNodes; private DependencyNode[] variableNodes;
private int parameterCount; private int parameterCount;
private DependencyNode resultNode; private DependencyNode resultNode;
@ -35,8 +36,10 @@ public class MethodDependency implements MethodDependencyInfo {
private AtomicBoolean used = new AtomicBoolean(); private AtomicBoolean used = new AtomicBoolean();
private volatile Runnable useRunner; private volatile Runnable useRunner;
MethodDependency(DependencyNode[] variableNodes, int parameterCount, DependencyNode resultNode, MethodDependency(DependencyChecker dependencyChecker, DependencyNode[] variableNodes, int parameterCount,
DependencyNode thrown, DependencyStack stack, MethodReader method, MethodReference reference) { DependencyNode resultNode, DependencyNode thrown, DependencyStack stack, MethodReader method,
MethodReference reference) {
this.dependencyChecker = dependencyChecker;
this.variableNodes = Arrays.copyOf(variableNodes, variableNodes.length); this.variableNodes = Arrays.copyOf(variableNodes, variableNodes.length);
this.parameterCount = parameterCount; this.parameterCount = parameterCount;
this.thrown = thrown; this.thrown = thrown;
@ -46,6 +49,10 @@ public class MethodDependency implements MethodDependencyInfo {
this.reference = reference; this.reference = reference;
} }
public DependencyAgent getDependencyAgent() {
return dependencyChecker;
}
@Override @Override
public DependencyNode[] getVariables() { public DependencyNode[] getVariables() {
return Arrays.copyOf(variableNodes, variableNodes.length); return Arrays.copyOf(variableNodes, variableNodes.length);

View File

@ -48,7 +48,7 @@ class ProgrammableDependencyGraphCreator implements DependencyGraphCreator {
checker.getClassSource().get(conn.superclass))); checker.getClassSource().get(conn.superclass)));
} }
for (TypePropagation propagation : typePropagations) { for (TypePropagation propagation : typePropagations) {
nodes[propagation.var].propagate(propagation.type); nodes[propagation.var].propagate(checker.getType(propagation.type));
} }
for (String className : initializedClasses) { for (String className : initializedClasses) {
checker.initClass(className, stack); checker.initClass(className, stack);
@ -79,11 +79,11 @@ class ProgrammableDependencyGraphCreator implements DependencyGraphCreator {
this.classSource = classSource; this.classSource = classSource;
this.superClass = superClass; this.superClass = superClass;
} }
@Override public boolean match(String type) { @Override public boolean match(DependencyAgentType type) {
if (superClass.getName().equals("java.lang.Object")) { if (superClass.getName().equals("java.lang.Object")) {
return true; return true;
} }
return isAssignableFrom(classSource, superClass, type); return isAssignableFrom(classSource, superClass, type.getName());
} }
} }

View File

@ -37,7 +37,7 @@ class TestExceptionDependency implements DependencyListener {
@Override @Override
public void classAchieved(DependencyAgent agent, String className) { public void classAchieved(DependencyAgent agent, String className) {
if (isException(agent.getClassSource(), className)) { if (isException(agent.getClassSource(), className)) {
allClasses.propagate(className); allClasses.propagate(agent.getType(className));
} }
} }

View File

@ -301,7 +301,7 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
ValueType.VOID), DependencyStack.ROOT).use(); ValueType.VOID), DependencyStack.ROOT).use();
MethodDependency internDep = dependencyChecker.linkMethod(new MethodReference("java.lang.String", "intern", MethodDependency internDep = dependencyChecker.linkMethod(new MethodReference("java.lang.String", "intern",
ValueType.object("java.lang.String")), DependencyStack.ROOT); 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(); internDep.use();
dependencyChecker.linkMethod(new MethodReference("java.lang.String", "length", ValueType.INTEGER), dependencyChecker.linkMethod(new MethodReference("java.lang.String", "length", ValueType.INTEGER),
DependencyStack.ROOT).use(); DependencyStack.ROOT).use();

View File

@ -88,7 +88,7 @@ public class TeaVMEntryPoint {
if (argument > reference.parameterCount()) { if (argument > reference.parameterCount()) {
throw new IllegalArgumentException("Illegal argument #" + argument + " of " + 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; return this;
} }
} }

View File

@ -37,7 +37,7 @@ public class JavaScriptBodyDependency implements DependencyListener {
public OneDirectionalConnection( DependencyNode target) { public OneDirectionalConnection( DependencyNode target) {
this.target = target; this.target = target;
} }
@Override public void consume(String type) { @Override public void consume(DependencyAgentType type) {
target.propagate(type); target.propagate(type);
} }
} }
@ -47,7 +47,7 @@ public class JavaScriptBodyDependency implements DependencyListener {
ClassReader cls = agent.getClassSource().get(className); ClassReader cls = agent.getClassSource().get(className);
if (cls != null && !cls.hasModifier(ElementModifier.ABSTRACT) && if (cls != null && !cls.hasModifier(ElementModifier.ABSTRACT) &&
!cls.hasModifier(ElementModifier.INTERFACE)) { !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.caller = caller;
this.superClass = agent.getClassSource().get(superMethod.getOwnerName()); this.superClass = agent.getClassSource().get(superMethod.getOwnerName());
} }
@Override public void consume(String type) { @Override public void consume(DependencyAgentType type) {
if (!isAssignableFrom(superClass, type)) { if (!isAssignableFrom(superClass, type.getName())) {
return; return;
} }
MethodReference methodRef = new MethodReference(type, superMethod.getDescriptor()); MethodReference methodRef = new MethodReference(type.getName(), superMethod.getDescriptor());
MethodDependency method = agent.linkMethod(methodRef, caller.getStack()); MethodDependency method = agent.linkMethod(methodRef, caller.getStack());
method.use(); method.use();
for (int i = 0; i < method.getParameterCount(); ++i) { for (int i = 0; i < method.getParameterCount(); ++i) {

View File

@ -118,8 +118,8 @@ public class JSNativeGenerator implements Generator, Injector, DependencyPlugin
public void methodAchieved(final DependencyAgent agent, final MethodDependency method) { public void methodAchieved(final DependencyAgent agent, final MethodDependency method) {
for (int i = 0; i < method.getReference().parameterCount(); ++i) { for (int i = 0; i < method.getReference().parameterCount(); ++i) {
method.getVariable(i).addConsumer(new DependencyConsumer() { method.getVariable(i).addConsumer(new DependencyConsumer() {
@Override public void consume(String type) { @Override public void consume(DependencyAgentType type) {
achieveFunctorMethods(agent, type, method); achieveFunctorMethods(agent, type.getName(), method);
} }
}); });
} }

View File

@ -38,7 +38,7 @@ class TestExceptionDependency implements DependencyListener {
@Override @Override
public void classAchieved(DependencyAgent agent, String className) { public void classAchieved(DependencyAgent agent, String className) {
if (isException(agent.getClassSource(), className)) { if (isException(agent.getClassSource(), className)) {
allClasses.propagate(className); allClasses.propagate(agent.getType(className));
} }
} }

View File

@ -37,7 +37,7 @@ class ResourceAccessorDependencyListener implements DependencyListener {
public void methodAchieved(DependencyAgent agent, MethodDependency method) { public void methodAchieved(DependencyAgent agent, MethodDependency method) {
switch (method.getReference().getName()) { switch (method.getReference().getName()) {
case "castToString": case "castToString":
method.getResult().propagate("java.lang.String"); method.getResult().propagate(agent.getType("java.lang.String"));
break; break;
} }
} }