From 81888784886a7ec9aec4f738ff1feb4b0bf19d18 Mon Sep 17 00:00:00 2001 From: alexey-andreev Date: Sun, 18 Jan 2015 19:06:21 +0400 Subject: [PATCH] Add support of new diagnostics API in Eclipse --- .../java/org/teavm/tooling/TeaVMTool.java | 5 + .../META-INF/MANIFEST.MF | 2 + teavm-eclipse/teavm-eclipse-plugin/plugin.xml | 3 +- .../org/teavm/eclipse/TeaVMEclipsePlugin.java | 3 +- .../teavm/eclipse/TeaVMProjectBuilder.java | 253 +++++++++++++----- 5 files changed, 192 insertions(+), 74 deletions(-) diff --git a/teavm-core/src/main/java/org/teavm/tooling/TeaVMTool.java b/teavm-core/src/main/java/org/teavm/tooling/TeaVMTool.java index ba71cd4ba..7e9b1cd9b 100644 --- a/teavm-core/src/main/java/org/teavm/tooling/TeaVMTool.java +++ b/teavm-core/src/main/java/org/teavm/tooling/TeaVMTool.java @@ -24,6 +24,7 @@ import org.teavm.cache.DiskRegularMethodNodeCache; import org.teavm.cache.FileSymbolTable; import org.teavm.debugging.information.DebugInformation; import org.teavm.debugging.information.DebugInformationBuilder; +import org.teavm.dependency.DependencyInfo; import org.teavm.diagnostics.ProblemProvider; import org.teavm.javascript.RenderingContext; import org.teavm.model.*; @@ -204,6 +205,10 @@ public class TeaVMTool { return vm != null ? vm.getProblemProvider() : null; } + public DependencyInfo getDependencyInfo() { + return vm.getDependencyInfo(); + } + public Collection getClasses() { return vm != null ? vm.getClasses() : Collections.emptyList(); } diff --git a/teavm-eclipse/teavm-eclipse-core-plugin/META-INF/MANIFEST.MF b/teavm-eclipse/teavm-eclipse-core-plugin/META-INF/MANIFEST.MF index ae109742e..d6c984c16 100644 --- a/teavm-eclipse/teavm-eclipse-core-plugin/META-INF/MANIFEST.MF +++ b/teavm-eclipse/teavm-eclipse-core-plugin/META-INF/MANIFEST.MF @@ -46,6 +46,7 @@ Bundle-ClassPath: ., lib/websocket-servlet-9.2.1.v20140609.jar Bundle-ActivationPolicy: lazy Export-Package: org.teavm.cache, + org.teavm.callgraph, org.teavm.chromerdp, org.teavm.chromerdp.data, org.teavm.chromerdp.messages, @@ -55,6 +56,7 @@ Export-Package: org.teavm.cache, org.teavm.debugging.information, org.teavm.debugging.javascript, org.teavm.dependency, + org.teavm.diagnostics, org.teavm.javascript, org.teavm.javascript.ast, org.teavm.javascript.ni, diff --git a/teavm-eclipse/teavm-eclipse-plugin/plugin.xml b/teavm-eclipse/teavm-eclipse-plugin/plugin.xml index 0f9a59ca3..0f8f33439 100644 --- a/teavm-eclipse/teavm-eclipse-plugin/plugin.xml +++ b/teavm-eclipse/teavm-eclipse-plugin/plugin.xml @@ -83,12 +83,13 @@ - + + diff --git a/teavm-eclipse/teavm-eclipse-plugin/src/main/java/org/teavm/eclipse/TeaVMEclipsePlugin.java b/teavm-eclipse/teavm-eclipse-plugin/src/main/java/org/teavm/eclipse/TeaVMEclipsePlugin.java index 9e2074a6a..8cab5c03f 100644 --- a/teavm-eclipse/teavm-eclipse-plugin/src/main/java/org/teavm/eclipse/TeaVMEclipsePlugin.java +++ b/teavm-eclipse/teavm-eclipse-plugin/src/main/java/org/teavm/eclipse/TeaVMEclipsePlugin.java @@ -41,7 +41,8 @@ public class TeaVMEclipsePlugin extends AbstractUIPlugin { public static final String NATURE_ID = ID + ".nature"; public static final String BUILDER_ID = ID + ".builder"; public static final String CLASS_DIALOG_ID = ID + ".dialogs.classSelection"; - public static final String DEPENDENCY_MARKER_ID = ID + ".dependencyMarker"; + public static final String PROBLEM_MARKER_ID = ID + ".problemMarker"; + public static final String PROBLEM_MARKER_PROJECT_ATTRIBUTE = ID + ".problemMarker.project"; public static final String CONFIG_MARKER_ID = ID + ".configMarker"; private static TeaVMEclipsePlugin defaultInstance; private ConcurrentMap settingsMap = new ConcurrentHashMap<>(); diff --git a/teavm-eclipse/teavm-eclipse-plugin/src/main/java/org/teavm/eclipse/TeaVMProjectBuilder.java b/teavm-eclipse/teavm-eclipse-plugin/src/main/java/org/teavm/eclipse/TeaVMProjectBuilder.java index b67a1196d..f065e905c 100644 --- a/teavm-eclipse/teavm-eclipse-plugin/src/main/java/org/teavm/eclipse/TeaVMProjectBuilder.java +++ b/teavm-eclipse/teavm-eclipse-plugin/src/main/java/org/teavm/eclipse/TeaVMProjectBuilder.java @@ -22,19 +22,20 @@ import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import org.eclipse.core.resources.*; import org.eclipse.core.runtime.*; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.core.variables.IStringVariableManager; import org.eclipse.core.variables.VariablesPlugin; -import org.eclipse.jdt.core.IClasspathEntry; -import org.eclipse.jdt.core.IJavaProject; -import org.eclipse.jdt.core.JavaCore; -import org.teavm.dependency.*; -import org.teavm.model.ClassHolderTransformer; -import org.teavm.model.InstructionLocation; -import org.teavm.model.MethodReference; -import org.teavm.model.ValueType; +import org.eclipse.jdt.core.*; +import org.teavm.callgraph.CallGraph; +import org.teavm.callgraph.CallGraphNode; +import org.teavm.callgraph.CallSite; +import org.teavm.diagnostics.Problem; +import org.teavm.diagnostics.ProblemTextConsumer; +import org.teavm.model.*; import org.teavm.tooling.*; /** @@ -49,6 +50,7 @@ public class TeaVMProjectBuilder extends IncrementalProjectBuilder { private SourceFileProvider[] sourceProviders; private Set usedProjects = new HashSet<>(); private static Map> profileClasses = new WeakHashMap<>(); + private static Pattern newLinePattern = Pattern.compile("\\r|\\n|\\r\\n"); @Override protected IProject[] build(int kind, Map args, IProgressMonitor monitor) throws CoreException { @@ -125,9 +127,8 @@ public class TeaVMProjectBuilder extends IncrementalProjectBuilder { try { monitor.beginTask("Running TeaVM", 10000); tool.generate(); - if (tool.getDependencyViolations().hasMissingItems()) { - putMarkers(tool.getDependencyViolations()); - } else if (!tool.wasCancelled()) { + if (!tool.wasCancelled()) { + putMarkers(tool.getDependencyInfo().getCallGraph(), tool.getProblemProvider().getProblems()); setClasses(profile, classesToResources(tool.getClasses())); refreshTarget(tool.getTargetDirectory()); } @@ -225,41 +226,183 @@ public class TeaVMProjectBuilder extends IncrementalProjectBuilder { } private void removeMarkers() throws CoreException { - getProject().deleteMarkers(TeaVMEclipsePlugin.DEPENDENCY_MARKER_ID, true, IResource.DEPTH_INFINITE); + for (IProject project : getProject().getWorkspace().getRoot().getProjects()) { + IMarker[] markers = project.findMarkers(TeaVMEclipsePlugin.PROBLEM_MARKER_ID, true, + IResource.DEPTH_INFINITE); + for (IMarker marker : markers) { + String projectName = (String)marker.getAttribute(TeaVMEclipsePlugin.PROBLEM_MARKER_PROJECT_ATTRIBUTE); + if (projectName.equals(getProject().getName())) { + marker.delete(); + } + } + } getProject().deleteMarkers(TeaVMEclipsePlugin.CONFIG_MARKER_ID, true, IResource.DEPTH_INFINITE); } - private void putMarkers(DependencyViolations violations) throws CoreException { - for (ClassDependencyInfo dep : violations.getMissingClasses()) { - putMarker("Missing class " + getSimpleClassName(dep.getClassName()), dep.getStack()); - } - for (FieldDependencyInfo dep : violations.getMissingFields()) { - putMarker("Missing field " + getSimpleClassName(dep.getReference().getClassName()) + "." + - dep.getReference().getFieldName(), dep.getStack()); - } - for (MethodDependencyInfo dep : violations.getMissingMethods()) { - putMarker("Missing method " + getFullMethodName(dep.getReference()), dep.getStack()); + private void putMarkers(CallGraph cg, List problems) throws CoreException { + for (Problem problem : problems) { + putMarker(cg, problem); } } - private void putMarker(String message, DependencyStack stack) throws CoreException { - StringBuilder sb = new StringBuilder(); - sb.append(message); + private void putMarker(CallGraph cg, Problem problem) throws CoreException { + if (problem.getLocation() == null || problem.getLocation().getMethod() == null) { + putMarkerAtDefaultLocation(problem); + return; + } + CallGraphNode problemNode = cg.getNode(problem.getLocation().getMethod()); + if (problemNode == null) { + putMarkerAtDefaultLocation(problem); + return; + } + Set callSites = new HashSet<>(); + collectCallSites(callSites, problemNode); + String messagePrefix = problemAsString(problem); boolean wasPut = false; - while (stack != DependencyStack.ROOT) { - wasPut |= putMarker(sb.toString(), stack.getLocation(), stack.getMethod()); - if (stack.getMethod() != null) { - sb.append(", used by ").append(getFullMethodName(stack.getMethod())); + for (CallSite callSite : callSites) { + IResource resource = findResource(new CallLocation(callSite.getCaller().getMethod(), + callSite.getLocation())); + if (resource == null) { + continue; } - stack = stack.getCause(); + wasPut = true; + CallGraphNode node = problemNode; + StringBuilder sb = new StringBuilder(messagePrefix); + while (node != callSite.getCaller()) { + sb.append(", used by ").append(getFullMethodName(node.getMethod())); + Iterator callerCallSites = node.getCallerCallSites().iterator(); + if (!callerCallSites.hasNext()) { + break; + } + node = callerCallSites.next().getCaller(); + } + putMarker(resource, callSite.getLocation(), callSite.getCaller().getMethod(), sb.toString()); + } + IResource resource = findResource(problem.getLocation()); + if (resource != null) { + putMarker(resource, problem.getLocation().getSourceLocation(), problem.getLocation().getMethod(), + messagePrefix); + wasPut = true; } if (!wasPut) { - IMarker marker = getProject().createMarker(TeaVMEclipsePlugin.DEPENDENCY_MARKER_ID); - marker.setAttribute(IMarker.SEVERITY, IMarker.SEVERITY_ERROR); - marker.setAttribute(IMarker.MESSAGE, message); + putMarkerAtDefaultLocation(problem); } } + private void putMarker(IResource resource, InstructionLocation location, MethodReference method, + String text) throws CoreException { + IMarker marker = resource.createMarker(TeaVMEclipsePlugin.PROBLEM_MARKER_ID); + marker.setAttribute(IMarker.SEVERITY, IMarker.SEVERITY_ERROR); + marker.setAttribute(IMarker.MESSAGE, text); + marker.setAttribute(TeaVMEclipsePlugin.PROBLEM_MARKER_PROJECT_ATTRIBUTE, getProject().getName()); + Integer lineNumber = location != null ? location.getLine() : null; + if (lineNumber == null) { + lineNumber = findMethodLocation(method, resource); + } + if (lineNumber == null) { + lineNumber = 1; + } + marker.setAttribute(IMarker.LINE_NUMBER, lineNumber); + } + + private IResource findResource(CallLocation location) { + IResource resource = null; + if (location.getSourceLocation() != null) { + String resourceName = location.getSourceLocation().getFileName(); + for (IContainer container : sourceContainers) { + resource = container.findMember(resourceName); + if (resource != null) { + break; + } + } + } + if (resource == null) { + String resourceName = location.getMethod().getClassName().replace('.', '/') + ".java"; + for (IContainer container : sourceContainers) { + resource = container.findMember(resourceName); + if (resource != null) { + break; + } + } + } + return resource; + } + + private void collectCallSites(Set callSites, CallGraphNode node) { + for (CallSite callSite : node.getCallerCallSites()) { + if (callSites.add(callSite)) { + collectCallSites(callSites, callSite.getCaller()); + } + } + } + + private void putMarkerAtDefaultLocation(Problem problem) throws CoreException { + IMarker marker = getProject().createMarker(TeaVMEclipsePlugin.PROBLEM_MARKER_ID); + marker.setAttribute(IMarker.SEVERITY, IMarker.SEVERITY_ERROR); + marker.setAttribute(IMarker.MESSAGE, problemAsString(problem)); + marker.setAttribute(TeaVMEclipsePlugin.PROBLEM_MARKER_PROJECT_ATTRIBUTE, getProject().getName()); + } + + private Integer findMethodLocation(MethodReference methodRef, IResource resource) throws CoreException { + if (resource.getType() != IResource.FILE) { + return null; + } + IJavaElement rootElement = JavaCore.createCompilationUnitFrom((IFile)resource); + if (rootElement.getElementType() != IJavaElement.COMPILATION_UNIT) { + return null; + } + ICompilationUnit unit = (ICompilationUnit)rootElement; + IType type = unit.getType(getSimpleClassName(methodRef.getClassName())); + if (type == null) { + return null; + } + for (IMethod method : type.getMethods()) { + StringBuilder sb = new StringBuilder(); + sb.append('('); + for (String paramType : method.getParameterTypes()) { + sb.append(Signature.getTypeErasure(paramType)); + } + sb.append(')').append(Signature.getTypeErasure(method.getReturnType())); + if (sb.toString().equals(methodRef.toString())) { + return getLineNumber(method); + } + } + return null; + } + + private int getLineNumber(IMethod method) throws CoreException { + int offset = method.getSourceRange().getOffset(); + Matcher matcher = newLinePattern.matcher(method.getCompilationUnit().getSource()); + int lineNumber = 1; + while (matcher.find() && matcher.start() < offset) { + ++lineNumber; + } + return lineNumber; + } + + private String problemAsString(Problem problem) { + final StringBuilder sb = new StringBuilder(); + problem.render(new ProblemTextConsumer() { + @Override public void appendMethod(MethodReference method) { + sb.append(getFullMethodName(method)); + } + @Override public void appendLocation(InstructionLocation location) { + sb.append(location); + } + @Override public void appendField(FieldReference field) { + sb.append(getFullFieldName(field)); + } + @Override public void appendClass(String cls) { + sb.append(getSimpleClassName(cls)); + } + @Override public void append(String text) { + sb.append(text); + } + }); + return sb.toString(); + } + + private String getFullMethodName(MethodReference methodRef) { StringBuilder sb = new StringBuilder(); sb.append(getSimpleClassName(methodRef.getClassName())).append('.').append(methodRef.getName()).append('('); @@ -273,6 +416,10 @@ public class TeaVMProjectBuilder extends IncrementalProjectBuilder { return sb.toString(); } + private String getFullFieldName(FieldReference fieldRef) { + return getSimpleClassName(fieldRef.getClassName()) + "." + fieldRef.getFieldName(); + } + private String getTypeName(ValueType type) { int arrayDim = 0; while (type instanceof ValueType.Array) { @@ -322,42 +469,6 @@ public class TeaVMProjectBuilder extends IncrementalProjectBuilder { return className.substring(index + 1); } - private boolean putMarker(String message, InstructionLocation location, MethodReference methodRef) - throws CoreException { - IResource resource = null; - if (location != null) { - String resourceName = location.getFileName(); - for (IContainer container : sourceContainers) { - resource = container.findMember(resourceName); - if (resource != null) { - break; - } - } - } - if (resource == null) { - String resourceName = methodRef.getClassName().replace('.', '/') + ".java"; - for (IContainer container : sourceContainers) { - resource = container.findMember(resourceName); - if (resource != null) { - break; - } - } - } - if (resource != null) { - IMarker marker = resource.createMarker(TeaVMEclipsePlugin.DEPENDENCY_MARKER_ID); - marker.setAttribute(IMarker.SEVERITY, IMarker.SEVERITY_ERROR); - marker.setAttribute(IMarker.MESSAGE, message); - if (location != null) { - marker.setAttribute(IMarker.LINE_NUMBER, location.getLine()); - } else { - marker.setAttribute(IMarker.LINE_NUMBER, 1); - } - return true; - } else { - return false; - } - } - private TeaVMProjectSettings getProjectSettings() { return TeaVMEclipsePlugin.getDefault().getSettings(getProject()); } @@ -485,9 +596,7 @@ public class TeaVMProjectBuilder extends IncrementalProjectBuilder { } IContainer srcContainer = (IContainer)workspaceRoot.findMember(entry.getPath()); if (srcContainer != null) { - if (srcContainer.getProject() == project) { - srcCollector.addContainer(srcContainer); - } + srcCollector.addContainer(srcContainer); sourceFileCollector.addFile(srcContainer.getLocation()); } break;