Refuse from CallStack for complete CallGraph

This commit is contained in:
Alexey Andreev 2014-11-23 22:34:12 +03:00
parent e736cf09d7
commit 30781bb16e
30 changed files with 714 additions and 296 deletions

View File

@ -0,0 +1,41 @@
/*
* 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.callgraph;
import java.util.Collection;
import org.teavm.model.FieldReference;
import org.teavm.model.MethodReference;
/**
* <p>Root object for traversing through call graph. Graph does not represent polymorphic calls.
* Instead, it generated multiple call sites for one location.</p>
*
* @author Alexey Andreev
*/
public interface CallGraph {
/**
* <p>Get node corresponding to the specific method.</p>
*
* @param method a method to lookup node for.
* @return the node that corresponds to the specified method or <code>null</code>, if this method is never
* called.
*/
CallGraphNode getNode(MethodReference method);
Collection<? extends FieldAccessSite> getFieldAccess(FieldReference reference);
Collection<? extends ClassAccessSite> getClassAccess(String className);
}

View File

@ -0,0 +1,49 @@
/*
* 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.callgraph;
import java.util.Collection;
import org.teavm.model.MethodReference;
/**
* Represents a method with information about what methods does it call and what method do call the method.
* @author Alexey Andreev
*/
public interface CallGraphNode {
/*
* Returns reference to entire call graph.
*/
CallGraph getGraph();
/**
* Returns the method that this node represents.
*/
MethodReference getMethod();
/**
* Returns immutable collection of all call sites that are in the method.
*/
Collection<? extends CallSite> getCallSites();
/**
* Returns immutable collection of all call sites that call this method.
*/
Collection<? extends CallSite> getCallerCallSites();
Collection<? extends FieldAccessSite> getFieldAccessSites();
Collection<? extends ClassAccessSite> getClassAccessSites();
}

View File

@ -0,0 +1,41 @@
/*
* 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.callgraph;
import org.teavm.model.InstructionLocation;
/**
* <p>Call site that represents exact place in the code that calls a method.</p>.
* @author Alexey Andreev
*/
public interface CallSite {
/**
* <p>Gets location of the call site</p>.
*
* @return location of the call site or <code>null</code> if no debug information found for this call site.
*/
InstructionLocation getLocation();
/**
* <p>Gets a method that this call site invokes.</p>
*/
CallGraphNode getCallee();
/**
* <p>Gets a method that contains this call site.</p>
*/
CallGraphNode getCaller();
}

View File

@ -0,0 +1,30 @@
/*
* 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.callgraph;
import org.teavm.model.InstructionLocation;
/**
*
* @author Alexey Andreev
*/
public interface ClassAccessSite {
InstructionLocation getLocation();
CallGraphNode getCallee();
String getClassName();
}

View File

@ -0,0 +1,72 @@
/*
* 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.callgraph;
import java.util.*;
import org.teavm.model.FieldReference;
import org.teavm.model.MethodReference;
/**
*
* @author Alexey Andreev
*/
public class DefaultCallGraph implements CallGraph {
private Map<MethodReference, DefaultCallGraphNode> nodes = new HashMap<>();
private Map<FieldReference, Set<DefaultFieldAccessSite>> fieldAccessSites = new HashMap<>();
private Map<String, Set<DefaultClassAccessSite>> classAccessSites = new HashMap<>();
@Override
public DefaultCallGraphNode getNode(MethodReference method) {
DefaultCallGraphNode node = nodes.get(method);
if (node == null) {
node = new DefaultCallGraphNode(this);
nodes.put(method, node);
}
return nodes.get(method);
}
@Override
public Collection<DefaultFieldAccessSite> getFieldAccess(FieldReference reference) {
Set<DefaultFieldAccessSite> resultSet = fieldAccessSites.get(reference);
return resultSet != null ? Collections.unmodifiableSet(resultSet) :
Collections.<DefaultFieldAccessSite>emptySet();
}
void addFieldAccess(DefaultFieldAccessSite accessSite) {
Set<DefaultFieldAccessSite> sites = fieldAccessSites.get(accessSite.getField());
if (sites == null) {
sites = new HashSet<>();
fieldAccessSites.put(accessSite.getField(), sites);
}
sites.add(accessSite);
}
@Override
public Collection<DefaultClassAccessSite> getClassAccess(String className) {
Set<DefaultClassAccessSite> resultSet = classAccessSites.get(className);
return resultSet != null ? Collections.unmodifiableSet(resultSet) :
Collections.<DefaultClassAccessSite>emptySet();
}
void addClassAccess(DefaultClassAccessSite accessSite) {
Set<DefaultClassAccessSite> sites = classAccessSites.get(accessSite.getClassName());
if (sites == null) {
sites = new HashSet<>();
classAccessSites.put(accessSite.getClassName(), sites);
}
sites.add(accessSite);
}
}

