From da8382271f3f4ff64ad897fd69132361f4c50214 Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Fri, 15 Apr 2016 00:21:24 +0300 Subject: [PATCH 1/5] JPS plugin runs only when one of significant classes change --- .../java/org/teavm/idea/jps/TeaVMBuild.java | 269 ++++++++++++++++++ .../java/org/teavm/idea/jps/TeaVMBuilder.java | 178 +----------- .../java/org/teavm/idea/jps/TeaVMStorage.java | 122 ++++++++ .../teavm/idea/jps/TeaVMStorageProvider.java | 29 ++ tools/idea/jps-plugin/teavm-jps-plugin.iml | 2 +- tools/idea/teavm-idea-plugin.iml | 2 +- 6 files changed, 427 insertions(+), 175 deletions(-) create mode 100644 tools/idea/jps-plugin/src/main/java/org/teavm/idea/jps/TeaVMBuild.java create mode 100644 tools/idea/jps-plugin/src/main/java/org/teavm/idea/jps/TeaVMStorage.java create mode 100644 tools/idea/jps-plugin/src/main/java/org/teavm/idea/jps/TeaVMStorageProvider.java diff --git a/tools/idea/jps-plugin/src/main/java/org/teavm/idea/jps/TeaVMBuild.java b/tools/idea/jps-plugin/src/main/java/org/teavm/idea/jps/TeaVMBuild.java new file mode 100644 index 000000000..92ef91ff2 --- /dev/null +++ b/tools/idea/jps-plugin/src/main/java/org/teavm/idea/jps/TeaVMBuild.java @@ -0,0 +1,269 @@ +/* + * 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.jps; + +import static java.util.stream.Collectors.toList; +import static java.util.stream.Collectors.toSet; +import java.io.File; +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Objects; +import java.util.Set; +import java.util.stream.Stream; +import org.jetbrains.jps.incremental.CompileContext; +import org.jetbrains.jps.incremental.ModuleBuildTarget; +import org.jetbrains.jps.incremental.messages.BuildMessage; +import org.jetbrains.jps.incremental.messages.CompilerMessage; +import org.jetbrains.jps.incremental.messages.ProgressMessage; +import org.jetbrains.jps.model.java.JpsJavaExtensionService; +import org.jetbrains.jps.model.library.JpsLibrary; +import org.jetbrains.jps.model.library.JpsOrderRootType; +import org.jetbrains.jps.model.module.JpsDependencyElement; +import org.jetbrains.jps.model.module.JpsLibraryDependency; +import org.jetbrains.jps.model.module.JpsModule; +import org.jetbrains.jps.model.module.JpsModuleDependency; +import org.teavm.idea.jps.model.TeaVMJpsConfiguration; +import org.teavm.tooling.TeaVMTool; +import org.teavm.tooling.TeaVMToolException; +import org.teavm.tooling.TeaVMToolLog; +import org.teavm.vm.TeaVMPhase; +import org.teavm.vm.TeaVMProgressFeedback; +import org.teavm.vm.TeaVMProgressListener; + +public class TeaVMBuild { + private CompileContext context; + private TeaVMStorageProvider storageProvider = new TeaVMStorageProvider(); + private List classPathEntries = new ArrayList<>(); + private List directoryClassPathEntries; + private TeaVMStorage storage; + + public TeaVMBuild(CompileContext context) { + this.context = context; + } + + public boolean perform(JpsModule module, ModuleBuildTarget target) throws IOException { + storage = context.getProjectDescriptor().dataManager.getStorage(target, storageProvider); + + TeaVMJpsConfiguration config = TeaVMJpsConfiguration.get(module); + if (config == null || !config.isEnabled()) { + return false; + } + + classPathEntries.clear(); + buildClassPath(module, new HashSet<>()); + directoryClassPathEntries = classPathEntries.stream().filter(name -> new File(name).isDirectory()) + .collect(toList()); + + if (!hasChanges(target)) { + return false; + } + + TeaVMTool tool = new TeaVMTool(); + tool.setProgressListener(createProgressListener(context)); + tool.setLog(createLog(context)); + tool.setMainClass(config.getMainClass()); + tool.setSourceMapsFileGenerated(true); + tool.setTargetDirectory(new File(config.getTargetDirectory())); + tool.setClassLoader(buildClassLoader()); + tool.setMinifying(false); + + boolean errorOccurred = false; + try { + tool.generate(); + } catch (TeaVMToolException | RuntimeException | Error e) { + e.printStackTrace(System.err); + context.processMessage(new CompilerMessage("TeaVM", e)); + errorOccurred = true; + } + + if (!errorOccurred && tool.getProblemProvider().getSevereProblems().isEmpty()) { + updateStorage(tool); + } + + return true; + } + + private boolean hasChanges(ModuleBuildTarget target) { + if (!context.getScope().isBuildIncrementally(target.getTargetType()) + || context.getScope().isBuildForced(target)) { + return true; + } + List filesToWatch = storage.getParticipatingFiles(); + if (filesToWatch == null) { + return true; + } + + for (TeaVMStorage.Entry fileToWatch : filesToWatch) { + Long actualTimestamp = getTimestamp(fileToWatch.path); + if (actualTimestamp == null || actualTimestamp > fileToWatch.timestamp) { + return true; + } + } + return false; + } + + private void updateStorage(TeaVMTool tool) { + Set resources = Stream.concat(tool.getClasses().stream().map(cls -> cls.replace('.', '/') + ".class"), + tool.getUsedResources().stream()) + .sorted() + .collect(toSet()); + List participatingFiles = resources.stream() + .map(path -> { + Long timestamp = getTimestamp(path); + return timestamp != null ? new TeaVMStorage.Entry(path, timestamp) : null; + }) + .filter(Objects::nonNull) + .collect(toList()); + storage.setParticipatingFiles(participatingFiles); + } + + private Long getTimestamp(String path) { + for (String classPathEntry : directoryClassPathEntries) { + File file = new File(classPathEntry, path); + if (file.exists()) { + return file.lastModified(); + } + } + return null; + } + + private TeaVMToolLog createLog(CompileContext context) { + return new TeaVMToolLog() { + @Override + public void info(String text) { + context.processMessage(new CompilerMessage("TeaVM", BuildMessage.Kind.INFO, text)); + } + + @Override + public void debug(String text) { + context.processMessage(new CompilerMessage("TeaVM", BuildMessage.Kind.INFO, text)); + } + + @Override + public void warning(String text) { + context.processMessage(new CompilerMessage("TeaVM", BuildMessage.Kind.WARNING, text)); + } + + @Override + public void error(String text) { + context.processMessage(new CompilerMessage("TeaVM", BuildMessage.Kind.ERROR, text)); + } + + @Override + public void info(String text, Throwable e) { + context.processMessage(new CompilerMessage("TeaVM", BuildMessage.Kind.INFO, text + "\n" + + CompilerMessage.getTextFromThrowable(e))); + } + + @Override + public void debug(String text, Throwable e) { + context.processMessage(new CompilerMessage("TeaVM", BuildMessage.Kind.INFO, text + "\n" + + CompilerMessage.getTextFromThrowable(e))); + } + + @Override + public void warning(String text, Throwable e) { + context.processMessage(new CompilerMessage("TeaVM", BuildMessage.Kind.WARNING, text + "\n" + + CompilerMessage.getTextFromThrowable(e))); + } + + @Override + public void error(String text, Throwable e) { + context.processMessage(new CompilerMessage("TeaVM", BuildMessage.Kind.ERROR, text + "\n" + + CompilerMessage.getTextFromThrowable(e))); + } + }; + } + + private TeaVMProgressListener createProgressListener(CompileContext context) { + return new TeaVMProgressListener() { + private TeaVMPhase currentPhase; + int expectedCount; + + @Override + public TeaVMProgressFeedback phaseStarted(TeaVMPhase phase, int count) { + expectedCount = count; + context.processMessage(new ProgressMessage(phaseName(phase), 0)); + currentPhase = phase; + return context.getCancelStatus().isCanceled() ? TeaVMProgressFeedback.CANCEL + : TeaVMProgressFeedback.CONTINUE; + } + + @Override + public TeaVMProgressFeedback progressReached(int progress) { + context.processMessage(new ProgressMessage(phaseName(currentPhase), (float) progress / expectedCount)); + return context.getCancelStatus().isCanceled() ? TeaVMProgressFeedback.CANCEL + : TeaVMProgressFeedback.CONTINUE; + } + }; + } + + private static String phaseName(TeaVMPhase phase) { + switch (phase) { + case DEPENDENCY_CHECKING: + return "Discovering classes to compile"; + case LINKING: + return "Resolving method invocations"; + case DEVIRTUALIZATION: + return "Eliminating virtual calls"; + case DECOMPILATION: + return "Compiling classes"; + case RENDERING: + return "Building JS file"; + default: + throw new AssertionError(); + } + } + + private ClassLoader buildClassLoader() { + URL[] urls = classPathEntries.stream().map(entry -> { + try { + return new File(entry).toURI().toURL(); + } catch (MalformedURLException e) { + throw new RuntimeException(entry); + } + }).toArray(URL[]::new); + + return new URLClassLoader(urls, TeaVMBuilder.class.getClassLoader()); + } + + private void buildClassPath(JpsModule module, Set visited) { + if (!visited.add(module)) { + return; + } + File output = JpsJavaExtensionService.getInstance().getOutputDirectory(module, false); + if (output != null) { + classPathEntries.add(output.getPath()); + } + for (JpsDependencyElement dependency : module.getDependenciesList().getDependencies()) { + if (dependency instanceof JpsModuleDependency) { + buildClassPath(((JpsModuleDependency) dependency).getModule(), visited); + } else if (dependency instanceof JpsLibraryDependency) { + JpsLibrary library = ((JpsLibraryDependency) dependency).getLibrary(); + if (library == null) { + continue; + } + classPathEntries.addAll(library.getFiles(JpsOrderRootType.COMPILED).stream().map(File::getPath) + .collect(toList())); + } + } + } +} diff --git a/tools/idea/jps-plugin/src/main/java/org/teavm/idea/jps/TeaVMBuilder.java b/tools/idea/jps-plugin/src/main/java/org/teavm/idea/jps/TeaVMBuilder.java index c93b008cb..47c86abaa 100644 --- a/tools/idea/jps-plugin/src/main/java/org/teavm/idea/jps/TeaVMBuilder.java +++ b/tools/idea/jps-plugin/src/main/java/org/teavm/idea/jps/TeaVMBuilder.java @@ -15,14 +15,7 @@ */ package org.teavm.idea.jps; -import java.io.File; import java.io.IOException; -import java.net.MalformedURLException; -import java.net.URL; -import java.net.URLClassLoader; -import java.util.HashSet; -import java.util.Set; -import java.util.stream.Collectors; import org.jetbrains.annotations.NotNull; import org.jetbrains.jps.ModuleChunk; import org.jetbrains.jps.builders.DirtyFilesHolder; @@ -32,25 +25,11 @@ import org.jetbrains.jps.incremental.CompileContext; import org.jetbrains.jps.incremental.ModuleBuildTarget; import org.jetbrains.jps.incremental.ModuleLevelBuilder; import org.jetbrains.jps.incremental.ProjectBuildException; -import org.jetbrains.jps.incremental.messages.BuildMessage; -import org.jetbrains.jps.incremental.messages.CompilerMessage; -import org.jetbrains.jps.incremental.messages.ProgressMessage; -import org.jetbrains.jps.model.java.JpsJavaExtensionService; -import org.jetbrains.jps.model.library.JpsLibrary; -import org.jetbrains.jps.model.library.JpsOrderRootType; -import org.jetbrains.jps.model.module.JpsDependencyElement; -import org.jetbrains.jps.model.module.JpsLibraryDependency; import org.jetbrains.jps.model.module.JpsModule; -import org.jetbrains.jps.model.module.JpsModuleDependency; -import org.teavm.idea.jps.model.TeaVMJpsConfiguration; -import org.teavm.tooling.TeaVMTool; -import org.teavm.tooling.TeaVMToolException; -import org.teavm.tooling.TeaVMToolLog; -import org.teavm.vm.TeaVMPhase; -import org.teavm.vm.TeaVMProgressFeedback; -import org.teavm.vm.TeaVMProgressListener; public class TeaVMBuilder extends ModuleLevelBuilder { + private TeaVMStorageProvider storageProvider = new TeaVMStorageProvider(); + public TeaVMBuilder() { super(BuilderCategory.CLASS_POST_PROCESSOR); } @@ -60,8 +39,10 @@ public class TeaVMBuilder extends ModuleLevelBuilder { DirtyFilesHolder dirtyFilesHolder, OutputConsumer outputConsumer) throws ProjectBuildException, IOException { boolean doneSomething = false; + + TeaVMBuild build = new TeaVMBuild(context); for (JpsModule module : chunk.getModules()) { - doneSomething |= buildModule(module, context); + doneSomething |= build.perform(module, chunk.representativeTarget()); if (context.getCancelStatus().isCanceled()) { return ExitCode.ABORT; } @@ -70,155 +51,6 @@ public class TeaVMBuilder extends ModuleLevelBuilder { return doneSomething ? ExitCode.OK : ExitCode.NOTHING_DONE; } - private boolean buildModule(JpsModule module, CompileContext context) { - TeaVMJpsConfiguration config = TeaVMJpsConfiguration.get(module); - if (config == null || !config.isEnabled()) { - return false; - } - - TeaVMTool tool = new TeaVMTool(); - tool.setProgressListener(createProgressListener(context)); - tool.setLog(createLog(context)); - tool.setMainClass(config.getMainClass()); - tool.setSourceMapsFileGenerated(true); - tool.setTargetDirectory(new File(config.getTargetDirectory())); - tool.setClassLoader(buildClassLoader(module)); - tool.setMinifying(false); - - try { - tool.generate(); - } catch (TeaVMToolException | RuntimeException | Error e) { - e.printStackTrace(System.err); - context.processMessage(new CompilerMessage("TeaVM", e)); - } - - return true; - } - - private TeaVMToolLog createLog(CompileContext context) { - return new TeaVMToolLog() { - @Override - public void info(String text) { - context.processMessage(new CompilerMessage("TeaVM", BuildMessage.Kind.INFO, text)); - } - - @Override - public void debug(String text) { - context.processMessage(new CompilerMessage("TeaVM", BuildMessage.Kind.INFO, text)); - } - - @Override - public void warning(String text) { - context.processMessage(new CompilerMessage("TeaVM", BuildMessage.Kind.WARNING, text)); - } - - @Override - public void error(String text) { - context.processMessage(new CompilerMessage("TeaVM", BuildMessage.Kind.ERROR, text)); - } - - @Override - public void info(String text, Throwable e) { - context.processMessage(new CompilerMessage("TeaVM", BuildMessage.Kind.INFO, text + "\n" - + CompilerMessage.getTextFromThrowable(e))); - } - - @Override - public void debug(String text, Throwable e) { - context.processMessage(new CompilerMessage("TeaVM", BuildMessage.Kind.INFO, text + "\n" - + CompilerMessage.getTextFromThrowable(e))); - } - - @Override - public void warning(String text, Throwable e) { - context.processMessage(new CompilerMessage("TeaVM", BuildMessage.Kind.WARNING, text + "\n" - + CompilerMessage.getTextFromThrowable(e))); - } - - @Override - public void error(String text, Throwable e) { - context.processMessage(new CompilerMessage("TeaVM", BuildMessage.Kind.ERROR, text + "\n" - + CompilerMessage.getTextFromThrowable(e))); - } - }; - } - - private TeaVMProgressListener createProgressListener(CompileContext context) { - return new TeaVMProgressListener() { - private TeaVMPhase currentPhase; - int expectedCount; - - @Override - public TeaVMProgressFeedback phaseStarted(TeaVMPhase phase, int count) { - expectedCount = count; - context.processMessage(new ProgressMessage(phaseName(phase), 0)); - currentPhase = phase; - return context.getCancelStatus().isCanceled() ? TeaVMProgressFeedback.CANCEL - : TeaVMProgressFeedback.CONTINUE; - } - - @Override - public TeaVMProgressFeedback progressReached(int progress) { - context.processMessage(new ProgressMessage(phaseName(currentPhase), (float) progress / expectedCount)); - return context.getCancelStatus().isCanceled() ? TeaVMProgressFeedback.CANCEL - : TeaVMProgressFeedback.CONTINUE; - } - }; - } - - private static String phaseName(TeaVMPhase phase) { - switch (phase) { - case DEPENDENCY_CHECKING: - return "Discovering classes to compile"; - case LINKING: - return "Resolving method invocations"; - case DEVIRTUALIZATION: - return "Eliminating virtual calls"; - case DECOMPILATION: - return "Compiling classes"; - case RENDERING: - return "Building JS file"; - default: - throw new AssertionError(); - } - } - - private ClassLoader buildClassLoader(JpsModule module) { - Set classPathEntries = new HashSet<>(); - buildClassPath(module, new HashSet<>(), classPathEntries); - - URL[] urls = classPathEntries.stream().map(entry -> { - try { - return new File(entry).toURI().toURL(); - } catch (MalformedURLException e) { - throw new RuntimeException(entry); - } - }).toArray(URL[]::new); - - return new URLClassLoader(urls, TeaVMBuilder.class.getClassLoader()); - } - - private void buildClassPath(JpsModule module, Set visited, Set classPathEntries) { - if (!visited.add(module)) { - return; - } - File output = JpsJavaExtensionService.getInstance().getOutputDirectory(module, false); - if (output != null) { - classPathEntries.add(output.getPath()); - } - for (JpsDependencyElement dependency : module.getDependenciesList().getDependencies()) { - if (dependency instanceof JpsModuleDependency) { - buildClassPath(((JpsModuleDependency) dependency).getModule(), visited, classPathEntries); - } else if (dependency instanceof JpsLibraryDependency) { - JpsLibrary library = ((JpsLibraryDependency) dependency).getLibrary(); - if (library == null) { - continue; - } - classPathEntries.addAll(library.getFiles(JpsOrderRootType.COMPILED).stream().map(File::getPath) - .collect(Collectors.toList())); - } - } - } @NotNull @Override diff --git a/tools/idea/jps-plugin/src/main/java/org/teavm/idea/jps/TeaVMStorage.java b/tools/idea/jps-plugin/src/main/java/org/teavm/idea/jps/TeaVMStorage.java new file mode 100644 index 000000000..65acbffbb --- /dev/null +++ b/tools/idea/jps-plugin/src/main/java/org/teavm/idea/jps/TeaVMStorage.java @@ -0,0 +1,122 @@ +/* + * 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.jps; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.Reader; +import java.io.Writer; +import java.util.ArrayList; +import java.util.List; +import org.jetbrains.jps.incremental.storage.StorageOwner; + +public class TeaVMStorage implements StorageOwner { + private File file; + private List participatingFiles; + private boolean dirty; + + TeaVMStorage(File file) throws IOException { + file = new File(file, "teavm.storage"); + this.file = file; + if (file.exists()) { + participatingFiles = new ArrayList<>(); + try (Reader innerReader = new InputStreamReader(new FileInputStream(file), "UTF-8"); + BufferedReader reader = new BufferedReader(innerReader)) { + while (true) { + String line = reader.readLine(); + if (line == null) { + break; + } + line = line.trim(); + if (line.isEmpty() || line.startsWith("#")) { + continue; + } + + int index = line.lastIndexOf(':'); + if (index < 0) { + participatingFiles = null; + file.delete(); + break; + } + participatingFiles.add(new Entry(line.substring(0, index), + Long.parseLong(line.substring(index + 1)))); + } + } + } + } + + public void setParticipatingFiles(List participatingFiles) { + if (participatingFiles == null) { + this.participatingFiles = null; + } else { + this.participatingFiles = new ArrayList<>(participatingFiles); + } + dirty = true; + } + + public List getParticipatingFiles() { + return participatingFiles != null ? new ArrayList<>(participatingFiles) : null; + } + + @Override + public void flush(boolean b) { + } + + @Override + public void clean() throws IOException { + file.delete(); + participatingFiles = null; + } + + @Override + public void close() throws IOException { + if (dirty) { + if (participatingFiles == null) { + if (file.exists()) { + file.delete(); + } + } else { + try (Writer innerWriter = new OutputStreamWriter(new FileOutputStream(file), "UTF-8"); + BufferedWriter writer = new BufferedWriter(innerWriter)) { + for (Entry participatingFile : participatingFiles) { + writer.append(participatingFile.path + ":" + participatingFile.timestamp); + writer.newLine(); + } + } + } + } + } + + public boolean causesBuild(String file) { + return participatingFiles == null || participatingFiles.contains(file); + } + + public static class Entry { + public final String path; + public final long timestamp; + + public Entry(String path, long timestamp) { + this.path = path; + this.timestamp = timestamp; + } + } +} diff --git a/tools/idea/jps-plugin/src/main/java/org/teavm/idea/jps/TeaVMStorageProvider.java b/tools/idea/jps-plugin/src/main/java/org/teavm/idea/jps/TeaVMStorageProvider.java new file mode 100644 index 000000000..df9848bac --- /dev/null +++ b/tools/idea/jps-plugin/src/main/java/org/teavm/idea/jps/TeaVMStorageProvider.java @@ -0,0 +1,29 @@ +/* + * 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.jps; + +import java.io.File; +import java.io.IOException; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jps.builders.storage.StorageProvider; + +public class TeaVMStorageProvider extends StorageProvider { + @NotNull + @Override + public TeaVMStorage createStorage(File file) throws IOException { + return new TeaVMStorage(file); + } +} diff --git a/tools/idea/jps-plugin/teavm-jps-plugin.iml b/tools/idea/jps-plugin/teavm-jps-plugin.iml index 4aa591fa7..29b643292 100644 --- a/tools/idea/jps-plugin/teavm-jps-plugin.iml +++ b/tools/idea/jps-plugin/teavm-jps-plugin.iml @@ -9,7 +9,7 @@ - + diff --git a/tools/idea/teavm-idea-plugin.iml b/tools/idea/teavm-idea-plugin.iml index 33bdc0471..656bd4099 100644 --- a/tools/idea/teavm-idea-plugin.iml +++ b/tools/idea/teavm-idea-plugin.iml @@ -10,7 +10,7 @@ - + From 93cc51c575dd8a48ebe8847a6718c16e34da633c Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Mon, 18 Apr 2016 23:25:44 +0300 Subject: [PATCH 2/5] Start implementing RMI service that reports method locations to JPS builder --- .../java/org/teavm/idea/jps/TeaVMBuilder.java | 2 - .../model/TeaVMJpsRemoteConfiguration.java | 58 +++++++++ .../jps/remote/TeaVMBuilderAssistant.java | 24 ++++ .../idea/jps/remote/TeaVMElementLocation.java | 48 ++++++++ tools/idea/jps-plugin/teavm-jps-plugin.iml | 2 +- .../teavm/idea/TeaVMConfigurationStorage.java | 2 +- .../org/teavm/idea/TeaVMJPSRemoteService.java | 110 ++++++++++++++++++ .../idea/TeaVMRemoteConfigurationStorage.java | 39 +++++++ tools/idea/teavm-idea-plugin.iml | 2 +- 9 files changed, 282 insertions(+), 5 deletions(-) create mode 100644 tools/idea/jps-plugin/src/main/java/org/teavm/idea/jps/model/TeaVMJpsRemoteConfiguration.java create mode 100644 tools/idea/jps-plugin/src/main/java/org/teavm/idea/jps/remote/TeaVMBuilderAssistant.java create mode 100644 tools/idea/jps-plugin/src/main/java/org/teavm/idea/jps/remote/TeaVMElementLocation.java create mode 100644 tools/idea/src/main/java/org/teavm/idea/TeaVMJPSRemoteService.java create mode 100644 tools/idea/src/main/java/org/teavm/idea/TeaVMRemoteConfigurationStorage.java diff --git a/tools/idea/jps-plugin/src/main/java/org/teavm/idea/jps/TeaVMBuilder.java b/tools/idea/jps-plugin/src/main/java/org/teavm/idea/jps/TeaVMBuilder.java index 47c86abaa..d41da4d05 100644 --- a/tools/idea/jps-plugin/src/main/java/org/teavm/idea/jps/TeaVMBuilder.java +++ b/tools/idea/jps-plugin/src/main/java/org/teavm/idea/jps/TeaVMBuilder.java @@ -28,8 +28,6 @@ import org.jetbrains.jps.incremental.ProjectBuildException; import org.jetbrains.jps.model.module.JpsModule; public class TeaVMBuilder extends ModuleLevelBuilder { - private TeaVMStorageProvider storageProvider = new TeaVMStorageProvider(); - public TeaVMBuilder() { super(BuilderCategory.CLASS_POST_PROCESSOR); } diff --git a/tools/idea/jps-plugin/src/main/java/org/teavm/idea/jps/model/TeaVMJpsRemoteConfiguration.java b/tools/idea/jps-plugin/src/main/java/org/teavm/idea/jps/model/TeaVMJpsRemoteConfiguration.java new file mode 100644 index 000000000..4a25e06e9 --- /dev/null +++ b/tools/idea/jps-plugin/src/main/java/org/teavm/idea/jps/model/TeaVMJpsRemoteConfiguration.java @@ -0,0 +1,58 @@ +/* + * 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.jps.model; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jps.model.JpsElementChildRole; +import org.jetbrains.jps.model.JpsProject; +import org.jetbrains.jps.model.ex.JpsElementBase; +import org.jetbrains.jps.model.ex.JpsElementChildRoleBase; +import org.jetbrains.jps.model.module.JpsModule; + +public class TeaVMJpsRemoteConfiguration extends JpsElementBase { + private static final JpsElementChildRole ROLE = JpsElementChildRoleBase.create( + "TeaVM remote configuration"); + private int port; + + public static TeaVMJpsRemoteConfiguration get(JpsProject project) { + return project.getContainer().getChild(ROLE); + } + + public void setTo(JpsModule project) { + project.getContainer().setChild(ROLE, this); + } + + public int getPort() { + return port; + } + + public void setPort(int port) { + this.port = port; + } + + @NotNull + @Override + public TeaVMJpsRemoteConfiguration createCopy() { + TeaVMJpsRemoteConfiguration copy = new TeaVMJpsRemoteConfiguration(); + copy.port = port; + return copy; + } + + @Override + public void applyChanges(@NotNull TeaVMJpsRemoteConfiguration modified) { + port = modified.port; + } +} diff --git a/tools/idea/jps-plugin/src/main/java/org/teavm/idea/jps/remote/TeaVMBuilderAssistant.java b/tools/idea/jps-plugin/src/main/java/org/teavm/idea/jps/remote/TeaVMBuilderAssistant.java new file mode 100644 index 000000000..fd4ae05d7 --- /dev/null +++ b/tools/idea/jps-plugin/src/main/java/org/teavm/idea/jps/remote/TeaVMBuilderAssistant.java @@ -0,0 +1,24 @@ +/* + * 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.jps.remote; + +import java.rmi.Remote; +import java.rmi.RemoteException; + +public interface TeaVMBuilderAssistant extends Remote { + TeaVMElementLocation getMethodLocation(String className, String methodName, String methodDesc) + throws RemoteException; +} diff --git a/tools/idea/jps-plugin/src/main/java/org/teavm/idea/jps/remote/TeaVMElementLocation.java b/tools/idea/jps-plugin/src/main/java/org/teavm/idea/jps/remote/TeaVMElementLocation.java new file mode 100644 index 000000000..d4649e77d --- /dev/null +++ b/tools/idea/jps-plugin/src/main/java/org/teavm/idea/jps/remote/TeaVMElementLocation.java @@ -0,0 +1,48 @@ +/* + * 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.jps.remote; + +import java.io.Serializable; + +public class TeaVMElementLocation implements Serializable { + private int startOffset; + private int endOffset; + private int line; + private int column; + + public TeaVMElementLocation(int startOffset, int endOffset, int line, int column) { + this.startOffset = startOffset; + this.endOffset = endOffset; + this.line = line; + this.column = column; + } + + public int getStartOffset() { + return startOffset; + } + + public int getEndOffset() { + return endOffset; + } + + public int getLine() { + return line; + } + + public int getColumn() { + return column; + } +} diff --git a/tools/idea/jps-plugin/teavm-jps-plugin.iml b/tools/idea/jps-plugin/teavm-jps-plugin.iml index 29b643292..8a7611ab7 100644 --- a/tools/idea/jps-plugin/teavm-jps-plugin.iml +++ b/tools/idea/jps-plugin/teavm-jps-plugin.iml @@ -9,7 +9,7 @@ - + diff --git a/tools/idea/src/main/java/org/teavm/idea/TeaVMConfigurationStorage.java b/tools/idea/src/main/java/org/teavm/idea/TeaVMConfigurationStorage.java index 03481ea7e..773ee37c8 100644 --- a/tools/idea/src/main/java/org/teavm/idea/TeaVMConfigurationStorage.java +++ b/tools/idea/src/main/java/org/teavm/idea/TeaVMConfigurationStorage.java @@ -21,7 +21,7 @@ import com.intellij.openapi.components.Storage; import org.jetbrains.annotations.Nullable; import org.teavm.idea.jps.model.TeaVMJpsConfiguration; -@State(name = "teavm", storages = @Storage(id = "other", file = "$MODULE_FILE$")) +@State(name = "teavm", storages = @Storage(id = "other", file = "$MODULE_FILE$")) public class TeaVMConfigurationStorage implements PersistentStateComponent { private TeaVMJpsConfiguration state = new TeaVMJpsConfiguration(); diff --git a/tools/idea/src/main/java/org/teavm/idea/TeaVMJPSRemoteService.java b/tools/idea/src/main/java/org/teavm/idea/TeaVMJPSRemoteService.java new file mode 100644 index 000000000..2098741c0 --- /dev/null +++ b/tools/idea/src/main/java/org/teavm/idea/TeaVMJPSRemoteService.java @@ -0,0 +1,110 @@ +/* + * 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; + +import com.intellij.openapi.components.ApplicationComponent; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.project.ProjectManager; +import com.intellij.openapi.project.ProjectManagerAdapter; +import com.intellij.psi.JavaPsiFacade; +import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiMethod; +import com.intellij.psi.search.GlobalSearchScope; +import java.rmi.AlreadyBoundException; +import java.rmi.NotBoundException; +import java.rmi.RemoteException; +import java.rmi.registry.LocateRegistry; +import java.rmi.registry.Registry; +import java.rmi.server.UnicastRemoteObject; +import org.jetbrains.annotations.NotNull; +import org.teavm.idea.jps.model.TeaVMJpsRemoteConfiguration; +import org.teavm.idea.jps.remote.TeaVMBuilderAssistant; +import org.teavm.idea.jps.remote.TeaVMElementLocation; + +public class TeaVMJPSRemoteService implements ApplicationComponent, TeaVMBuilderAssistant { + private ProjectManager projectManager = ProjectManager.getInstance(); + private int port; + private Registry registry; + + @Override + public void initComponent() { + + for (Project project : projectManager.getOpenProjects()) { + configureProject(project); + } + projectManager.addProjectManagerListener(new ProjectManagerAdapter() { + @Override + public void projectOpened(Project project) { + configureProject(project); + } + }); + } + + private void configureProject(Project project) { + try { + registry = LocateRegistry.createRegistry(0); + registry.bind("TeaVM", this); + } catch (RemoteException | AlreadyBoundException e) { + e.printStackTrace(); + } + TeaVMRemoteConfigurationStorage storage = project.getComponent(TeaVMRemoteConfigurationStorage.class); + TeaVMJpsRemoteConfiguration config = storage.getState(); + config.setPort(port); + storage.loadState(config); + } + + @Override + public void disposeComponent() { + try { + registry.unbind("TeaVM"); + UnicastRemoteObject.unexportObject(registry, true); + } catch (RemoteException | NotBoundException e) { + e.printStackTrace(); + } + } + + @NotNull + @Override + public String getComponentName() { + return "TeaVM JPS service"; + } + + @Override + public TeaVMElementLocation getMethodLocation(String className, String methodName, String methodDesc) + throws RemoteException { + for (Project project : projectManager.getOpenProjects()) { + JavaPsiFacade psi = JavaPsiFacade.getInstance(project); + PsiClass cls = psi.findClass(className, GlobalSearchScope.allScope(project)); + if (cls == null) { + continue; + } + + for (PsiMethod method : cls.getAllMethods()) { + if (!method.getName().equals(methodName)) { + continue; + } + // TODO: check method raw signature + return getMethodLocation(method); + } + } + return null; + } + + private TeaVMElementLocation getMethodLocation(PsiMethod method) { + return new TeaVMElementLocation(method.getTextOffset(), method.getTextOffset() + method.getTextLength(), + -1, -1); + } +} diff --git a/tools/idea/src/main/java/org/teavm/idea/TeaVMRemoteConfigurationStorage.java b/tools/idea/src/main/java/org/teavm/idea/TeaVMRemoteConfigurationStorage.java new file mode 100644 index 000000000..de8021013 --- /dev/null +++ b/tools/idea/src/main/java/org/teavm/idea/TeaVMRemoteConfigurationStorage.java @@ -0,0 +1,39 @@ +/* + * 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; + +import com.intellij.openapi.components.PersistentStateComponent; +import com.intellij.openapi.components.State; +import com.intellij.openapi.components.Storage; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.teavm.idea.jps.model.TeaVMJpsRemoteConfiguration; + +@State(name = "teavm", storages = @Storage(id = "other", file = "$PROJECT_FILE$")) +public class TeaVMRemoteConfigurationStorage implements PersistentStateComponent { + private TeaVMJpsRemoteConfiguration state = new TeaVMJpsRemoteConfiguration(); + + @NotNull + @Override + public TeaVMJpsRemoteConfiguration getState() { + return state.createCopy(); + } + + @Override + public void loadState(TeaVMJpsRemoteConfiguration state) { + this.state.applyChanges(state); + } +} diff --git a/tools/idea/teavm-idea-plugin.iml b/tools/idea/teavm-idea-plugin.iml index 656bd4099..8ec274334 100644 --- a/tools/idea/teavm-idea-plugin.iml +++ b/tools/idea/teavm-idea-plugin.iml @@ -10,7 +10,7 @@ - + From 7f379eaeb7b470607aa4b15bbab0a794209a3ff7 Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Wed, 20 Apr 2016 23:40:17 +0300 Subject: [PATCH 3/5] Improving build diagnostics in IDEA --- .../java/org/teavm/idea/jps/TeaVMBuild.java | 199 +++++++++++++----- .../java/org/teavm/idea/jps/TeaVMBuilder.java | 27 ++- .../model/TeaVMJpsRemoteConfiguration.java | 58 ----- .../jps/remote/TeaVMBuilderAssistant.java | 2 + .../org/teavm/idea/TeaVMJPSConfigurator.java | 36 ++++ .../org/teavm/idea/TeaVMJPSRemoteService.java | 56 ++--- .../idea/TeaVMRemoteConfigurationStorage.java | 39 ---- .../src/main/resources/META-INF/plugin.xml | 7 + 8 files changed, 248 insertions(+), 176 deletions(-) delete mode 100644 tools/idea/jps-plugin/src/main/java/org/teavm/idea/jps/model/TeaVMJpsRemoteConfiguration.java create mode 100644 tools/idea/src/main/java/org/teavm/idea/TeaVMJPSConfigurator.java delete mode 100644 tools/idea/src/main/java/org/teavm/idea/TeaVMRemoteConfigurationStorage.java diff --git a/tools/idea/jps-plugin/src/main/java/org/teavm/idea/jps/TeaVMBuild.java b/tools/idea/jps-plugin/src/main/java/org/teavm/idea/jps/TeaVMBuild.java index 92ef91ff2..f2b61ed26 100644 --- a/tools/idea/jps-plugin/src/main/java/org/teavm/idea/jps/TeaVMBuild.java +++ b/tools/idea/jps-plugin/src/main/java/org/teavm/idea/jps/TeaVMBuild.java @@ -18,13 +18,18 @@ package org.teavm.idea.jps; import static java.util.stream.Collectors.toList; import static java.util.stream.Collectors.toSet; import java.io.File; +import java.io.FileInputStream; import java.io.IOException; +import java.io.InputStreamReader; +import java.io.Reader; import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; import java.util.ArrayList; +import java.util.HashMap; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.stream.Stream; @@ -33,6 +38,7 @@ import org.jetbrains.jps.incremental.ModuleBuildTarget; import org.jetbrains.jps.incremental.messages.BuildMessage; import org.jetbrains.jps.incremental.messages.CompilerMessage; import org.jetbrains.jps.incremental.messages.ProgressMessage; +import org.jetbrains.jps.model.JpsProject; import org.jetbrains.jps.model.java.JpsJavaExtensionService; import org.jetbrains.jps.model.library.JpsLibrary; import org.jetbrains.jps.model.library.JpsOrderRootType; @@ -40,10 +46,21 @@ import org.jetbrains.jps.model.module.JpsDependencyElement; 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.common.IntegerArray; +import org.teavm.diagnostics.DefaultProblemTextConsumer; +import org.teavm.diagnostics.Problem; +import org.teavm.diagnostics.ProblemProvider; import org.teavm.idea.jps.model.TeaVMJpsConfiguration; +import org.teavm.idea.jps.remote.TeaVMBuilderAssistant; +import org.teavm.idea.jps.remote.TeaVMElementLocation; +import org.teavm.model.CallLocation; +import org.teavm.model.InstructionLocation; +import org.teavm.model.MethodReference; +import org.teavm.model.ValueType; +import org.teavm.tooling.EmptyTeaVMToolLog; import org.teavm.tooling.TeaVMTool; import org.teavm.tooling.TeaVMToolException; -import org.teavm.tooling.TeaVMToolLog; import org.teavm.vm.TeaVMPhase; import org.teavm.vm.TeaVMProgressFeedback; import org.teavm.vm.TeaVMProgressListener; @@ -54,9 +71,13 @@ public class TeaVMBuild { private List classPathEntries = new ArrayList<>(); private List directoryClassPathEntries; private TeaVMStorage storage; + private TeaVMBuilderAssistant assistant; + private Map sourceFileCache = new HashMap<>(); + private Map fileLineCache = new HashMap<>(); - public TeaVMBuild(CompileContext context) { + public TeaVMBuild(CompileContext context, TeaVMBuilderAssistant assistant) { this.context = context; + this.assistant = assistant; } public boolean perform(JpsModule module, ModuleBuildTarget target) throws IOException { @@ -78,7 +99,7 @@ public class TeaVMBuild { TeaVMTool tool = new TeaVMTool(); tool.setProgressListener(createProgressListener(context)); - tool.setLog(createLog(context)); + tool.setLog(new EmptyTeaVMToolLog()); tool.setMainClass(config.getMainClass()); tool.setSourceMapsFileGenerated(true); tool.setTargetDirectory(new File(config.getTargetDirectory())); @@ -98,9 +119,133 @@ public class TeaVMBuild { updateStorage(tool); } + reportProblems(tool.getProblemProvider()); + return true; } + private void reportProblems(ProblemProvider problemProvider) { + for (Problem problem : problemProvider.getProblems()) { + BuildMessage.Kind kind; + switch (problem.getSeverity()) { + case ERROR: + kind = BuildMessage.Kind.ERROR; + break; + case WARNING: + kind = BuildMessage.Kind.WARNING; + break; + default: + 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(); + } catch (Exception e) { + // just don't fill location fields + } + } + } + + DefaultProblemTextConsumer textConsumer = new DefaultProblemTextConsumer(); + problem.render(textConsumer); + + if (path != null) { + file = lookupSource(path); + path = file != null ? file.getPath() : null; + } + + if (startOffset < 0 && file != null && line > 0) { + int[] lines = getLineOffsets(file); + if (lines != null && line < lines.length) { + startOffset = lines[line - 1]; + endOffset = lines[line] - 1; + } + } + + context.processMessage(new CompilerMessage("TeaVM", kind, textConsumer.getText(), path, + startOffset, endOffset, startOffset, line, 0)); + } + } + + private File lookupSource(String relativePath) { + return sourceFileCache.computeIfAbsent(relativePath, this::lookupSourceCacheMiss); + } + + private File lookupSourceCacheMiss(String relativePath) { + JpsProject project = context.getProjectDescriptor().getModel().getProject(); + for (JpsModule module : project.getModules()) { + for (JpsModuleSourceRoot sourceRoot : module.getSourceRoots()) { + File fullPath = new File(sourceRoot.getFile(), relativePath); + if (fullPath.exists()) { + return fullPath; + } + } + } + return null; + } + + private int[] getLineOffsets(File file) { + return fileLineCache.computeIfAbsent(file, this::getLineOffsetsCacheMiss); + } + + private int[] getLineOffsetsCacheMiss(File file) { + IntegerArray lines = new IntegerArray(50); + try (Reader reader = new InputStreamReader(new FileInputStream(file), "UTF-8")) { + int offset = 0; + lines.add(0); + + boolean expectingLf = false; + while (true) { + int c = reader.read(); + if (c == -1) { + break; + } + if (c == '\n') { + expectingLf = false; + lines.add(offset + 1); + } else { + if (expectingLf) { + expectingLf = false; + lines.add(offset); + } + if (c == '\r') { + lines.add(offset + 1); + expectingLf = true; + } + } + ++offset; + } + + if (expectingLf) { + lines.add(offset); + } + lines.add(offset + 1); + } catch (IOException e) { + return null; + } + return lines.getAll(); + } + private boolean hasChanges(ModuleBuildTarget target) { if (!context.getScope().isBuildIncrementally(target.getTargetType()) || context.getScope().isBuildForced(target)) { @@ -145,54 +290,6 @@ public class TeaVMBuild { return null; } - private TeaVMToolLog createLog(CompileContext context) { - return new TeaVMToolLog() { - @Override - public void info(String text) { - context.processMessage(new CompilerMessage("TeaVM", BuildMessage.Kind.INFO, text)); - } - - @Override - public void debug(String text) { - context.processMessage(new CompilerMessage("TeaVM", BuildMessage.Kind.INFO, text)); - } - - @Override - public void warning(String text) { - context.processMessage(new CompilerMessage("TeaVM", BuildMessage.Kind.WARNING, text)); - } - - @Override - public void error(String text) { - context.processMessage(new CompilerMessage("TeaVM", BuildMessage.Kind.ERROR, text)); - } - - @Override - public void info(String text, Throwable e) { - context.processMessage(new CompilerMessage("TeaVM", BuildMessage.Kind.INFO, text + "\n" - + CompilerMessage.getTextFromThrowable(e))); - } - - @Override - public void debug(String text, Throwable e) { - context.processMessage(new CompilerMessage("TeaVM", BuildMessage.Kind.INFO, text + "\n" - + CompilerMessage.getTextFromThrowable(e))); - } - - @Override - public void warning(String text, Throwable e) { - context.processMessage(new CompilerMessage("TeaVM", BuildMessage.Kind.WARNING, text + "\n" - + CompilerMessage.getTextFromThrowable(e))); - } - - @Override - public void error(String text, Throwable e) { - context.processMessage(new CompilerMessage("TeaVM", BuildMessage.Kind.ERROR, text + "\n" - + CompilerMessage.getTextFromThrowable(e))); - } - }; - } - private TeaVMProgressListener createProgressListener(CompileContext context) { return new TeaVMProgressListener() { private TeaVMPhase currentPhase; diff --git a/tools/idea/jps-plugin/src/main/java/org/teavm/idea/jps/TeaVMBuilder.java b/tools/idea/jps-plugin/src/main/java/org/teavm/idea/jps/TeaVMBuilder.java index d41da4d05..ac9bedc3d 100644 --- a/tools/idea/jps-plugin/src/main/java/org/teavm/idea/jps/TeaVMBuilder.java +++ b/tools/idea/jps-plugin/src/main/java/org/teavm/idea/jps/TeaVMBuilder.java @@ -16,6 +16,10 @@ package org.teavm.idea.jps; import java.io.IOException; +import java.rmi.NotBoundException; +import java.rmi.RemoteException; +import java.rmi.registry.LocateRegistry; +import java.rmi.registry.Registry; import org.jetbrains.annotations.NotNull; import org.jetbrains.jps.ModuleChunk; import org.jetbrains.jps.builders.DirtyFilesHolder; @@ -25,20 +29,41 @@ import org.jetbrains.jps.incremental.CompileContext; import org.jetbrains.jps.incremental.ModuleBuildTarget; import org.jetbrains.jps.incremental.ModuleLevelBuilder; import org.jetbrains.jps.incremental.ProjectBuildException; +import org.jetbrains.jps.incremental.messages.BuildMessage; +import org.jetbrains.jps.incremental.messages.CompilerMessage; import org.jetbrains.jps.model.module.JpsModule; +import org.teavm.idea.jps.remote.TeaVMBuilderAssistant; public class TeaVMBuilder extends ModuleLevelBuilder { + public static final String REMOTE_PORT = "teavm.jps.remote-port"; + private static TeaVMBuilderAssistant assistant; + public TeaVMBuilder() { super(BuilderCategory.CLASS_POST_PROCESSOR); + + String portString = System.getProperty(REMOTE_PORT); + if (portString != null) { + try { + Registry registry = LocateRegistry.getRegistry(Integer.parseInt(portString)); + assistant = (TeaVMBuilderAssistant) registry.lookup(TeaVMBuilderAssistant.ID); + } catch (NumberFormatException | RemoteException | NotBoundException e) { + e.printStackTrace(); + } + } } @Override public ExitCode build(CompileContext context, ModuleChunk chunk, DirtyFilesHolder dirtyFilesHolder, OutputConsumer outputConsumer) throws ProjectBuildException, IOException { + if (assistant == null) { + context.processMessage(new CompilerMessage("TeaVM", BuildMessage.Kind.WARNING, + "No TeaVM builder assistant available. Diagnostic messages will be less informative")); + } + boolean doneSomething = false; - TeaVMBuild build = new TeaVMBuild(context); + TeaVMBuild build = new TeaVMBuild(context, assistant); for (JpsModule module : chunk.getModules()) { doneSomething |= build.perform(module, chunk.representativeTarget()); if (context.getCancelStatus().isCanceled()) { diff --git a/tools/idea/jps-plugin/src/main/java/org/teavm/idea/jps/model/TeaVMJpsRemoteConfiguration.java b/tools/idea/jps-plugin/src/main/java/org/teavm/idea/jps/model/TeaVMJpsRemoteConfiguration.java deleted file mode 100644 index 4a25e06e9..000000000 --- a/tools/idea/jps-plugin/src/main/java/org/teavm/idea/jps/model/TeaVMJpsRemoteConfiguration.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * 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.jps.model; - -import org.jetbrains.annotations.NotNull; -import org.jetbrains.jps.model.JpsElementChildRole; -import org.jetbrains.jps.model.JpsProject; -import org.jetbrains.jps.model.ex.JpsElementBase; -import org.jetbrains.jps.model.ex.JpsElementChildRoleBase; -import org.jetbrains.jps.model.module.JpsModule; - -public class TeaVMJpsRemoteConfiguration extends JpsElementBase { - private static final JpsElementChildRole ROLE = JpsElementChildRoleBase.create( - "TeaVM remote configuration"); - private int port; - - public static TeaVMJpsRemoteConfiguration get(JpsProject project) { - return project.getContainer().getChild(ROLE); - } - - public void setTo(JpsModule project) { - project.getContainer().setChild(ROLE, this); - } - - public int getPort() { - return port; - } - - public void setPort(int port) { - this.port = port; - } - - @NotNull - @Override - public TeaVMJpsRemoteConfiguration createCopy() { - TeaVMJpsRemoteConfiguration copy = new TeaVMJpsRemoteConfiguration(); - copy.port = port; - return copy; - } - - @Override - public void applyChanges(@NotNull TeaVMJpsRemoteConfiguration modified) { - port = modified.port; - } -} diff --git a/tools/idea/jps-plugin/src/main/java/org/teavm/idea/jps/remote/TeaVMBuilderAssistant.java b/tools/idea/jps-plugin/src/main/java/org/teavm/idea/jps/remote/TeaVMBuilderAssistant.java index fd4ae05d7..0a9741b8a 100644 --- a/tools/idea/jps-plugin/src/main/java/org/teavm/idea/jps/remote/TeaVMBuilderAssistant.java +++ b/tools/idea/jps-plugin/src/main/java/org/teavm/idea/jps/remote/TeaVMBuilderAssistant.java @@ -19,6 +19,8 @@ import java.rmi.Remote; import java.rmi.RemoteException; public interface TeaVMBuilderAssistant extends Remote { + String ID = "TeaVM-JPS-Assistant"; + TeaVMElementLocation getMethodLocation(String className, String methodName, String methodDesc) throws RemoteException; } diff --git a/tools/idea/src/main/java/org/teavm/idea/TeaVMJPSConfigurator.java b/tools/idea/src/main/java/org/teavm/idea/TeaVMJPSConfigurator.java new file mode 100644 index 000000000..a03f8ed06 --- /dev/null +++ b/tools/idea/src/main/java/org/teavm/idea/TeaVMJPSConfigurator.java @@ -0,0 +1,36 @@ +/* + * 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; + +import com.intellij.compiler.server.BuildProcessParametersProvider; +import java.util.Collections; +import java.util.List; +import org.jetbrains.annotations.NotNull; +import org.teavm.idea.jps.TeaVMBuilder; + +public class TeaVMJPSConfigurator extends BuildProcessParametersProvider { + private TeaVMJPSRemoteService remoteService; + + public TeaVMJPSConfigurator(TeaVMJPSRemoteService remoteService) { + this.remoteService = remoteService; + } + + @NotNull + @Override + public List getVMArguments() { + return Collections.singletonList("-D" + TeaVMBuilder.REMOTE_PORT + "=" + remoteService.getPort()); + } +} diff --git a/tools/idea/src/main/java/org/teavm/idea/TeaVMJPSRemoteService.java b/tools/idea/src/main/java/org/teavm/idea/TeaVMJPSRemoteService.java index 2098741c0..c13279dd1 100644 --- a/tools/idea/src/main/java/org/teavm/idea/TeaVMJPSRemoteService.java +++ b/tools/idea/src/main/java/org/teavm/idea/TeaVMJPSRemoteService.java @@ -18,7 +18,6 @@ package org.teavm.idea; import com.intellij.openapi.components.ApplicationComponent; import com.intellij.openapi.project.Project; import com.intellij.openapi.project.ProjectManager; -import com.intellij.openapi.project.ProjectManagerAdapter; import com.intellij.psi.JavaPsiFacade; import com.intellij.psi.PsiClass; import com.intellij.psi.PsiMethod; @@ -29,50 +28,53 @@ import java.rmi.RemoteException; import java.rmi.registry.LocateRegistry; import java.rmi.registry.Registry; import java.rmi.server.UnicastRemoteObject; +import java.util.Random; import org.jetbrains.annotations.NotNull; -import org.teavm.idea.jps.model.TeaVMJpsRemoteConfiguration; import org.teavm.idea.jps.remote.TeaVMBuilderAssistant; import org.teavm.idea.jps.remote.TeaVMElementLocation; -public class TeaVMJPSRemoteService implements ApplicationComponent, TeaVMBuilderAssistant { +public class TeaVMJPSRemoteService extends UnicastRemoteObject implements ApplicationComponent, TeaVMBuilderAssistant { + private static final int MIN_PORT = 10000; + private static final int MAX_PORT = 1 << 16; private ProjectManager projectManager = ProjectManager.getInstance(); private int port; private Registry registry; - @Override - public void initComponent() { - - for (Project project : projectManager.getOpenProjects()) { - configureProject(project); - } - projectManager.addProjectManagerListener(new ProjectManagerAdapter() { - @Override - public void projectOpened(Project project) { - configureProject(project); - } - }); + public TeaVMJPSRemoteService() throws RemoteException { + super(); } - private void configureProject(Project project) { - try { - registry = LocateRegistry.createRegistry(0); - registry.bind("TeaVM", this); - } catch (RemoteException | AlreadyBoundException e) { - e.printStackTrace(); + @Override + public void initComponent() { + Random random = new Random(); + for (int i = 0; i < 20; ++i) { + port = random.nextInt(MAX_PORT - MIN_PORT) + MIN_PORT; + try { + registry = LocateRegistry.createRegistry(port); + } catch (RemoteException e) { + continue; + } + try { + registry.bind(TeaVMBuilderAssistant.ID, this); + } catch (RemoteException | AlreadyBoundException e) { + throw new IllegalStateException("Could not bind remote build assistant service", e); + } + return; } - TeaVMRemoteConfigurationStorage storage = project.getComponent(TeaVMRemoteConfigurationStorage.class); - TeaVMJpsRemoteConfiguration config = storage.getState(); - config.setPort(port); - storage.loadState(config); + throw new IllegalStateException("Could not create RMI registry"); + } + + public int getPort() { + return port; } @Override public void disposeComponent() { try { - registry.unbind("TeaVM"); + registry.unbind(TeaVMBuilderAssistant.ID); UnicastRemoteObject.unexportObject(registry, true); } catch (RemoteException | NotBoundException e) { - e.printStackTrace(); + throw new IllegalStateException("Could not clean-up RMI server", e); } } diff --git a/tools/idea/src/main/java/org/teavm/idea/TeaVMRemoteConfigurationStorage.java b/tools/idea/src/main/java/org/teavm/idea/TeaVMRemoteConfigurationStorage.java deleted file mode 100644 index de8021013..000000000 --- a/tools/idea/src/main/java/org/teavm/idea/TeaVMRemoteConfigurationStorage.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * 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; - -import com.intellij.openapi.components.PersistentStateComponent; -import com.intellij.openapi.components.State; -import com.intellij.openapi.components.Storage; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.teavm.idea.jps.model.TeaVMJpsRemoteConfiguration; - -@State(name = "teavm", storages = @Storage(id = "other", file = "$PROJECT_FILE$")) -public class TeaVMRemoteConfigurationStorage implements PersistentStateComponent { - private TeaVMJpsRemoteConfiguration state = new TeaVMJpsRemoteConfiguration(); - - @NotNull - @Override - public TeaVMJpsRemoteConfiguration getState() { - return state.createCopy(); - } - - @Override - public void loadState(TeaVMJpsRemoteConfiguration state) { - this.state.applyChanges(state); - } -} diff --git a/tools/idea/src/main/resources/META-INF/plugin.xml b/tools/idea/src/main/resources/META-INF/plugin.xml index b5dadbfbb..7a7b6b784 100644 --- a/tools/idea/src/main/resources/META-INF/plugin.xml +++ b/tools/idea/src/main/resources/META-INF/plugin.xml @@ -17,10 +17,17 @@ + + + org.teavm.idea.TeaVMJPSRemoteService + + + + \ No newline at end of file From 0d3d6e883d0315cb14c111ce3d588d571786199f Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Thu, 21 Apr 2016 22:29:40 +0300 Subject: [PATCH 4/5] Improving build diagnostics in IDEA. Fix minor problems in JSO --- .../org/teavm/jso/impl/JSClassProcessor.java | 20 ++++------- .../java/org/teavm/idea/jps/TeaVMBuild.java | 22 +++++++++++-- .../idea/jps/remote/TeaVMElementLocation.java | 8 ++++- .../org/teavm/idea/TeaVMJPSRemoteService.java | 33 +++++++++++-------- 4 files changed, 52 insertions(+), 31 deletions(-) diff --git a/jso/impl/src/main/java/org/teavm/jso/impl/JSClassProcessor.java b/jso/impl/src/main/java/org/teavm/jso/impl/JSClassProcessor.java index 5c70de17c..1d8379e78 100644 --- a/jso/impl/src/main/java/org/teavm/jso/impl/JSClassProcessor.java +++ b/jso/impl/src/main/java/org/teavm/jso/impl/JSClassProcessor.java @@ -329,11 +329,11 @@ class JSClassProcessor { } if (method.getProgram() != null && method.getProgram().basicBlockCount() > 0) { - MethodReader overriden = getOverridenMethod(method); - if (overriden != null) { + MethodReader overridden = getOverridenMethod(method); + if (overridden != null) { diagnostics.error(callLocation, "JS final method {{m0}} overrides {{m1}}. " + "Overriding final method of overlay types is prohibited.", - method.getReference(), overriden.getReference()); + method.getReference(), overridden.getReference()); } if (method.getProgram() != null && method.getProgram().basicBlockCount() > 0) { invoke.setMethod(new MethodReference(method.getOwnerName(), method.getName() + "$static", @@ -545,7 +545,7 @@ class JSClassProcessor { int jsParamCount = bodyAnnot.getValue("params").getList().size(); if (methodToProcess.parameterCount() != jsParamCount) { diagnostics.error(location, "JSBody method {{m0}} declares " + methodToProcess.parameterCount() - + " parameters, but annotation specifies " + jsParamCount, methodToProcess); + + " parameters, but annotation specifies " + jsParamCount, methodToProcess.getReference()); return; } @@ -554,11 +554,8 @@ class JSClassProcessor { if (!isStatic) { ++paramCount; } - ValueType[] paramTypes = new ValueType[paramCount]; - int offset = 0; if (!isStatic) { ValueType paramType = ValueType.object(methodToProcess.getOwnerName()); - paramTypes[offset++] = paramType; if (!typeHelper.isSupportedType(paramType)) { diagnostics.error(location, "Non-static JSBody method {{m0}} is owned by non-JS class {{c1}}", methodToProcess.getReference(), methodToProcess.getOwnerName()); @@ -571,9 +568,6 @@ class JSClassProcessor { } // generate parameter types for proxy method - for (int i = 0; i < methodToProcess.parameterCount(); ++i) { - paramTypes[offset++] = methodToProcess.parameterType(i); - } ValueType[] proxyParamTypes = new ValueType[paramCount + 1]; for (int i = 0; i < paramCount; ++i) { proxyParamTypes[i] = ValueType.parse(JSObject.class); @@ -587,7 +581,7 @@ class JSClassProcessor { methodToProcess.getName() + "$js_body$_" + methodIndexGenerator++, proxyParamTypes); String script = bodyAnnot.getValue("script").getString(); String[] parameterNames = bodyAnnot.getValue("params").getList().stream() - .map(ann -> ann.getString()) + .map(AnnotationValue::getString) .toArray(String[]::new); // Parse JS script @@ -598,15 +592,13 @@ class JSClassProcessor { env.setLanguageVersion(Context.VERSION_1_8); env.setIdeMode(true); JSParser parser = new JSParser(env, errorReporter); - //parser.enterFunction(); AstRoot rootNode; try { rootNode = parser.parse(new StringReader("function(){" + script + "}"), null, 0); } catch (IOException e) { - throw new RuntimeException("IO Error occured", e); + throw new RuntimeException("IO Error occurred", e); } AstNode body = ((FunctionNode) rootNode.getFirstChild()).getBody(); - //parser.exitFunction(); repository.methodMap.put(methodToProcess.getReference(), proxyMethod); if (errorReporter.hasErrors()) { diff --git a/tools/idea/jps-plugin/src/main/java/org/teavm/idea/jps/TeaVMBuild.java b/tools/idea/jps-plugin/src/main/java/org/teavm/idea/jps/TeaVMBuild.java index f2b61ed26..aa183f62a 100644 --- a/tools/idea/jps-plugin/src/main/java/org/teavm/idea/jps/TeaVMBuild.java +++ b/tools/idea/jps-plugin/src/main/java/org/teavm/idea/jps/TeaVMBuild.java @@ -26,6 +26,7 @@ import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -160,6 +161,17 @@ public class TeaVMBuild { 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 } @@ -169,9 +181,13 @@ public class TeaVMBuild { DefaultProblemTextConsumer textConsumer = new DefaultProblemTextConsumer(); problem.render(textConsumer); - if (path != null) { - file = lookupSource(path); - path = file != null ? file.getPath() : null; + if (file == null) { + if (path != null) { + file = lookupSource(path); + path = file != null ? file.getPath() : null; + } + } else { + path = file.getPath(); } if (startOffset < 0 && file != null && line > 0) { diff --git a/tools/idea/jps-plugin/src/main/java/org/teavm/idea/jps/remote/TeaVMElementLocation.java b/tools/idea/jps-plugin/src/main/java/org/teavm/idea/jps/remote/TeaVMElementLocation.java index d4649e77d..2ef745a53 100644 --- a/tools/idea/jps-plugin/src/main/java/org/teavm/idea/jps/remote/TeaVMElementLocation.java +++ b/tools/idea/jps-plugin/src/main/java/org/teavm/idea/jps/remote/TeaVMElementLocation.java @@ -22,12 +22,14 @@ public class TeaVMElementLocation implements Serializable { private int endOffset; private int line; private int column; + private String path; - public TeaVMElementLocation(int startOffset, int endOffset, int line, int column) { + public TeaVMElementLocation(int startOffset, int endOffset, int line, int column, String path) { this.startOffset = startOffset; this.endOffset = endOffset; this.line = line; this.column = column; + this.path = path; } public int getStartOffset() { @@ -45,4 +47,8 @@ public class TeaVMElementLocation implements Serializable { public int getColumn() { return column; } + + public String getPath() { + return path; + } } diff --git a/tools/idea/src/main/java/org/teavm/idea/TeaVMJPSRemoteService.java b/tools/idea/src/main/java/org/teavm/idea/TeaVMJPSRemoteService.java index c13279dd1..38e0d72ff 100644 --- a/tools/idea/src/main/java/org/teavm/idea/TeaVMJPSRemoteService.java +++ b/tools/idea/src/main/java/org/teavm/idea/TeaVMJPSRemoteService.java @@ -15,6 +15,7 @@ */ package org.teavm.idea; +import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.components.ApplicationComponent; import com.intellij.openapi.project.Project; import com.intellij.openapi.project.ProjectManager; @@ -87,26 +88,32 @@ public class TeaVMJPSRemoteService extends UnicastRemoteObject implements Applic @Override public TeaVMElementLocation getMethodLocation(String className, String methodName, String methodDesc) throws RemoteException { - for (Project project : projectManager.getOpenProjects()) { - JavaPsiFacade psi = JavaPsiFacade.getInstance(project); - PsiClass cls = psi.findClass(className, GlobalSearchScope.allScope(project)); - if (cls == null) { - continue; - } + TeaVMElementLocation[] resultHolder = new TeaVMElementLocation[1]; - for (PsiMethod method : cls.getAllMethods()) { - if (!method.getName().equals(methodName)) { + ApplicationManager.getApplication().runReadAction(() -> { + for (Project project : projectManager.getOpenProjects()) { + JavaPsiFacade psi = JavaPsiFacade.getInstance(project); + PsiClass cls = psi.findClass(className, GlobalSearchScope.allScope(project)); + if (cls == null) { continue; } - // TODO: check method raw signature - return getMethodLocation(method); + + for (PsiMethod method : cls.getAllMethods()) { + if (!method.getName().equals(methodName)) { + continue; + } + // TODO: check method raw signature + resultHolder[0] = getMethodLocation(method); + return; + } } - } - return null; + }); + + return resultHolder[0]; } private TeaVMElementLocation getMethodLocation(PsiMethod method) { return new TeaVMElementLocation(method.getTextOffset(), method.getTextOffset() + method.getTextLength(), - -1, -1); + -1, -1, method.getContainingFile().getVirtualFile().getPath()); } } From 9efed77d603ac49f8e697ab20ca028ea39bd3bde Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Sat, 23 Apr 2016 16:15:43 +0300 Subject: [PATCH 5/5] Add copying of source files in JPS. Add precise method location recognition for JPS diagnostics --- .idea/libraries/teavm_all.xml | 5 +- .../java/org/teavm/idea/jps/TeaVMBuild.java | 59 ++++++++++++++++--- .../org/teavm/idea/TeaVMJPSRemoteService.java | 20 ++++++- 3 files changed, 71 insertions(+), 13 deletions(-) diff --git a/.idea/libraries/teavm_all.xml b/.idea/libraries/teavm_all.xml index 1a7362281..55f1409ea 100644 --- a/.idea/libraries/teavm_all.xml +++ b/.idea/libraries/teavm_all.xml @@ -4,6 +4,9 @@ - + + + + \ No newline at end of file diff --git a/tools/idea/jps-plugin/src/main/java/org/teavm/idea/jps/TeaVMBuild.java b/tools/idea/jps-plugin/src/main/java/org/teavm/idea/jps/TeaVMBuild.java index aa183f62a..1ba36bb43 100644 --- a/tools/idea/jps-plugin/src/main/java/org/teavm/idea/jps/TeaVMBuild.java +++ b/tools/idea/jps-plugin/src/main/java/org/teavm/idea/jps/TeaVMBuild.java @@ -33,6 +33,7 @@ import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; +import java.util.stream.Collectors; import java.util.stream.Stream; import org.jetbrains.jps.incremental.CompileContext; import org.jetbrains.jps.incremental.ModuleBuildTarget; @@ -42,6 +43,7 @@ import org.jetbrains.jps.incremental.messages.ProgressMessage; import org.jetbrains.jps.model.JpsProject; import org.jetbrains.jps.model.java.JpsJavaExtensionService; import org.jetbrains.jps.model.library.JpsLibrary; +import org.jetbrains.jps.model.library.JpsLibraryRoot; import org.jetbrains.jps.model.library.JpsOrderRootType; import org.jetbrains.jps.model.module.JpsDependencyElement; import org.jetbrains.jps.model.module.JpsLibraryDependency; @@ -62,19 +64,23 @@ import org.teavm.model.ValueType; import org.teavm.tooling.EmptyTeaVMToolLog; import org.teavm.tooling.TeaVMTool; import org.teavm.tooling.TeaVMToolException; +import org.teavm.tooling.sources.DirectorySourceFileProvider; +import org.teavm.tooling.sources.JarSourceFileProvider; +import org.teavm.tooling.sources.SourceFileProvider; import org.teavm.vm.TeaVMPhase; import org.teavm.vm.TeaVMProgressFeedback; import org.teavm.vm.TeaVMProgressListener; public class TeaVMBuild { - private CompileContext context; - private TeaVMStorageProvider storageProvider = new TeaVMStorageProvider(); - private List classPathEntries = new ArrayList<>(); + private final CompileContext context; + private final TeaVMStorageProvider storageProvider = new TeaVMStorageProvider(); + private final List classPathEntries = new ArrayList<>(); private List directoryClassPathEntries; private TeaVMStorage storage; - private TeaVMBuilderAssistant assistant; - private Map sourceFileCache = new HashMap<>(); - private Map fileLineCache = new HashMap<>(); + private final TeaVMBuilderAssistant assistant; + private final Map sourceFileCache = new HashMap<>(); + private final Map fileLineCache = new HashMap<>(); + private final List sourceFileProviders = new ArrayList<>(); public TeaVMBuild(CompileContext context, TeaVMBuilderAssistant assistant) { this.context = context; @@ -102,10 +108,14 @@ public class TeaVMBuild { tool.setProgressListener(createProgressListener(context)); tool.setLog(new EmptyTeaVMToolLog()); tool.setMainClass(config.getMainClass()); - tool.setSourceMapsFileGenerated(true); + tool.setSourceMapsFileGenerated(config.isSourceMapsFileGenerated()); tool.setTargetDirectory(new File(config.getTargetDirectory())); tool.setClassLoader(buildClassLoader()); - tool.setMinifying(false); + tool.setSourceFilesCopied(config.isSourceFilesCopied()); + tool.setMinifying(config.isMinifying()); + for (SourceFileProvider fileProvider : sourceFileProviders) { + tool.addSourceFileProvider(fileProvider); + } boolean errorOccurred = false; try { @@ -366,9 +376,15 @@ public class TeaVMBuild { if (output != null) { classPathEntries.add(output.getPath()); } + + sourceFileProviders.addAll(module.getSourceRoots().stream() + .map(sourceRoot -> new DirectorySourceFileProvider(sourceRoot.getFile())) + .collect(Collectors.toList())); + for (JpsDependencyElement dependency : module.getDependenciesList().getDependencies()) { if (dependency instanceof JpsModuleDependency) { - buildClassPath(((JpsModuleDependency) dependency).getModule(), visited); + JpsModuleDependency moduleDependency = (JpsModuleDependency) dependency; + buildClassPath(moduleDependency.getModule(), visited); } else if (dependency instanceof JpsLibraryDependency) { JpsLibrary library = ((JpsLibraryDependency) dependency).getLibrary(); if (library == null) { @@ -376,7 +392,32 @@ public class TeaVMBuild { } classPathEntries.addAll(library.getFiles(JpsOrderRootType.COMPILED).stream().map(File::getPath) .collect(toList())); + + for (JpsLibraryRoot libraryRoot : library.getRoots(JpsOrderRootType.SOURCES)) { + File file = getFileFromUrl(libraryRoot.getUrl()); + if (file != null) { + if (file.isDirectory()) { + sourceFileProviders.add(new DirectorySourceFileProvider(file)); + } else { + sourceFileProviders.add(new JarSourceFileProvider(file)); + } + } + } } } } + + private File getFileFromUrl(String url) { + if (url.startsWith("file://")) { + return new File(url.substring("file://".length())); + } else if (url.startsWith("jar://")) { + int index = url.indexOf('!'); + return new File(url.substring("file://".length(), index)); + } else if (url.startsWith("jar:file://")) { + int index = url.indexOf('!'); + return new File(url.substring("jar:file://".length(), index)); + } else { + return null; + } + } } diff --git a/tools/idea/src/main/java/org/teavm/idea/TeaVMJPSRemoteService.java b/tools/idea/src/main/java/org/teavm/idea/TeaVMJPSRemoteService.java index 38e0d72ff..c6cf902ed 100644 --- a/tools/idea/src/main/java/org/teavm/idea/TeaVMJPSRemoteService.java +++ b/tools/idea/src/main/java/org/teavm/idea/TeaVMJPSRemoteService.java @@ -22,7 +22,10 @@ import com.intellij.openapi.project.ProjectManager; import com.intellij.psi.JavaPsiFacade; import com.intellij.psi.PsiClass; import com.intellij.psi.PsiMethod; +import com.intellij.psi.PsiParameter; +import com.intellij.psi.PsiType; import com.intellij.psi.search.GlobalSearchScope; +import com.siyeh.ig.fixes.MemberSignature; import java.rmi.AlreadyBoundException; import java.rmi.NotBoundException; import java.rmi.RemoteException; @@ -37,7 +40,7 @@ import org.teavm.idea.jps.remote.TeaVMElementLocation; public class TeaVMJPSRemoteService extends UnicastRemoteObject implements ApplicationComponent, TeaVMBuilderAssistant { private static final int MIN_PORT = 10000; private static final int MAX_PORT = 1 << 16; - private ProjectManager projectManager = ProjectManager.getInstance(); + private final ProjectManager projectManager = ProjectManager.getInstance(); private int port; private Registry registry; @@ -99,10 +102,9 @@ public class TeaVMJPSRemoteService extends UnicastRemoteObject implements Applic } for (PsiMethod method : cls.getAllMethods()) { - if (!method.getName().equals(methodName)) { + if (!method.getName().equals(methodName) || !getMethodSignature(method).equals(methodDesc)) { continue; } - // TODO: check method raw signature resultHolder[0] = getMethodLocation(method); return; } @@ -112,6 +114,18 @@ public class TeaVMJPSRemoteService extends UnicastRemoteObject implements Applic return resultHolder[0]; } + private String getMethodSignature(PsiMethod method) { + StringBuilder sb = new StringBuilder("("); + for (PsiParameter parameter : method.getParameterList().getParameters()) { + sb.append(MemberSignature.createTypeSignature(parameter.getType())); + } + sb.append(")"); + + PsiType returnType = method.getReturnType(); + sb.append(MemberSignature.createTypeSignature(returnType != null ? returnType : PsiType.VOID)); + return sb.toString(); + } + private TeaVMElementLocation getMethodLocation(PsiMethod method) { return new TeaVMElementLocation(method.getTextOffset(), method.getTextOffset() + method.getTextLength(), -1, -1, method.getContainingFile().getVirtualFile().getPath());