IDEA-130959 (method and system for interrupting binary decompilers)
authorRoman Shevchenko <roman.shevchenko@jetbrains.com>
Mon, 10 Nov 2014 20:59:02 +0000 (21:59 +0100)
committerRoman Shevchenko <roman.shevchenko@jetbrains.com>
Mon, 10 Nov 2014 20:59:02 +0000 (21:59 +0100)
platform/core-impl/src/com/intellij/openapi/application/ex/ApplicationUtil.java
platform/core-impl/src/com/intellij/openapi/fileEditor/impl/LoadTextUtil.java
plugins/java-decompiler/engine/src/org/jetbrains/java/decompiler/main/rels/ClassWrapper.java
plugins/java-decompiler/plugin/src/org/jetbrains/java/decompiler/IdeaDecompiler.java
plugins/java-decompiler/plugin/src/org/jetbrains/java/decompiler/IdeaLogger.java

index 1d7de90e5f619298832d8852d4b77da9bd68247c..0022be7759ad53528f77993514268ae88374b6c9 100644 (file)
 package com.intellij.openapi.application.ex;
 
 import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.progress.ProcessCanceledException;
+import com.intellij.openapi.progress.ProgressManager;
 import com.intellij.openapi.util.Computable;
 import com.intellij.openapi.util.Ref;
 import org.jetbrains.annotations.NotNull;
 