View File

@ -0,0 +1,113 @@
/*
* 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.callgraph;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import org.teavm.model.FieldReference;
import org.teavm.model.InstructionLocation;
import org.teavm.model.MethodReference;
/**
*
* @author Alexey Andreev
*/
public class DefaultCallGraphNode implements CallGraphNode {
private DefaultCallGraph graph;
private MethodReference method;
private Set<DefaultCallSite> callSites;
private transient Set<DefaultCallSite> safeCallSites;
private List<DefaultCallSite> callerCallSites;
private transient List<DefaultCallSite> safeCallersCallSites;
private Set<DefaultFieldAccessSite> fieldAccessSites;
private transient Set<DefaultFieldAccessSite> safeFieldAccessSites;
private Set<DefaultClassAccessSite> classAccessSites;
private transient Set<DefaultClassAccessSite> safeClassAccessSites;
DefaultCallGraphNode(DefaultCallGraph graph) {
this.graph = graph;
}
@Override
public DefaultCallGraph getGraph() {
return graph;
}
@Override
public MethodReference getMethod() {
return method;
}
@Override
public Collection<DefaultCallSite> getCallSites() {
if (safeCallSites == null) {
safeCallSites = Collections.unmodifiableSet(callSites);
}
return safeCallSites;
}
@Override
public Collection<DefaultCallSite> getCallerCallSites() {
if (safeCallersCallSites == null) {
safeCallersCallSites = Collections.unmodifiableList(callerCallSites);
}
return safeCallersCallSites;
}
public void addCallSite(MethodReference method, InstructionLocation location) {
DefaultCallGraphNode callee = graph.getNode(method);
DefaultCallSite callSite = new DefaultCallSite(location, callee, this);
if (callSites.add(callSite)) {
callee.callerCallSites.add(callSite);
}
}
public void addCallSite(MethodReference method) {
addCallSite(method, null);
}
@Override
public Collection<DefaultFieldAccessSite> getFieldAccessSites() {
if (safeFieldAccessSites == null) {
safeFieldAccessSites = Collections.unmodifiableSet(fieldAccessSites);
}
return safeFieldAccessSites;
}
public void addFieldAccess(FieldReference field, InstructionLocation location) {
DefaultFieldAccessSite site = new DefaultFieldAccessSite(location, this, field);
if (fieldAccessSites.add(site)) {
graph.addFieldAccess(site);
}
}
@Override
public Collection<? extends ClassAccessSite> getClassAccessSites() {
if (safeClassAccessSites == null) {
safeClassAccessSites = Collections.unmodifiableSet(classAccessSites);
}
return safeClassAccessSites;
}
public void addClassAccess(String className, InstructionLocation location) {
DefaultClassAccessSite site = new DefaultClassAccessSite(location, this, className);
if (classAccessSites.add(site)) {
graph.addClassAccess(site);
}
}
}

View File

@ -0,0 +1,68 @@
/*
* 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.callgraph;
import java.util.Objects;
import org.teavm.model.InstructionLocation;
/**
*
* @author Alexey Andreev
*/
public class DefaultCallSite implements CallSite {
private InstructionLocation location;
private DefaultCallGraphNode callee;
private DefaultCallGraphNode caller;
DefaultCallSite(InstructionLocation location, DefaultCallGraphNode callee, DefaultCallGraphNode caller) {
this.location = location;
this.callee = callee;
this.caller = caller;
}
@Override
public InstructionLocation getLocation() {
return location;
}
@Override
public DefaultCallGraphNode getCallee() {
return callee;
}
@Override
public DefaultCallGraphNode getCaller() {
return caller;
}
@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (!(obj instanceof DefaultCallSite)) {
return false;
}
DefaultCallSite other = (DefaultCallSite)obj;
return Objects.equals(callee.getMethod(), other.callee.getMethod()) &&
Objects.equals(location, other.location);
}
@Override
public int hashCode() {
return Objects.hash(callee.getMethod(), location);
}
}

View File

@ -0,0 +1,49 @@
/*
* 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.callgraph;
import org.teavm.model.InstructionLocation;
/**
*
* @author Alexey Andreev
*/
public class DefaultClassAccessSite implements ClassAccessSite {
private InstructionLocation location;
private CallGraphNode callee;
private String className;
DefaultClassAccessSite(InstructionLocation location, CallGraphNode callee, String className) {
this.location = location;
this.callee = callee;
this.className = className;
}
@Override
public InstructionLocation getLocation() {
return location;
}
@Override
public CallGraphNode getCallee() {
return callee;
}
@Override
public String getClassName() {
return className;
}
}

View File

