Merge branch 'idea-plugin'

This commit is contained in:
Alexey Andreev 2016-04-25 22:00:51 +03:00
commit aeca4c2df0
4 changed files with 264 additions and 48 deletions

View File

@ -222,7 +222,7 @@ public class TeaVMTool implements BaseTeaVMTool {
}
public Collection<String> getClasses() {
return vm != null ? vm.getClasses() : Collections.<String>emptyList();
return vm != null ? vm.getClasses() : Collections.emptyList();
}
public Collection<String> getUsedResources() {
@ -361,6 +361,7 @@ public class TeaVMTool implements BaseTeaVMTool {
TeaVMProblemRenderer.describeProblems(vm, log);
}
if (debugInformationGenerated) {
assert debugEmitter != null;
DebugInformation debugInfo = debugEmitter.getDebugInformation();
try (OutputStream debugInfoOut = new FileOutputStream(new File(targetDirectory,
targetFileName + ".teavmdbg"))) {
@ -369,6 +370,7 @@ public class TeaVMTool implements BaseTeaVMTool {
log.info("Debug information successfully written");
}
if (sourceMapsFileGenerated) {
assert debugEmitter != null;
DebugInformation debugInfo = debugEmitter.getDebugInformation();
String sourceMapsFileName = targetFileName + ".map";
writer.append("\n//# sourceMappingURL=").append(sourceMapsFileName);

View File

@ -25,13 +25,16 @@ import java.io.Reader;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Queue;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@ -50,6 +53,9 @@ import org.jetbrains.jps.model.module.JpsLibraryDependency;
import org.jetbrains.jps.model.module.JpsModule;
import org.jetbrains.jps.model.module.JpsModuleDependency;
import org.jetbrains.jps.model.module.JpsModuleSourceRoot;
import org.teavm.callgraph.CallGraph;
import org.teavm.callgraph.CallGraphNode;
import org.teavm.callgraph.CallSite;
import org.teavm.common.IntegerArray;
import org.teavm.diagnostics.DefaultProblemTextConsumer;
import org.teavm.diagnostics.Problem;
@ -71,7 +77,7 @@ import org.teavm.vm.TeaVMPhase;
import org.teavm.vm.TeaVMProgressFeedback;
import org.teavm.vm.TeaVMProgressListener;
public class TeaVMBuild {
class TeaVMBuild {
private final CompileContext context;
private final TeaVMStorageProvider storageProvider = new TeaVMStorageProvider();
private final List<String> classPathEntries = new ArrayList<>();
@ -82,12 +88,12 @@ public class TeaVMBuild {
private final Map<File, int[]> fileLineCache = new HashMap<>();
private final List<SourceFileProvider> sourceFileProviders = new ArrayList<>();
public TeaVMBuild(CompileContext context, TeaVMBuilderAssistant assistant) {
TeaVMBuild(CompileContext context, TeaVMBuilderAssistant assistant) {
this.context = context;
this.assistant = assistant;
}
public boolean perform(JpsModule module, ModuleBuildTarget target) throws IOException {
boolean perform(JpsModule module, ModuleBuildTarget target) throws IOException {
storage = context.getProjectDescriptor().dataManager.getStorage(target, storageProvider);
TeaVMJpsConfiguration config = TeaVMJpsConfiguration.get(module);
@ -130,12 +136,13 @@ public class TeaVMBuild {
updateStorage(tool);
}
reportProblems(tool.getProblemProvider());
CallGraph callGraph = tool.getDependencyInfo().getCallGraph();
reportProblems(tool.getProblemProvider(), callGraph);
return true;
}
private void reportProblems(ProblemProvider problemProvider) {
private void reportProblems(ProblemProvider problemProvider, CallGraph callGraph) {
for (Problem problem : problemProvider.getProblems()) {
BuildMessage.Kind kind;
switch (problem.getSeverity()) {
@ -149,47 +156,119 @@ public class TeaVMBuild {
continue;
}
String path = null;
File file = null;
int line = -1;
long startOffset = -1;
long endOffset = -1;
if (problem.getLocation() != null) {
CallLocation callLocation = problem.getLocation();
InstructionLocation insnLocation = problem.getLocation().getSourceLocation();
if (insnLocation != null) {
path = insnLocation.getFileName();
line = insnLocation.getLine();
}
if (line <= 0 && assistant != null && callLocation != null && callLocation.getMethod() != null) {
MethodReference method = callLocation.getMethod();
try {
TeaVMElementLocation location = assistant.getMethodLocation(method.getClassName(),
method.getName(), ValueType.methodTypeToString(method.getSignature()));
line = location.getLine();
startOffset = location.getStartOffset();
endOffset = location.getEndOffset();
file = new File(location.getPath());
if (line <= 0) {
int[] lines = getLineOffsets(file);
if (lines != null) {
line = Arrays.binarySearch(lines, (int) startOffset + 1);
if (line < 0) {
line = -line - 1;
}
}
}
} catch (Exception e) {
// just don't fill location fields
}
}
}
DefaultProblemTextConsumer textConsumer = new DefaultProblemTextConsumer();
problem.render(textConsumer);
String baseText = textConsumer.getText();
List<ProblemToReport> problemsToReport = resolveProblemLocation(problem, callGraph);
for (ProblemToReport problemToReport : problemsToReport) {
String text = baseText + buildCallStack(problemToReport.locations);
context.processMessage(new CompilerMessage("TeaVM", kind, text, problemToReport.path,
problemToReport.startOffset, problemToReport.endOffset, problemToReport.startOffset,
problemToReport.line, 0));
}
}
}
private class ProblemToReport {
int line;
int startOffset;
int endOffset;
String path;
CallLocationList locations;
}
private class CallLocationList {
final CallLocation value;
final CallLocationList next;
private CallLocationList(CallLocation value, CallLocationList next) {
this.value = value;
this.next = next;
}
}
private List<ProblemToReport> resolveProblemLocation(Problem problem, CallGraph callGraph) {
class Step {
private final CallLocationList locationList;
private final CallLocation location;
private Step(CallLocationList locationList, CallLocation location) {
this.locationList = locationList;
this.location = location;
}
}
List<ProblemToReport> problemsToReport = new ArrayList<>();
Set<MethodReference> visited = new HashSet<>();
Queue<Step> workList = new ArrayDeque<>();
workList.add(new Step(null, problem.getLocation()));
while (!workList.isEmpty()) {
Step step = workList.remove();
if (step.location == null || !visited.add(step.location.getMethod())) {
continue;
}
ProblemToReport result = getProblemLocation(step.location);
CallGraphNode node = callGraph.getNode(step.location.getMethod());
if (node.getCallerCallSites().isEmpty() || isValid(result)) {
result.locations = step.locationList;
problemsToReport.add(result);
} else {
for (CallSite callSite : node.getCallerCallSites()) {
CallLocation nextLocation = new CallLocation(callSite.getCaller().getMethod(),
callSite.getLocation());
workList.add(new Step(new CallLocationList(step.location, step.locationList), nextLocation));
}
}
}
return problemsToReport;
}
private boolean isValid(ProblemToReport problemToReport) {
return problemToReport.path != null && (problemToReport.line >= 0 || problemToReport.startOffset >= 0);
}
private ProblemToReport getProblemLocation(CallLocation callLocation) {
String path = null;
File file = null;
int line = -1;
int startOffset = -1;
int endOffset = -1;
if (callLocation != null) {
InstructionLocation insnLocation = callLocation.getSourceLocation();
if (insnLocation != null) {
path = insnLocation.getFileName();
line = insnLocation.getLine();
}
if (line <= 0 && assistant != null && callLocation.getMethod() != null) {
MethodReference method = callLocation.getMethod();
try {
TeaVMElementLocation location = assistant.getMethodLocation(method.getClassName(),
method.getName(), ValueType.methodTypeToString(method.getSignature()));
line = location.getLine();
startOffset = location.getStartOffset();
endOffset = location.getEndOffset();
file = new File(location.getPath());
if (line <= 0) {
int[] lines = getLineOffsets(file);
if (lines != null) {
line = Arrays.binarySearch(lines, startOffset + 1);
if (line < 0) {
line = -line - 1;
}
}
}
} catch (Exception e) {
// just don't fill location fields
}
}
if (file == null) {
if (path != null) {
@ -207,10 +286,32 @@ public class TeaVMBuild {
endOffset = lines[line] - 1;
}
}
context.processMessage(new CompilerMessage("TeaVM", kind, textConsumer.getText(), path,
startOffset, endOffset, startOffset, line, 0));
}
ProblemToReport result = new ProblemToReport();
result.line = line;
result.startOffset = startOffset;
result.endOffset = endOffset;
result.path = path;
return result;
}
private String buildCallStack(CallLocationList callLocationList) {
List<CallLocation> locations = new ArrayList<>();
while (callLocationList != null) {
locations.add(callLocationList.value);
callLocationList = callLocationList.next;
}
Collections.reverse(locations);
StringBuilder sb = new StringBuilder();
for (CallLocation location : locations) {
sb.append("\n at ").append(location.getMethod());
if (location.getSourceLocation() != null) {
sb.append("(").append(location.getSourceLocation()).append(")");
}
}
return sb.toString();
}
private File lookupSource(String relativePath) {

View File

@ -0,0 +1,107 @@
/*
* Copyright 2016 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.idea.maven;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.externalSystem.service.project.IdeModifiableModelsProvider;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleServiceManager;
import java.util.List;
import java.util.Map;
import org.jdom.Element;
import org.jetbrains.idea.maven.importing.MavenImporter;
import org.jetbrains.idea.maven.importing.MavenRootModelAdapter;
import org.jetbrains.idea.maven.model.MavenPlugin;
import org.jetbrains.idea.maven.project.MavenProject;
import org.jetbrains.idea.maven.project.MavenProjectChanges;
import org.jetbrains.idea.maven.project.MavenProjectsProcessorTask;
import org.jetbrains.idea.maven.project.MavenProjectsTree;
import org.teavm.idea.TeaVMConfigurationStorage;
import org.teavm.idea.jps.model.TeaVMJpsConfiguration;
public class TeaVMMavenImporter extends MavenImporter {
private static final Logger logger = Logger.getInstance(TeaVMMavenImporter.class);
public TeaVMMavenImporter() {
super("org.teavm", "teavm-maven-plugin");
}
@Override
public void preProcess(Module module, MavenProject mavenProject, MavenProjectChanges changes,
IdeModifiableModelsProvider modifiableModelsProvider) {
}
@Override
public void process(IdeModifiableModelsProvider modifiableModelsProvider, Module module,
MavenRootModelAdapter rootModel, MavenProjectsTree mavenModel, MavenProject mavenProject,
MavenProjectChanges changes, Map<MavenProject, String> mavenProjectToModuleName,
List<MavenProjectsProcessorTask> postTasks) {
TeaVMConfigurationStorage configurationStorage = ModuleServiceManager.getService(module,
TeaVMConfigurationStorage.class);
if (configurationStorage == null) {
logger.warn("Could not load component to retrieve TeaVM build configuration");
return;
}
TeaVMJpsConfiguration configuration = configurationStorage.getState();
for (MavenPlugin mavenPlugin : mavenProject.getPlugins()) {
if (mavenPlugin.getGroupId().equals(myPluginGroupID)
&& mavenPlugin.getArtifactId().equals(myPluginArtifactID)) {
updateConfiguration(mavenPlugin, configuration);
}
}
configurationStorage.loadState(configuration);
}
private void updateConfiguration(MavenPlugin plugin, TeaVMJpsConfiguration configuration) {
if (plugin.getConfigurationElement() != null) {
updateConfiguration(plugin.getConfigurationElement(), configuration);
}
for (MavenPlugin.Execution execution : plugin.getExecutions()) {
if (execution.getGoals().contains("compile")) {
if (execution.getConfigurationElement() != null) {
updateConfiguration(execution.getConfigurationElement(), configuration);
}
break;
}
}
}
private void updateConfiguration(Element source, TeaVMJpsConfiguration configuration) {
configuration.setEnabled(true);
for (Element child : source.getChildren()) {
switch (child.getName()) {
case "sourceFilesCopied":
configuration.setSourceFilesCopied(Boolean.parseBoolean(child.getTextTrim()));
break;
case "sourceMapsGenerated":
configuration.setSourceMapsFileGenerated(Boolean.parseBoolean(child.getTextTrim()));
break;
case "minifying":
configuration.setMinifying(Boolean.parseBoolean(child.getTextTrim()));
break;
case "targetDirectory":
configuration.setTargetDirectory(child.getTextTrim());
break;
case "mainClass":
configuration.setMainClass(child.getTextTrim());
break;
}
}
}
}

View File

@ -4,6 +4,8 @@
<version>1.0</version>
<vendor email="info@teavm.org" url="http://teavm.org">TeaVM community</vendor>
<depends>org.jetbrains.idea.maven</depends>
<description><![CDATA[
Enter short description for your plugin here.<br>
<em>most HTML tags may be used</em>
@ -30,4 +32,8 @@
<compileServer.plugin classpath="jps/teavm-jps-plugin.jar;teavm-all.jar"/>
<buildProcess.parametersProvider implementation="org.teavm.idea.TeaVMJPSConfigurator"/>
</extensions>
<extensions defaultExtensionNs="org.jetbrains.idea.maven">
<importer implementation="org.teavm.idea.maven.TeaVMMavenImporter"/>
</extensions>
</idea-plugin>