+import java.util.concurrent.*;
+
 public class ApplicationUtil {
   // throws exception if can't grab read action right now
   public static <T> T tryRunReadAction(@NotNull final Computable<T> computable) throws CannotRunReadActionException {
@@ -41,7 +45,31 @@ public class ApplicationUtil {
     }
   }
 
-  public static class CannotRunReadActionException extends RuntimeException{
+  /**
+   * Allows to interrupt a process which does not performs checkCancelled() calls by itself.
+   * Note that the process may continue to run in background indefinitely - so <b>avoid using this method unless absolutely needed</b>.
+   */
+  public static <T> T runWithCheckCanceled(@NotNull Callable<T> callable) throws ExecutionException, InterruptedException {
+    Future<T> future = ApplicationManager.getApplication().executeOnPooledThread(callable);
+
+    while (true) {
+      try {
+        ProgressManager.checkCanceled();
+      }
+      catch (ProcessCanceledException e) {
+        future.cancel(true);
+        throw e;
+      }
+
+      try {
+        return future.get(200, TimeUnit.MILLISECONDS);
+      }
+      catch (TimeoutException ignored) { }
+    }
+  }
+
+  public static class CannotRunReadActionException extends RuntimeException {
+    @SuppressWarnings({"NullableProblems", "NonSynchronizedMethodOverridesSynchronizedMethod"})
     @Override
     public Throwable fillInStackTrace() {
       return this;
index 5e7e3739471c1fa702511311f4769f6c8c44d8ce..edeb9423d190ede7eec99cb863f895bc44b24134 100644 (file)
 package com.intellij.openapi.fileEditor.impl;
 
 import com.intellij.lang.properties.charset.Native2AsciiCharset;
-import com.intellij.openapi.fileTypes.*;
+import com.intellij.openapi.application.Application;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.application.ex.ApplicationUtil;
+import com.intellij.openapi.fileTypes.BinaryFileDecompiler;
+import com.intellij.openapi.fileTypes.BinaryFileTypeDecompilers;
+import com.intellij.openapi.fileTypes.CharsetUtil;
+import com.intellij.openapi.fileTypes.FileType;
+import com.intellij.openapi.progress.ProgressIndicator;
+import com.intellij.openapi.progress.ProgressManager;
+import com.intellij.openapi.progress.Task;
 import com.intellij.openapi.project.Project;
 import com.intellij.openapi.util.Key;
 import com.intellij.openapi.util.Pair;
+import com.intellij.openapi.util.Ref;
 import com.intellij.openapi.util.Trinity;
 import com.intellij.openapi.util.text.StringUtil;
 import com.intellij.openapi.vfs.CharsetToolkit;
@@ -38,6 +48,7 @@ import java.nio.ByteBuffer;
 import java.nio.CharBuffer;
 import java.nio.charset.Charset;
 import java.nio.charset.UnsupportedCharsetException;
+import java.util.concurrent.Callable;
 
 public final class LoadTextUtil {
   @Nls private static final String AUTO_DETECTED_FROM_BOM = "auto-detected from BOM";
@@ -348,7 +359,7 @@ public final class LoadTextUtil {
   }
 
   @NotNull
-  public static CharSequence loadText(@NotNull VirtualFile file) {
+  public static CharSequence loadText(@NotNull final VirtualFile file) {
     if (file instanceof LightVirtualFile) {
       return ((LightVirtualFile)file).getContent();
     }
@@ -361,7 +372,42 @@ public final class LoadTextUtil {
     if (fileType.isBinary()) {
       final BinaryFileDecompiler decompiler = BinaryFileTypeDecompilers.INSTANCE.forFileType(fileType);
       if (decompiler != null) {
-        CharSequence text = decompiler.decompile(file);
+        CharSequence text;
+
+        final Application app = ApplicationManager.getApplication();
+        if (app != null && app.isDispatchThread()) {
+          final Ref<CharSequence> result = Ref.create(ArrayUtil.EMPTY_CHAR_SEQUENCE);
+          final Ref<RuntimeException> exception = Ref.create();
+
+          ProgressManager.getInstance().run(new Task.Modal(null, "Decompiling " + file.getName(), true) {
+            @Override
+            public void run(@NotNull ProgressIndicator indicator) {
+              indicator.setIndeterminate(true);
+              try {
+                result.set(ApplicationUtil.runWithCheckCanceled(new Callable<CharSequence>() {
+                  @Override
+                  public CharSequence call() {
+                    return decompiler.decompile(file);
+                  }
+                }));
+              }
+              catch (RuntimeException e) {
+                exception.set(e);
+              }
+              catch (Exception ignored) { }
+            }
+          });
+
+          if (!exception.isNull()) {
+            throw exception.get();
+          }
+
+          text = result.get();
+        }
+        else {
+          text = decompiler.decompile(file);
+        }
+
         StringUtil.assertValidSeparators(text);
         return text;
       }
index 8f2958f83bb57f3bb84b15797e731204061c25c3..fcc1a5dd022561e0bab69dcce2266d3cc4a2effc 100644 (file)
@@ -95,8 +95,14 @@ public class ClassWrapper {
             mtThread.start();
 
             while (!mtProc.isFinished()) {
-              synchronized (mtProc.lock) {
-                mtProc.lock.wait(100);
+              try {
+                synchronized (mtProc.lock) {
+                  mtProc.lock.wait(200);
+                }
+              }
+              catch (InterruptedException e) {
+                killThread(mtThread);
+                throw e;
               }
 
               if (System.currentTimeMillis() >= stopAt) {
index 5cf87ed095ade84a65dfd2e7c0fed85f720936e2..130acb7ec7da26f666c9422f1ab890fe67e9b4f3 100644 (file)
@@ -26,6 +26,7 @@ import com.intellij.openapi.application.ModalityState;
 import com.intellij.openapi.application.ex.ApplicationManagerEx;
 import com.intellij.openapi.fileEditor.*;
 import com.intellij.openapi.fileTypes.StdFileTypes;
+import com.intellij.openapi.progress.ProcessCanceledException;
 import com.intellij.openapi.project.DefaultProjectFactory;
 import com.intellij.openapi.project.Project;
 import com.intellij.openapi.ui.DialogWrapper;
@@ -175,6 +176,9 @@ public class IdeaDecompiler extends ClassFileDecompilers.Light {
 
       return saver.myResult;
     }
+    catch (ProcessCanceledException e) {
+      throw e;
+    }
     catch (Exception e) {
       if (ApplicationManager.getApplication().isUnitTestMode()) {
         AssertionError error = new AssertionError(file.getUrl());
index d65e95a5b5970b082bd31fdc82b1b7028c2b8013..c9adb6c7417da0ec4ba7c035210b8a58eb3fa0ba 100644 (file)
@@ -16,6 +16,7 @@
 package org.jetbrains.java.decompiler;
 
 import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.progress.ProcessCanceledException;
 import org.jetbrains.java.decompiler.main.extern.IFernflowerLogger;
 
 public class IdeaLogger extends IFernflowerLogger {
@@ -41,6 +42,8 @@ public class IdeaLogger extends IFernflowerLogger {
   @Override
   public void writeMessage(String message, Throwable t) {
     if (t instanceof InternalException) throw (InternalException)t;
+    else if (t instanceof ProcessCanceledException) throw (ProcessCanceledException)t;
+    else if (t instanceof InterruptedException) throw new ProcessCanceledException(t);
     else throw new InternalException(message, t);
   }