@ -0,0 +1,69 @@
/*
* 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.callgraph;
import java.util.Objects;
import org.teavm.model.FieldReference;
import org.teavm.model.InstructionLocation;
/**
*
* @author Alexey Andreev
*/
public class DefaultFieldAccessSite implements FieldAccessSite {
private InstructionLocation location;
private CallGraphNode callee;
private FieldReference field;
DefaultFieldAccessSite(InstructionLocation location, CallGraphNode callee, FieldReference field) {
this.location = location;
this.callee = callee;
this.field = field;
}
@Override
public InstructionLocation getLocation() {
return null;
}
@Override
public CallGraphNode getCallee() {
return null;
}
@Override
public FieldReference getField() {
return null;
}
@Override
public int hashCode() {
return Objects.hash(location, callee, field);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof DefaultFieldAccessSite)) {
return false;
}
DefaultFieldAccessSite other = (DefaultFieldAccessSite)obj;
return Objects.equals(location, other.location) && Objects.equals(callee, other.callee) &&
Objects.equals(field, other.field);
}
}

View File

@ -0,0 +1,31 @@
/*
* 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.callgraph;
import org.teavm.model.FieldReference;
import org.teavm.model.InstructionLocation;
/**
*
* @author Alexey Andreev
*/
public interface FieldAccessSite {
InstructionLocation getLocation();
CallGraphNode getCallee();
FieldReference getField();
}

View File

@ -16,6 +16,8 @@
package org.teavm.dependency;
import org.teavm.model.ClassReader;
import org.teavm.model.InstructionLocation;
import org.teavm.model.MethodReference;
/**
*
@ -24,13 +26,11 @@ import org.teavm.model.ClassReader;
public class ClassDependency implements ClassDependencyInfo {
private DependencyChecker checker;
private String className;
private DependencyStack stack;
private ClassReader classReader;
ClassDependency(DependencyChecker checker, String className, DependencyStack stack, ClassReader classReader) {
ClassDependency(DependencyChecker checker, String className, ClassReader classReader) {
this.checker = checker;
this.className = className;
this.stack = stack;
this.classReader = classReader;
}
@ -48,14 +48,9 @@ public class ClassDependency implements ClassDependencyInfo {
return classReader;
}
@Override
public DependencyStack getStack() {
return stack;
}
public void initClass(DependencyStack stack) {
public void initClass(MethodReference caller, InstructionLocation location) {
if (!isMissing()) {
checker.initClass(this, stack);
checker.initClass(this, caller, location);
}
}
}

View File

@ -23,6 +23,4 @@ public interface ClassDependencyInfo {
String getClassName();
boolean isMissing();
DependencyStack getStack();
}

View File

@ -16,10 +16,8 @@
package org.teavm.dependency;
import org.teavm.common.ServiceRepository;
import org.teavm.model.ClassHolder;
import org.teavm.model.Diagnostics;
import org.teavm.model.FieldReference;
import org.teavm.model.MethodReference;
import org.teavm.diagnostics.Diagnostics;
import org.teavm.model.*;
/**
*
@ -34,11 +32,11 @@ public interface DependencyAgent extends DependencyInfo, ServiceRepository {
void submitClass(ClassHolder cls);
MethodDependency linkMethod(MethodReference methodRef, DependencyStack stack);
MethodDependency linkMethod(MethodReference methodRef, MethodReference caller, InstructionLocation location);
ClassDependency linkClass(String className, final DependencyStack stack);
ClassDependency linkClass(String className, MethodReference caller, InstructionLocation location);
FieldDependency linkField(FieldReference fieldRef, DependencyStack stack);
FieldDependency linkField(FieldReference fieldRef, MethodReference caller, InstructionLocation location);
Diagnostics getDiagnostics();
}

View File

@ -17,8 +17,11 @@ package org.teavm.dependency;
import java.io.IOException;
import java.util.*;
import org.teavm.callgraph.CallGraph;
import org.teavm.callgraph.DefaultCallGraph;
import org.teavm.common.*;
import org.teavm.common.CachedMapper.KeyListener;
import org.teavm.diagnostics.Diagnostics;
import org.teavm.model.*;
import org.teavm.model.util.ModelUtils;
@ -33,9 +36,6 @@ public class DependencyChecker implements DependencyInfo, DependencyAgent {
private ClassLoader classLoader;
private Mapper<MethodReference, MethodReader> methodReaderCache;
private Mapper<FieldReference, FieldReader> fieldReaderCache;
private Map<MethodReference, DependencyStack> stacks = new HashMap<>();
private Map<FieldReference, DependencyStack> fieldStacks = new HashMap<>();
private Map<String, DependencyStack> classStacks = new HashMap<>();
private CachedMapper<MethodReference, MethodDependency> methodCache;
private CachedMapper<FieldReference, FieldDependency> fieldCache;
private CachedMapper<String, ClassDependency> classCache;
@ -51,6 +51,7 @@ public class DependencyChecker implements DependencyInfo, DependencyAgent {
private DependencyCheckerInterruptor interruptor;
private boolean interrupted;
private Diagnostics diagnostics;
private DefaultCallGraph callGraph = new DefaultCallGraph();
public DependencyChecker(ClassReaderSource classSource, ClassLoader classLoader, ServiceRepository services,
Diagnostics diagnostics) {
@ -72,26 +73,24 @@ public class DependencyChecker implements DependencyInfo, DependencyAgent {
@Override public MethodDependency map(MethodReference preimage) {
MethodReader method = methodReaderCache.map(preimage);
if (method != null && !method.getReference().equals(preimage)) {
stacks.put(method.getReference(), stacks.get(preimage));
return methodCache.map(method.getReference());
}
return createMethodDep(preimage, method, stacks.get(preimage));
return createMethodDep(preimage, method);
}
});
fieldCache = new CachedMapper<>(new Mapper<FieldReference, FieldDependency>() {
@Override public FieldDependency map(FieldReference preimage) {
FieldReader field = fieldReaderCache.map(preimage);
if (field != null && !field.getReference().equals(preimage)) {
fieldStacks.put(field.getReference(), fieldStacks.get(preimage));
return fieldCache.map(field.getReference());
}
return createFieldNode(preimage, field, fieldStacks.get(preimage));
return createFieldNode(preimage, field);
}
});
classCache = new CachedMapper<>(new Mapper<String, ClassDependency>() {
@Override public ClassDependency map(String preimage) {
return createClassDependency(preimage, classStacks.get(preimage));
return createClassDependency(preimage);
}
});
methodCache.addKeyListener(new KeyListener<MethodReference>() {
@ -167,7 +166,7 @@ public class DependencyChecker implements DependencyInfo, DependencyAgent {
@Override
public String generateClassName() {
return "$$tmp$$.TempClass" + classNameSuffix++;
return "$$teavm_generated_class$$" + classNameSuffix++;
}
@Override
@ -189,7 +188,7 @@ public class DependencyChecker implements DependencyInfo, DependencyAgent {
if (parameters.length + 1 != argumentTypes.length) {
throw new IllegalArgumentException("argumentTypes length does not match the number of method's arguments");
}
MethodDependency method = linkMethod(methodRef, DependencyStack.ROOT);
MethodDependency method = linkMethod(methodRef, null, null);
method.use();
DependencyNode[] varNodes = method.getVariables();
varNodes[0].propagate(getType(methodRef.getClassName()));
@ -218,7 +217,6 @@ public class DependencyChecker implements DependencyInfo, DependencyAgent {
@Override
public ClassDependency linkClass(String className, DependencyStack stack) {
classStacks.put(className, stack);
return classCache.map(className);
}
@ -239,21 +237,26 @@ public class DependencyChecker implements DependencyInfo, DependencyAgent {
}
@Override
public MethodDependency linkMethod(MethodReference methodRef, DependencyStack stack) {
public MethodDependency linkMethod(MethodReference methodRef, MethodReference caller,
InstructionLocation location) {
if (methodRef == null) {
throw new IllegalArgumentException();
}
stacks.put(methodRef, stack);
callGraph.addNode(methodRef);
if (caller != null) {
callGraph.addNode(caller);
callGraph.getNode(caller).addCallSite(methodRef, location);
}
return methodCache.map(methodRef);
}
void initClass(ClassDependency cls, final DependencyStack stack) {
void initClass(ClassDependency cls, final MethodReference caller, final InstructionLocation location) {
ClassReader reader = cls.getClassReader();
final MethodReader method = reader.getMethod(new MethodDescriptor("<clinit>", void.class));
if (method != null) {
tasks.add(new Runnable() {
@Override public void run() {
linkMethod(method.getReference(), stack).use();
linkMethod(method.getReference(), caller, location).use();
}
});
}
@ -373,7 +376,7 @@ public class DependencyChecker implements DependencyInfo, DependencyAgent {
}
@Override
public FieldDependency linkField(FieldReference fieldRef, DependencyStack stack) {
public FieldDependency linkField(FieldReference fieldRef, MethodReference caller, InstructionLocation location) {
fieldStacks.put(fieldRef, stack);
return fieldCache.map(fieldRef);
}
@ -434,25 +437,6 @@ public class DependencyChecker implements DependencyInfo, DependencyAgent {
return methodCache.getKnown(methodRef);
}
public DependencyViolations getViolations() {
if (dependencyViolations == null) {
dependencyViolations = new DependencyViolations(missingMethods, missingClasses, missingFields);
}
return dependencyViolations;
}
public void checkForViolations() {
getViolations().checkForViolations();
}
public boolean hasViolations() {
return getViolations().hasSevereViolations();
}
public void showViolations(Appendable sb) throws IOException {
getViolations().showViolations(sb);
}
public void processDependencies() {
interrupted = false;
int index = 0;
@ -477,4 +461,8 @@ public class DependencyChecker implements DependencyInfo, DependencyAgent {
public Diagnostics getDiagnostics() {
return diagnostics;
}
public CallGraph getCallGraph() {
return callGraph;
}
}

View File

@ -22,6 +22,7 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import org.teavm.common.CachedMapper;
import org.teavm.common.Mapper;
import org.teavm.diagnostics.Diagnostics;
import org.teavm.model.*;
import org.teavm.model.util.ModelUtils;

View File

@ -18,6 +18,7 @@ package org.teavm.dependency;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.teavm.callgraph.DefaultCallGraphNode;
import org.teavm.model.*;
import org.teavm.model.instructions.*;
import org.teavm.model.util.ListingBuilder;
@ -31,7 +32,8 @@ class DependencyGraphBuilder {
private DependencyNode[] nodes;
private DependencyNode resultNode;
private ProgramReader program;
private DependencyStack callerStack;
private DefaultCallGraphNode caller;
private InstructionLocation currentLocation;
private ExceptionConsumer currentExceptionConsumer;
public DependencyGraphBuilder(DependencyChecker dependencyChecker) {
@ -50,7 +52,6 @@ class DependencyGraphBuilder {
}
resultNode = dep.getResult();
nodes = dep.getVariables();
callerStack = new DependencyStack(method.getReference(), dep.getStack());
for (int i = 0; i < program.basicBlockCount(); ++i) {
BasicBlockReader block = program.basicBlockAt(i);
currentExceptionConsumer = createExceptionConsumer(dep, block);
@ -62,7 +63,7 @@ class DependencyGraphBuilder {
}
for (TryCatchBlockReader tryCatch : block.readTryCatchBlocks()) {
if (tryCatch.getExceptionType() != null) {
dependencyChecker.linkClass(tryCatch.getExceptionType(), callerStack);
dependencyChecker.linkClass(tryCatch.getExceptionType(), caller.getMethod(), null);
}
}
}
@ -116,20 +117,23 @@ class DependencyGraphBuilder {
private final DependencyChecker checker;
private final DependencyNode[] parameters;
private final DependencyNode result;
private final DependencyStack stack;
private final DefaultCallGraphNode caller;
private final InstructionLocation location;
private final Set<MethodReference> knownMethods = new HashSet<>();
private ExceptionConsumer exceptionConsumer;
public VirtualCallConsumer(DependencyNode node, ClassReader filterClass,
MethodDescriptor methodDesc, DependencyChecker checker, DependencyNode[] parameters,
DependencyNode result, DependencyStack stack, ExceptionConsumer exceptionConsumer) {
DependencyNode result, DefaultCallGraphNode caller, InstructionLocation location,
ExceptionConsumer exceptionConsumer) {
this.node = node;
this.filterClass = filterClass;
this.methodDesc = methodDesc;
this.checker = checker;
this.parameters = parameters;
this.result = result;
this.stack = stack;
this.caller = caller;
this.location = location;
this.exceptionConsumer = exceptionConsumer;
}
@ -147,7 +151,7 @@ class DependencyGraphBuilder {
return;
}
MethodReference methodRef = new MethodReference(className, methodDesc);
MethodDependency methodDep = checker.linkMethod(methodRef, stack);
MethodDependency methodDep = checker.linkMethod(methodRef, caller.getMethod(), location);
if (!methodDep.isMissing() && knownMethods.add(methodRef)) {
methodDep.use();
DependencyNode[] targetParams = methodDep.getVariables();
@ -185,7 +189,7 @@ class DependencyGraphBuilder {
private InstructionReader reader = new InstructionReader() {
@Override
public void location(InstructionLocation location) {
callerStack = new DependencyStack(callerStack.getMethod(), location, callerStack.getCause());
currentLocation = location;
}
@Override
@ -200,7 +204,7 @@ class DependencyGraphBuilder {
}
if (cst instanceof ValueType.Object) {
final String className = ((ValueType.Object)cst).getClassName();
dependencyChecker.linkClass(className, callerStack);
dependencyChecker.linkClass(className, caller.getMethod(), currentLocation);
}
}
@ -228,7 +232,7 @@ class DependencyGraphBuilder {
public void stringConstant(VariableReader receiver, String cst) {
nodes[receiver.getIndex()].propagate(dependencyChecker.getType("java.lang.String"));
MethodDependency method = dependencyChecker.linkMethod(new MethodReference(String.class,
"<init>", char[].class, void.class), callerStack);
"<init>", char[].class, void.class), caller.getMethod(), currentLocation);
method.use();
}
@ -316,7 +320,7 @@ class DependencyGraphBuilder {
nodes[receiver.getIndex()].propagate(dependencyChecker.getType("[" + itemType));
String className = extractClassName(itemType);
if (className != null) {
dependencyChecker.linkClass(className, callerStack);
dependencyChecker.linkClass(className, caller.getMethod(), currentLocation);
}
}
@ -338,7 +342,7 @@ class DependencyGraphBuilder {
nodes[receiver.getIndex()].propagate(dependencyChecker.getType(sb.toString()));
String className = extractClassName(itemType);
if (className != null) {
dependencyChecker.linkClass(className, callerStack);
dependencyChecker.linkClass(className, caller.getMethod(), currentLocation);
}
}
@ -350,7 +354,7 @@ class DependencyGraphBuilder {
@Override
public void getField(VariableReader receiver, VariableReader instance, FieldReference field,
ValueType fieldType) {
FieldDependency fieldDep = dependencyChecker.linkField(field, callerStack);
FieldDependency fieldDep = dependencyChecker.linkField(field, caller.getMethod(), currentLocation);
DependencyNode receiverNode = nodes[receiver.getIndex()];
fieldDep.getValue().connect(receiverNode);
initClass(field.getClassName());
@ -358,7 +362,7 @@ class DependencyGraphBuilder {
@Override
public void putField(VariableReader instance, FieldReference field, VariableReader value) {
FieldDependency fieldDep = dependencyChecker.linkField(field, callerStack);
FieldDependency fieldDep = dependencyChecker.linkField(field, caller.getMethod(), currentLocation);
DependencyNode valueNode = nodes[value.getIndex()];
valueNode.connect(fieldDep.getValue());
initClass(field.getClassName());
@ -420,7 +424,7 @@ class DependencyGraphBuilder {
private void invokeSpecial(VariableReader receiver, VariableReader instance, MethodReference method,
List<? extends VariableReader> arguments) {
MethodDependency methodDep = dependencyChecker.linkMethod(method, callerStack);
MethodDependency methodDep = dependencyChecker.linkMethod(method, caller.getMethod(), currentLocation);
if (methodDep.isMissing()) {
return;
}
@ -441,7 +445,7 @@ class DependencyGraphBuilder {
private void invokeVirtual(VariableReader receiver, VariableReader instance, MethodReference method,
List<? extends VariableReader> arguments) {
MethodDependency methodDep = dependencyChecker.linkMethod(method, callerStack);
MethodDependency methodDep = dependencyChecker.linkMethod(method, caller.getMethod(), currentLocation);
if (methodDep.isMissing()) {
return;
}
@ -453,7 +457,7 @@ class DependencyGraphBuilder {
DependencyConsumer listener = new VirtualCallConsumer(nodes[instance.getIndex()],
dependencyChecker.getClassSource().get(methodDep.getMethod().getOwnerName()),
method.getDescriptor(), dependencyChecker, actualArgs,
receiver != null ? nodes[receiver.getIndex()] : null, callerStack,
receiver != null ? nodes[receiver.getIndex()] : null, caller, currentLocation,
currentExceptionConsumer);
nodes[instance.getIndex()].addConsumer(listener);
}
@ -462,13 +466,14 @@ class DependencyGraphBuilder {
public void isInstance(VariableReader receiver, VariableReader value, final ValueType type) {
String className = extractClassName(type);
if (className != null) {
dependencyChecker.linkClass(className, callerStack);
dependencyChecker.linkClass(className, caller.getMethod(), currentLocation);
}
}
@Override
public void initClass(final String className) {
dependencyChecker.linkClass(className, callerStack).initClass(callerStack);
dependencyChecker.linkClass(className, caller.getMethod(), currentLocation)
.initClass(caller.getMethod(), currentLocation);
}
@Override
@ -476,8 +481,8 @@ class DependencyGraphBuilder {
DependencyNode valueNode = nodes[value.getIndex()];
DependencyNode receiverNode = nodes[receiver.getIndex()];
valueNode.connect(receiverNode);
dependencyChecker.linkMethod(new MethodReference("java.lang.NullPointerException",
"<init>", ValueType.VOID), callerStack).use();
dependencyChecker.linkMethod(new MethodReference(NullPointerException.class, "<init>", void.class),
caller.getMethod(), currentLocation).use();
currentExceptionConsumer.consume(dependencyChecker.getType("java.lang.NullPointerException"));
}
};

View File

@ -1,85 +0,0 @@
/*
* 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;
import org.teavm.model.InstructionLocation;
import org.teavm.model.MethodReference;
/**
*
* @author Alexey Andreev
*/
public class DependencyStack {
public static final DependencyStack ROOT = new DependencyStack();
private MethodReference method;
private DependencyStack cause;
private InstructionLocation location;
private DependencyStack() {
}
public DependencyStack(MethodReference method) {
this(method, ROOT);
}
public DependencyStack(MethodReference method, InstructionLocation location) {
this(method, location, ROOT);
}
public DependencyStack(MethodReference method, DependencyStack cause) {
this(method, null, cause);
}
public DependencyStack(MethodReference method, InstructionLocation location, DependencyStack cause) {
if (method == null || cause == null) {
throw new IllegalArgumentException("Arguments must not be null");
}
this.method = method;
this.location = location;
this.cause = cause;
}
public MethodReference getMethod() {
return method;
}
public DependencyStack getCause() {
return cause;
}
public InstructionLocation getLocation() {
return location;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
DependencyStack stack = this;
while (true) {
if (stack.method == null) {
sb.append(" used by ROOT\n");
break;
} else {
sb.append(" used by " + stack.method);
if (stack.location != null) {
sb.append(" : ").append(stack.location.getLine());
}
stack = stack.cause;
}
}
return sb.toString();
}
}

View File

@ -1,102 +0,0 @@
/*
* 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;
import java.io.IOException;
import java.util.*;
/**
*
* @author Alexey Andreev
*/
public class DependencyViolations {
private final Set<MethodDependencyInfo> missingMethods;
private final Set<ClassDependencyInfo> missingClasses;
private final Set<FieldDependencyInfo> missingFields;
DependencyViolations(Collection<? extends MethodDependencyInfo> missingMethods,
Collection<? extends ClassDependencyInfo> missingClasses,
Collection<? extends FieldDependencyInfo> missingFields) {
this.missingMethods = Collections.unmodifiableSet(new HashSet<>(missingMethods));
this.missingClasses = Collections.unmodifiableSet(new HashSet<>(missingClasses));
this.missingFields = Collections.unmodifiableSet(new HashSet<>(missingFields));
}
public Set<MethodDependencyInfo> getMissingMethods() {
return missingMethods;
}
public Set<ClassDependencyInfo> getMissingClasses() {
return missingClasses;
}
public Set<FieldDependencyInfo> getMissingFields() {
return missingFields;
}
public boolean hasSevereViolations() {
return !missingMethods.isEmpty() || !missingClasses.isEmpty() || !missingFields.isEmpty();
}
public void checkForViolations() {
if (!hasSevereViolations()) {
return;
}
StringBuilder sb = new StringBuilder();
try {
showViolations(sb);
} catch (IOException e) {
throw new AssertionError("StringBuilder should not throw IOException");
}
throw new IllegalStateException(sb.toString());
}
public void showViolations(Appendable sb) throws IOException {
List<String> items = new ArrayList<>();
Map<String, DependencyStack> stackMap = new HashMap<>();
for (ClassDependencyInfo cls : missingClasses) {
stackMap.put(cls.getClassName(), cls.getStack());
items.add(cls.getClassName());
}
for (MethodDependencyInfo method : missingMethods) {
stackMap.put(method.getReference().toString(), method.getStack());
items.add(method.getReference().toString());
}
for (FieldDependencyInfo field : missingFields) {
stackMap.put(field.getReference().toString(), field.getStack());
items.add(field.getReference().toString());
}
Collections.sort(items);
sb.append("Can't compile due to the following violations:\n");
for (String item : items) {
sb.append("Missing ").append(item).append("\n");
DependencyStack stack = stackMap.get(item);
if (stack == null) {
sb.append(" at unknown location\n");
} else {
while (stack.getMethod() != null) {
sb.append(" at ").append(stack.getMethod().toString());
if (stack.getLocation() != null) {
sb.append(":").append(String.valueOf(stack.getLocation().getLine()));
}
sb.append("\n");
stack = stack.getCause();
}
}
sb.append('\n');
}
}
}

View File

@ -27,6 +27,4 @@ public interface FieldDependencyInfo {
FieldReference getReference();
boolean isMissing();
DependencyStack getStack();
}

View File

@ -39,6 +39,4 @@ public interface MethodDependencyInfo {
boolean isUsed();
boolean isMissing();
DependencyStack getStack();
}

View File

@ -13,26 +13,24 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.dependency;
package org.teavm.diagnostics;
import java.util.ArrayList;
import java.util.List;
import org.teavm.model.Diagnostics;
import org.teavm.model.InstructionLocation;
import org.teavm.vm.DiagnosticsProblem;
import org.teavm.vm.DiagnosticsProblemSeverity;
import org.teavm.model.MethodReference;
/**
*
* @author Alexey Andreev <konsoletyper@gmail.com>
*/
class DependencyDiagnostics implements Diagnostics {
private List<DiagnosticsProblem> problems = new ArrayList<>();
private List<DiagnosticsProblem> severeProblems = new ArrayList<>();
private List<Problem> problems = new ArrayList<>();
private List<Problem> severeProblems = new ArrayList<>();
@Override
public void error(InstructionLocation location, String error) {
DiagnosticsProblem violation = new DiagnosticsProblem(DiagnosticsProblemSeverity.ERROR, location, error);
public void error(MethodReference method, InstructionLocation location, String error, Object[] params) {
Problem violation = new Problem(ProblemSeverity.ERROR, location, error);
problems.add(violation);
severeProblems.add(violation);
}
@ -44,7 +42,7 @@ class DependencyDiagnostics implements Diagnostics {
@Override
public void warning(InstructionLocation location, String error) {
DiagnosticsProblem violation = new DiagnosticsProblem(DiagnosticsProblemSeverity.WARNING, location, error);
Problem violation = new Problem(ProblemSeverity.WARNING, location, error);
problems.add(violation);
}
@ -53,11 +51,11 @@ class DependencyDiagnostics implements Diagnostics {
warning(null, error);
}
public List<DiagnosticsProblem> getProblems() {
public List<Problem> getProblems() {
return problems;
}
public List<DiagnosticsProblem> getSevereProblems() {
public List<Problem> getSevereProblems() {
return severeProblems;
}
}

View File

@ -13,19 +13,16 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.model;
package org.teavm.diagnostics;
import org.teavm.model.CallLocation;
/**
*
* @author Alexey Andreev <konsoletyper@gmail.com>
*/
public interface Diagnostics {
void error(InstructionLocation location, String error);
void error(CallLocation location, String error, Object... params);
void error(String error);
void warning(InstructionLocation location, String error);
void warning(String error);
void warning(CallLocation location, String error, Object... params);
}

View File

@ -13,30 +13,33 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.vm;
package org.teavm.diagnostics;
import org.teavm.model.InstructionLocation;
import java.util.Arrays;
import org.teavm.model.CallLocation;
/**
*
* @author Alexey Andreev <konsoletyper@gmail.com>
*/
public class DiagnosticsProblem {
private DiagnosticsProblemSeverity severity;
private InstructionLocation location;
public class Problem {
private ProblemSeverity severity;
private CallLocation location;
private String text;
private Object[] params;
public DiagnosticsProblem(DiagnosticsProblemSeverity severity, InstructionLocation location, String text) {
public Problem(ProblemSeverity severity, CallLocation location, String text, Object[] params) {
this.severity = severity;
this.location = location;
this.text = text;
this.params = Arrays.copyOf(params, params.length);
}
public DiagnosticsProblemSeverity getSeverity() {
public ProblemSeverity getSeverity() {
return severity;
}
public InstructionLocation getLocation() {
public CallLocation getLocation() {
return location;
}

View File

@ -13,13 +13,13 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.vm;
package org.teavm.diagnostics;
/**
*
* @author Alexey Andreev <konsoletyper@gmail.com>
*/
public enum DiagnosticsProblemSeverity {
public enum ProblemSeverity {
ERROR,
WARNING
}

View File

@ -15,6 +15,7 @@
*/
package org.teavm.javascript;
import org.teavm.diagnostics.Diagnostics;
import org.teavm.model.*;
import org.teavm.model.instructions.InvocationType;
import org.teavm.model.instructions.InvokeInstruction;

View File

@ -0,0 +1,57 @@
/*
* 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.model;
import java.util.Objects;
/**
*
* @author Alexey Andreev
*/
public class CallLocation {
private MethodReference method;
private InstructionLocation sourceLocation;
public CallLocation(MethodReference method, InstructionLocation sourceLocation) {
this.method = method;
this.sourceLocation = sourceLocation;
}
public MethodReference getMethod() {
return method;
}
public InstructionLocation getSourceLocation() {
return sourceLocation;
}
@Override
public int hashCode() {
return Objects.hash(method, sourceLocation);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof CallLocation)) {
return false;
}
CallLocation other = (CallLocation)obj;
return Objects.equals(method, other.method) && Objects.equals(sourceLocation, other.sourceLocation);
}
}

View File

@ -15,6 +15,8 @@
*/
package org.teavm.model;
import org.teavm.diagnostics.Diagnostics;
/**
*

View File

@ -20,6 +20,7 @@ import java.util.Set;
import org.teavm.dependency.ClassDependencyInfo;
import org.teavm.dependency.FieldDependencyInfo;
import org.teavm.dependency.MethodDependencyInfo;
import org.teavm.diagnostics.Problem;
/**
*
@ -32,9 +33,9 @@ public interface Violations {
Set<FieldDependencyInfo> getMissingFields();
List<DiagnosticsProblem> getDiagnosticsProblems();
List<Problem> getDiagnosticsProblems();
List<DiagnosticsProblem> getSevereDiagnosticsProblems();
List<Problem> getSevereDiagnosticsProblems();
boolean hasSevereViolations();

View File

@ -15,6 +15,7 @@
*/
package org.teavm.jso.plugin;
import org.teavm.diagnostics.Diagnostics;
import org.teavm.model.*;
/**

View File

@ -16,6 +16,7 @@
package org.teavm.jso.plugin;
import java.util.*;
import org.teavm.diagnostics.Diagnostics;
import org.teavm.javascript.ni.PreserveOriginalName;
import org.teavm.jso.*;
import org.teavm.model.*;